diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
commit | e6918187568dbd01842d8d1d2c808ce16a894239 (patch) | |
tree | 64f88b554b444a49f656b6c656111a145cbbaa28 /src/fmt | |
parent | Initial commit. (diff) | |
download | ceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip |
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/fmt')
210 files changed, 87811 insertions, 0 deletions
diff --git a/src/fmt/.clang-format b/src/fmt/.clang-format new file mode 100644 index 000000000..df55d340f --- /dev/null +++ b/src/fmt/.clang-format @@ -0,0 +1,8 @@ +# Run manually to reformat a file: +# clang-format -i --style=file <file> +Language: Cpp +BasedOnStyle: Google +IndentPPDirectives: AfterHash +IndentCaseLabels: false +AlwaysBreakTemplateDeclarations: false +DerivePointerAlignment: false diff --git a/src/fmt/.github/issue_template.md b/src/fmt/.github/issue_template.md new file mode 100644 index 000000000..8e39c5ba7 --- /dev/null +++ b/src/fmt/.github/issue_template.md @@ -0,0 +1,6 @@ +<!-- +Please make sure that the problem reproduces on the current master before +submitting an issue. +If possible please provide a repro on Compiler Explorer: +https://godbolt.org/z/fxccbh53W. +--> diff --git a/src/fmt/.github/pull_request_template.md b/src/fmt/.github/pull_request_template.md new file mode 100644 index 000000000..b70c27bfb --- /dev/null +++ b/src/fmt/.github/pull_request_template.md @@ -0,0 +1,6 @@ +<!-- +Please read the contribution guidelines before submitting a pull request: +https://github.com/fmtlib/fmt/blob/master/CONTRIBUTING.md. +By submitting this pull request, you agree that your contributions are licensed +under the {fmt} license, and agree to future changes to the licensing. +--> diff --git a/src/fmt/.github/workflows/doc.yml b/src/fmt/.github/workflows/doc.yml new file mode 100644 index 000000000..47c097746 --- /dev/null +++ b/src/fmt/.github/workflows/doc.yml @@ -0,0 +1,27 @@ +name: doc + +on: [push, pull_request] + +permissions: + contents: read + +jobs: + build: + # Use Ubuntu 20.04 because doxygen 1.8.13 from Ubuntu 18.04 is broken. + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2 + + - name: Create Build Environment + run: | + sudo apt update + sudo apt install doxygen python3-virtualenv + sudo npm install -g less clean-css + cmake -E make_directory ${{runner.workspace}}/build + + - name: Build + working-directory: ${{runner.workspace}}/build + env: + KEY: ${{secrets.KEY}} + run: $GITHUB_WORKSPACE/support/build-docs.py diff --git a/src/fmt/.github/workflows/linux.yml b/src/fmt/.github/workflows/linux.yml new file mode 100644 index 000000000..70085f5c6 --- /dev/null +++ b/src/fmt/.github/workflows/linux.yml @@ -0,0 +1,94 @@ +name: linux + +on: [push, pull_request] + +permissions: + contents: read + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + cxx: [g++-4.8, g++-10, clang++-9] + build_type: [Debug, Release] + std: [11] + os: [ubuntu-18.04] + include: + - cxx: g++-4.8 + install: sudo apt install g++-4.8 + os: ubuntu-18.04 + - cxx: g++-8 + build_type: Debug + std: 14 + install: sudo apt install g++-8 + os: ubuntu-18.04 + - cxx: g++-8 + build_type: Debug + std: 17 + install: sudo apt install g++-8 + os: ubuntu-18.04 + - cxx: g++-10 + build_type: Debug + std: 17 + os: ubuntu-18.04 + - cxx: g++-11 + build_type: Debug + std: 20 + os: ubuntu-20.04 + install: sudo apt install g++-11 + - cxx: clang++-8 + build_type: Debug + std: 17 + cxxflags: -stdlib=libc++ + os: ubuntu-18.04 + install: sudo apt install clang-8 libc++-8-dev libc++abi-8-dev + - cxx: clang++-9 + build_type: Debug + fuzz: -DFMT_FUZZ=ON -DFMT_FUZZ_LINKMAIN=ON + std: 17 + os: ubuntu-18.04 + - cxx: clang++-11 + build_type: Debug + std: 20 + os: ubuntu-20.04 + - cxx: clang++-11 + build_type: Debug + std: 20 + cxxflags: -stdlib=libc++ + os: ubuntu-20.04 + install: sudo apt install libc++-11-dev libc++abi-11-dev + - shared: -DBUILD_SHARED_LIBS=ON + + steps: + - uses: actions/checkout@v2 + + - name: Create Build Environment + run: | + ${{matrix.install}} + sudo apt update + sudo apt install locales-all + cmake -E make_directory ${{runner.workspace}}/build + + - name: Configure + working-directory: ${{runner.workspace}}/build + env: + CXX: ${{matrix.cxx}} + CXXFLAGS: ${{matrix.cxxflags}} + run: | + cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ${{matrix.fuzz}} ${{matrix.shared}} \ + -DCMAKE_CXX_STANDARD=${{matrix.std}} -DFMT_DOC=OFF \ + -DCMAKE_CXX_VISIBILITY_PRESET=hidden -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON \ + -DFMT_PEDANTIC=ON -DFMT_WERROR=ON $GITHUB_WORKSPACE + + - name: Build + working-directory: ${{runner.workspace}}/build + run: | + threads=`nproc` + cmake --build . --config ${{matrix.build_type}} --parallel $threads + + - name: Test + working-directory: ${{runner.workspace}}/build + run: ctest -C ${{matrix.build_type}} + env: + CTEST_OUTPUT_ON_FAILURE: True diff --git a/src/fmt/.github/workflows/macos.yml b/src/fmt/.github/workflows/macos.yml new file mode 100644 index 000000000..24dd59588 --- /dev/null +++ b/src/fmt/.github/workflows/macos.yml @@ -0,0 +1,40 @@ +name: macos + +on: [push, pull_request] + +permissions: + contents: read + +jobs: + build: + runs-on: macos-10.15 + strategy: + matrix: + build_type: [Debug, Release] + include: + - shared: -DBUILD_SHARED_LIBS=ON + + steps: + - uses: actions/checkout@v2 + + - name: Create Build Environment + run: cmake -E make_directory ${{runner.workspace}}/build + + - name: Configure + working-directory: ${{runner.workspace}}/build + run: | + cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ${{matrix.shared}} \ + -DCMAKE_CXX_VISIBILITY_PRESET=hidden -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON \ + -DFMT_DOC=OFF -DFMT_PEDANTIC=ON -DFMT_WERROR=ON $GITHUB_WORKSPACE + + - name: Build + working-directory: ${{runner.workspace}}/build + run: | + threads=`sysctl -n hw.logicalcpu` + cmake --build . --config ${{matrix.build_type}} --parallel $threads + + - name: Test + working-directory: ${{runner.workspace}}/build + run: ctest -C ${{matrix.build_type}} + env: + CTEST_OUTPUT_ON_FAILURE: True diff --git a/src/fmt/.github/workflows/windows.yml b/src/fmt/.github/workflows/windows.yml new file mode 100644 index 000000000..6e79f60c2 --- /dev/null +++ b/src/fmt/.github/workflows/windows.yml @@ -0,0 +1,92 @@ +name: windows + +on: [push, pull_request] + +permissions: + contents: read + +jobs: + build: + runs-on: ${{matrix.os}} + strategy: + matrix: + # windows-2019 has MSVC 2019 installed; + # windows-2022 has MSVC 2022 installed: + # https://github.com/actions/virtual-environments. + os: [windows-2019] + platform: [Win32, x64] + toolset: [v140, v141, v142] + standard: [14, 17, 20] + shared: ["", -DBUILD_SHARED_LIBS=ON] + build_type: [Debug, Release] + exclude: + - { toolset: v140, standard: 17 } + - { toolset: v140, standard: 20 } + - { toolset: v141, standard: 14 } + - { toolset: v141, standard: 20 } + - { toolset: v142, standard: 14 } + - { platform: Win32, toolset: v140 } + - { platform: Win32, toolset: v141 } + - { platform: Win32, standard: 14 } + - { platform: Win32, standard: 20 } + - { platform: x64, toolset: v140, shared: -DBUILD_SHARED_LIBS=ON } + - { platform: x64, toolset: v141, shared: -DBUILD_SHARED_LIBS=ON } + - { platform: x64, standard: 14, shared: -DBUILD_SHARED_LIBS=ON } + - { platform: x64, standard: 20, shared: -DBUILD_SHARED_LIBS=ON } + include: + - os: windows-2022 + platform: x64 + toolset: v143 + build_type: Debug + standard: 20 + + steps: + - uses: actions/checkout@v2 + + - name: Create Build Environment + run: cmake -E make_directory ${{runner.workspace}}/build + + - name: Configure + # Use a bash shell for $GITHUB_WORKSPACE. + shell: bash + working-directory: ${{runner.workspace}}/build + run: | + cmake -A ${{matrix.platform}} -T ${{matrix.toolset}} \ + -DCMAKE_CXX_STANDARD=${{matrix.standard}} \ + ${{matrix.shared}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ + $GITHUB_WORKSPACE + + - name: Build + working-directory: ${{runner.workspace}}/build + run: | + $threads = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors + cmake --build . --config ${{matrix.build_type}} --parallel $threads + + - name: Test + working-directory: ${{runner.workspace}}/build + run: ctest -C ${{matrix.build_type}} -V + env: + CTEST_OUTPUT_ON_FAILURE: True + + mingw: + runs-on: windows-latest + defaults: + run: + shell: msys2 {0} + strategy: + matrix: + sys: [ mingw64, mingw32, ucrt64 ] + steps: + - uses: msys2/setup-msys2@v2 + with: + release: false + msystem: ${{matrix.sys}} + pacboy: cc:p cmake:p ninja:p lld:p + - uses: actions/checkout@v2 + - name: Configure + run: cmake -B ../build -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug + env: { LDFLAGS: -fuse-ld=lld } + - name: Build + run: cmake --build ../build + - name: Test + run: ctest -j `nproc` --test-dir ../build diff --git a/src/fmt/.gitignore b/src/fmt/.gitignore new file mode 100644 index 000000000..8a37cb987 --- /dev/null +++ b/src/fmt/.gitignore @@ -0,0 +1,37 @@ +.vscode/ +.vs/ + +*.iml +.idea/ +.externalNativeBuild/ +.gradle/ +gradle/ +gradlew* +local.properties +build/ +support/.cxx + +bin/ +/_CPack_Packages +/CMakeScripts +/doc/doxyxml +/doc/html +/doc/node_modules +virtualenv +/Testing +/install_manifest.txt +*~ +*.a +*.so* +*.xcodeproj +*.zip +cmake_install.cmake +CPack*.cmake +fmt-*.cmake +CTestTestfile.cmake +CMakeCache.txt +CMakeFiles +FMT.build +Makefile +run-msbuild.bat +fmt.pc diff --git a/src/fmt/CMakeLists.txt b/src/fmt/CMakeLists.txt new file mode 100644 index 000000000..088a2a9c5 --- /dev/null +++ b/src/fmt/CMakeLists.txt @@ -0,0 +1,387 @@ +cmake_minimum_required(VERSION 3.1...3.18) + +# Fallback for using newer policies on CMake <3.12. +if(${CMAKE_VERSION} VERSION_LESS 3.12) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +endif() + +# Determine if fmt is built as a subproject (using add_subdirectory) +# or if it is the master project. +if (NOT DEFINED FMT_MASTER_PROJECT) + set(FMT_MASTER_PROJECT OFF) + if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(FMT_MASTER_PROJECT ON) + message(STATUS "CMake version: ${CMAKE_VERSION}") + endif () +endif () + +# Joins arguments and places the results in ${result_var}. +function(join result_var) + set(result "") + foreach (arg ${ARGN}) + set(result "${result}${arg}") + endforeach () + set(${result_var} "${result}" PARENT_SCOPE) +endfunction() + +function(enable_module target) + if (MSVC) + set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc) + target_compile_options(${target} + PRIVATE /interface /ifcOutput ${BMI} + INTERFACE /reference fmt=${BMI}) + endif () + set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI}) + set_source_files_properties(${BMI} PROPERTIES GENERATED ON) +endfunction() + +include(CMakeParseArguments) + +# Sets a cache variable with a docstring joined from multiple arguments: +# set(<variable> <value>... CACHE <type> <docstring>...) +# This allows splitting a long docstring for readability. +function(set_verbose) + # cmake_parse_arguments is broken in CMake 3.4 (cannot parse CACHE) so use + # list instead. + list(GET ARGN 0 var) + list(REMOVE_AT ARGN 0) + list(GET ARGN 0 val) + list(REMOVE_AT ARGN 0) + list(REMOVE_AT ARGN 0) + list(GET ARGN 0 type) + list(REMOVE_AT ARGN 0) + join(doc ${ARGN}) + set(${var} ${val} CACHE ${type} ${doc}) +endfunction() + +# Set the default CMAKE_BUILD_TYPE to Release. +# This should be done before the project command since the latter can set +# CMAKE_BUILD_TYPE itself (it does so for nmake). +if (FMT_MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE) + set_verbose(CMAKE_BUILD_TYPE Release CACHE STRING + "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or " + "CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") +endif () + +project(FMT CXX) +include(GNUInstallDirs) +set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING + "Installation directory for include files, a relative path that " + "will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute path.") + +option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF) +option(FMT_WERROR "Halt the compilation with an error on compiler warnings." + OFF) + +# Options that control generation of various targets. +option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT}) +option(FMT_INSTALL "Generate the install target." ${FMT_MASTER_PROJECT}) +option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT}) +option(FMT_FUZZ "Generate the fuzz target." OFF) +option(FMT_CUDA_TEST "Generate the cuda-test target." OFF) +option(FMT_OS "Include core requiring OS (Windows/Posix) " ON) +option(FMT_MODULE "Build a module instead of a traditional library." OFF) +option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF) + +set(FMT_CAN_MODULE OFF) +if (CMAKE_CXX_STANDARD GREATER 17 AND + # msvc 16.10-pre4 + MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.29.30035) + set(FMT_CAN_MODULE OFF) +endif () +if (NOT FMT_CAN_MODULE) + set(FMT_MODULE OFF) + message(STATUS "Module support is disabled.") +endif () +if (FMT_TEST AND FMT_MODULE) + # The tests require {fmt} to be compiled as traditional library + message(STATUS "Testing is incompatible with build mode 'module'.") +endif () +set(FMT_SYSTEM_HEADERS_ATTRIBUTE "") +if (FMT_SYSTEM_HEADERS) + set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM) +endif () + +# Get version from core.h +file(READ include/fmt/core.h core_h) +if (NOT core_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])") + message(FATAL_ERROR "Cannot get FMT_VERSION from core.h.") +endif () +# Use math to skip leading zeros if any. +math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1}) +math(EXPR CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2}) +math(EXPR CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3}) +join(FMT_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}. + ${CPACK_PACKAGE_VERSION_PATCH}) +message(STATUS "Version: ${FMT_VERSION}") + +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") + +if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +endif () + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/support/cmake") + +include(cxx14) +include(JoinPaths) + +list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index) +if (${index} GREATER -1) + # Use cxx_variadic_templates instead of more appropriate cxx_std_11 for + # compatibility with older CMake versions. + set(FMT_REQUIRED_FEATURES cxx_variadic_templates) +endif () +message(STATUS "Required features: ${FMT_REQUIRED_FEATURES}") + +if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET) + set_verbose(CMAKE_CXX_VISIBILITY_PRESET hidden CACHE STRING + "Preset for the export of private symbols") + set_property(CACHE CMAKE_CXX_VISIBILITY_PRESET PROPERTY STRINGS + hidden default) +endif () + +if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_VISIBILITY_INLINES_HIDDEN) + set_verbose(CMAKE_VISIBILITY_INLINES_HIDDEN ON CACHE BOOL + "Whether to add a compile flag to hide symbols of inline functions") +endif () + +if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic + -Wold-style-cast -Wundef + -Wredundant-decls -Wwrite-strings -Wpointer-arith + -Wcast-qual -Wformat=2 -Wmissing-include-dirs + -Wcast-align + -Wctor-dtor-privacy -Wdisabled-optimization + -Winvalid-pch -Woverloaded-virtual + -Wconversion -Wundef + -Wno-ctor-dtor-privacy -Wno-format-nonliteral) + if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6) + set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} + -Wno-dangling-else -Wno-unused-local-typedefs) + endif () + if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) + set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion + -Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast + -Wvector-operation-performance -Wsized-deallocation -Wshadow) + endif () + if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) + set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2 + -Wnull-dereference -Wduplicated-cond) + endif () + set(WERROR_FLAG -Werror) +endif () + +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wundef + -Wdeprecated -Wweak-vtables -Wshadow + -Wno-gnu-zero-variadic-macro-arguments) + check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING) + if (HAS_NULLPTR_WARNING) + set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} + -Wzero-as-null-pointer-constant) + endif () + set(WERROR_FLAG -Werror) +endif () + +if (MSVC) + set(PEDANTIC_COMPILE_FLAGS /W3) + set(WERROR_FLAG /WX) +endif () + +if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio") + # If Microsoft SDK is installed create script run-msbuild.bat that + # calls SetEnv.cmd to set up build environment and runs msbuild. + # It is useful when building Visual Studio projects with the SDK + # toolchain rather than Visual Studio. + include(FindSetEnv) + if (WINSDK_SETENV) + set(MSBUILD_SETUP "call \"${WINSDK_SETENV}\"") + endif () + # Set FrameworkPathOverride to get rid of MSB3644 warnings. + join(netfxpath + "C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\" + ".NETFramework\\v4.0") + file(WRITE run-msbuild.bat " + ${MSBUILD_SETUP} + ${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*") +endif () + +function(add_headers VAR) + set(headers ${${VAR}}) + foreach (header ${ARGN}) + set(headers ${headers} include/fmt/${header}) + endforeach() + set(${VAR} ${headers} PARENT_SCOPE) +endfunction() + +# Define the fmt library, its includes and the needed defines. +add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h + format-inl.h os.h ostream.h printf.h ranges.h std.h + xchar.h) +if (FMT_MODULE) + set(FMT_SOURCES src/fmt.cc) +elseif (FMT_OS) + set(FMT_SOURCES src/format.cc src/os.cc) +else() + set(FMT_SOURCES src/format.cc) +endif () + +add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst) +add_library(fmt::fmt ALIAS fmt) + +if (FMT_WERROR) + target_compile_options(fmt PRIVATE ${WERROR_FLAG}) +endif () +if (FMT_PEDANTIC) + target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS}) +endif () +if (FMT_MODULE) + enable_module(fmt) +endif () + +target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES}) + +target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC + $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> + $<INSTALL_INTERFACE:${FMT_INC_DIR}>) + +set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.") + +set_target_properties(fmt PROPERTIES + VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR} + PUBLIC_HEADER "${FMT_HEADERS}" + DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}") + +# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target +# property because it's not set by default. +set(FMT_LIB_NAME fmt) +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(FMT_LIB_NAME ${FMT_LIB_NAME}${FMT_DEBUG_POSTFIX}) +endif () + +if (BUILD_SHARED_LIBS) + target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED) +endif () +if (FMT_SAFE_DURATION_CAST) + target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST) +endif() + +add_library(fmt-header-only INTERFACE) +add_library(fmt::fmt-header-only ALIAS fmt-header-only) + +target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1) +target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES}) + +target_include_directories(fmt-header-only ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE + $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> + $<INSTALL_INTERFACE:${FMT_INC_DIR}>) + +# Install targets. +if (FMT_INSTALL) + include(CMakePackageConfigHelpers) + set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING + "Installation directory for cmake files, a relative path that " + "will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute " + "path.") + set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake) + set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake) + set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc) + set(targets_export_name fmt-targets) + + set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING + "Installation directory for libraries, a relative path that " + "will be joined to ${CMAKE_INSTALL_PREFIX} or an absolute path.") + + set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH + "Installation directory for pkgconfig (.pc) files, a relative " + "path that will be joined with ${CMAKE_INSTALL_PREFIX} or an " + "absolute path.") + + # Generate the version, config and target files into the build directory. + write_basic_package_version_file( + ${version_config} + VERSION ${FMT_VERSION} + COMPATIBILITY AnyNewerVersion) + + join_paths(libdir_for_pc_file "\${exec_prefix}" "${FMT_LIB_DIR}") + join_paths(includedir_for_pc_file "\${prefix}" "${FMT_INC_DIR}") + + configure_file( + "${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in" + "${pkgconfig}" + @ONLY) + configure_package_config_file( + ${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in + ${project_config} + INSTALL_DESTINATION ${FMT_CMAKE_DIR}) + + set(INSTALL_TARGETS fmt fmt-header-only) + + # Install the library and headers. + install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} + LIBRARY DESTINATION ${FMT_LIB_DIR} + ARCHIVE DESTINATION ${FMT_LIB_DIR} + PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt" + FRAMEWORK DESTINATION "." + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + + # Use a namespace because CMake provides better diagnostics for namespaced + # imported targets. + export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt:: + FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake) + + # Install version, config and target files. + install( + FILES ${project_config} ${version_config} + DESTINATION ${FMT_CMAKE_DIR}) + install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR} + NAMESPACE fmt::) + + install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}> + DESTINATION ${FMT_LIB_DIR} OPTIONAL) + install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") +endif () + +if (FMT_DOC) + add_subdirectory(doc) +endif () + +if (FMT_TEST) + enable_testing() + add_subdirectory(test) +endif () + +# Control fuzzing independent of the unit tests. +if (FMT_FUZZ) + add_subdirectory(test/fuzzing) + + # The FMT_FUZZ macro is used to prevent resource exhaustion in fuzzing + # mode and make fuzzing practically possible. It is similar to + # FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION but uses a different name to + # avoid interfering with fuzzing of projects that use {fmt}. + # See also https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode. + target_compile_definitions(fmt PUBLIC FMT_FUZZ) +endif () + +set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore) +if (FMT_MASTER_PROJECT AND EXISTS ${gitignore}) + # Get the list of ignored files from .gitignore. + file (STRINGS ${gitignore} lines) + list(REMOVE_ITEM lines /doc/html) + foreach (line ${lines}) + string(REPLACE "." "[.]" line "${line}") + string(REPLACE "*" ".*" line "${line}") + set(ignored_files ${ignored_files} "${line}$" "${line}/") + endforeach () + set(ignored_files ${ignored_files} + /.git /breathe /format-benchmark sphinx/ .buildinfo .doctrees) + + set(CPACK_SOURCE_GENERATOR ZIP) + set(CPACK_SOURCE_IGNORE_FILES ${ignored_files}) + set(CPACK_SOURCE_PACKAGE_FILE_NAME fmt-${FMT_VERSION}) + set(CPACK_PACKAGE_NAME fmt) + set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.rst) + include(CPack) +endif () diff --git a/src/fmt/CONTRIBUTING.md b/src/fmt/CONTRIBUTING.md new file mode 100644 index 000000000..b82f14506 --- /dev/null +++ b/src/fmt/CONTRIBUTING.md @@ -0,0 +1,20 @@ +Contributing to {fmt} +===================== + +By submitting a pull request or a patch, you represent that you have the right +to license your contribution to the {fmt} project owners and the community, +agree that your contributions are licensed under the {fmt} license, and agree +to future changes to the licensing. + +All C++ code must adhere to [Google C++ Style Guide]( +https://google.github.io/styleguide/cppguide.html) with the following +exceptions: + +* Exceptions are permitted +* snake_case should be used instead of UpperCamelCase for function and type + names + +All documentation must adhere to the [Google Developer Documentation Style +Guide](https://developers.google.com/style). + +Thanks for contributing! diff --git a/src/fmt/ChangeLog.rst b/src/fmt/ChangeLog.rst new file mode 100644 index 000000000..4ebc5c733 --- /dev/null +++ b/src/fmt/ChangeLog.rst @@ -0,0 +1,5255 @@ +9.1.0 - 2022-08-27 +------------------ + +* ``fmt::formatted_size`` now works at compile time + (`#3026 <https://github.com/fmtlib/fmt/pull/3026>`_). For example + (`godbolt <https://godbolt.org/z/1MW5rMdf8>`__): + + .. code:: c++ + + #include <fmt/compile.h> + + int main() { + using namespace fmt::literals; + constexpr size_t n = fmt::formatted_size("{}"_cf, 42); + fmt::print("{}\n", n); // prints 2 + } + + Thanks `@marksantaniello (Mark Santaniello) + <https://github.com/marksantaniello>`_. + +* Fixed handling of invalid UTF-8 + (`#3038 <https://github.com/fmtlib/fmt/pull/3038>`_, + `#3044 <https://github.com/fmtlib/fmt/pull/3044>`_, + `#3056 <https://github.com/fmtlib/fmt/pull/3056>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_ and + `@skeeto (Christopher Wellons) <https://github.com/skeeto>`_. + +* Improved Unicode support in ``ostream`` overloads of ``print`` + (`#2994 <https://github.com/fmtlib/fmt/pull/2994>`_, + `#3001 <https://github.com/fmtlib/fmt/pull/3001>`_, + `#3025 <https://github.com/fmtlib/fmt/pull/3025>`_). + Thanks `@dimztimz (Dimitrij Mijoski) <https://github.com/dimztimz>`_. + +* Fixed handling of the sign specifier in localized formatting on systems with + 32-bit ``wchar_t`` (`#3041 <https://github.com/fmtlib/fmt/issues/3041>`_). + +* Added support for wide streams to ``fmt::streamed`` + (`#2994 <https://github.com/fmtlib/fmt/pull/2994>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Added the ``n`` specifier that disables the output of delimiters when + formatting ranges (`#2981 <https://github.com/fmtlib/fmt/pull/2981>`_, + `#2983 <https://github.com/fmtlib/fmt/pull/2983>`_). + For example (`godbolt <https://godbolt.org/z/roKqGdj8c>`__): + + .. code:: c++ + + #include <fmt/ranges.h> + #include <vector> + + int main() { + auto v = std::vector{1, 2, 3}; + fmt::print("{:n}\n", v); // prints 1, 2, 3 + } + + Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_. + +* Worked around problematic ``std::string_view`` constructors introduced in + C++23 (`#3030 <https://github.com/fmtlib/fmt/issues/3030>`_, + `#3050 <https://github.com/fmtlib/fmt/issues/3050>`_). + Thanks `@strega-nil-ms (nicole mazzuca) <https://github.com/strega-nil-ms>`_. + +* Improve handling (exclusion) of recursive ranges + (`#2968 <https://github.com/fmtlib/fmt/issues/2968>`_, + `#2974 <https://github.com/fmtlib/fmt/pull/2974>`_). + Thanks `@Dani-Hub (Daniel Krügler) <https://github.com/Dani-Hub>`_. + +* Improved error reporting in format string compilation + (`#3055 <https://github.com/fmtlib/fmt/issues/3055>`_). + +* Improved the implementation of + `Dragonbox <https://github.com/jk-jeon/dragonbox>`_, the algorithm used for + the default floating-point formatting + (`#2984 <https://github.com/fmtlib/fmt/pull/2984>`_). + Thanks `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_. + +* Fixed issues with floating-point formatting on exotic platforms. + +* Improved the implementation of chrono formatting + (`#3010 <https://github.com/fmtlib/fmt/pull/3010>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Improved documentation + (`#2966 <https://github.com/fmtlib/fmt/pull/2966>`_, + `#3009 <https://github.com/fmtlib/fmt/pull/3009>`_, + `#3020 <https://github.com/fmtlib/fmt/issues/3020>`_, + `#3037 <https://github.com/fmtlib/fmt/pull/3037>`_). + Thanks `@mwinterb <https://github.com/mwinterb>`_, + `@jcelerier (Jean-Michaël Celerier) <https://github.com/jcelerier>`_ + and `@remiburtin (Rémi Burtin) <https://github.com/remiburtin>`_. + +* Improved build configuration + (`#2991 <https://github.com/fmtlib/fmt/pull/2991>`_, + `#2995 <https://github.com/fmtlib/fmt/pull/2995>`_, + `#3004 <https://github.com/fmtlib/fmt/issues/3004>`_, + `#3007 <https://github.com/fmtlib/fmt/pull/3007>`_, + `#3040 <https://github.com/fmtlib/fmt/pull/3040>`_). + Thanks `@dimztimz (Dimitrij Mijoski) <https://github.com/dimztimz>`_ and + `@hwhsu1231 (Haowei Hsu) <https://github.com/hwhsu1231>`_. + +* Fixed various warnings and compilation issues + (`#2969 <https://github.com/fmtlib/fmt/issues/2969>`_, + `#2971 <https://github.com/fmtlib/fmt/pull/2971>`_, + `#2975 <https://github.com/fmtlib/fmt/issues/2975>`_, + `#2982 <https://github.com/fmtlib/fmt/pull/2982>`_, + `#2985 <https://github.com/fmtlib/fmt/pull/2985>`_, + `#2988 <https://github.com/fmtlib/fmt/issues/2988>`_, + `#3000 <https://github.com/fmtlib/fmt/issues/3000>`_, + `#3006 <https://github.com/fmtlib/fmt/issues/3006>`_, + `#3014 <https://github.com/fmtlib/fmt/issues/3014>`_, + `#3015 <https://github.com/fmtlib/fmt/issues/3015>`_, + `#3021 <https://github.com/fmtlib/fmt/pull/3021>`_, + `#3023 <https://github.com/fmtlib/fmt/issues/3023>`_, + `#3024 <https://github.com/fmtlib/fmt/pull/3024>`_, + `#3029 <https://github.com/fmtlib/fmt/pull/3029>`_, + `#3043 <https://github.com/fmtlib/fmt/pull/3043>`_, + `#3052 <https://github.com/fmtlib/fmt/issues/3052>`_, + `#3053 <https://github.com/fmtlib/fmt/pull/3053>`_, + `#3054 <https://github.com/fmtlib/fmt/pull/3054>`_). + Thanks `@h-friederich (Hannes Friederich) <https://github.com/h-friederich>`_, + `@dimztimz (Dimitrij Mijoski) <https://github.com/dimztimz>`_, + `@olupton (Olli Lupton) <https://github.com/olupton>`_, + `@bernhardmgruber (Bernhard Manfred Gruber) + <https://github.com/bernhardmgruber>`_, + `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +9.0.0 - 2022-07-04 +------------------ + +* Switched to the internal floating point formatter for all decimal presentation + formats. In particular this results in consistent rounding on all platforms + and removing the ``s[n]printf`` fallback for decimal FP formatting. + +* Compile-time floating point formatting no longer requires the header-only + mode. For example (`godbolt <https://godbolt.org/z/G37PTeG3b>`__): + + .. code:: c++ + + #include <array> + #include <fmt/compile.h> + + consteval auto compile_time_dtoa(double value) -> std::array<char, 10> { + auto result = std::array<char, 10>(); + fmt::format_to(result.data(), FMT_COMPILE("{}"), value); + return result; + } + + constexpr auto answer = compile_time_dtoa(0.42); + + works with the default settings. + +* Improved the implementation of + `Dragonbox <https://github.com/jk-jeon/dragonbox>`_, the algorithm used for + the default floating-point formatting + (`#2713 <https://github.com/fmtlib/fmt/pull/2713>`_, + `#2750 <https://github.com/fmtlib/fmt/pull/2750>`_). + Thanks `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_. + +* Made ``fmt::to_string`` work with ``__float128``. This uses the internal + FP formatter and works even on system without ``__float128`` support in + ``[s]printf``. + +* Disabled automatic ``std::ostream`` insertion operator (``operator<<``) + discovery when ``fmt/ostream.h`` is included to prevent ODR violations. + You can get the old behavior by defining ``FMT_DEPRECATED_OSTREAM`` but this + will be removed in the next major release. Use ``fmt::streamed`` or + ``fmt::ostream_formatter`` to enable formatting via ``std::ostream`` instead. + +* Added ``fmt::ostream_formatter`` that can be used to write ``formatter`` + specializations that perform formatting via ``std::ostream``. + For example (`godbolt <https://godbolt.org/z/5sEc5qMsf>`__): + + .. code:: c++ + + #include <fmt/ostream.h> + + struct date { + int year, month, day; + + friend std::ostream& operator<<(std::ostream& os, const date& d) { + return os << d.year << '-' << d.month << '-' << d.day; + } + }; + + template <> struct fmt::formatter<date> : ostream_formatter {}; + + std::string s = fmt::format("The date is {}", date{2012, 12, 9}); + // s == "The date is 2012-12-9" + +* Added the ``fmt::streamed`` function that takes an object and formats it + via ``std::ostream``. + For example (`godbolt <https://godbolt.org/z/5G3346G1f>`__): + + .. code:: c++ + + #include <thread> + #include <fmt/ostream.h> + + int main() { + fmt::print("Current thread id: {}\n", + fmt::streamed(std::this_thread::get_id())); + } + + Note that ``fmt/std.h`` provides a ``formatter`` specialization for + ``std::thread::id`` so you don't need to format it via ``std::ostream``. + +* Deprecated implicit conversions of unscoped enums to integers for consistency + with scoped enums. + +* Added an argument-dependent lookup based ``format_as`` extension API to + simplify formatting of enums. + +* Added experimental ``std::variant`` formatting support + (`#2941 <https://github.com/fmtlib/fmt/pull/2941>`_). + For example (`godbolt <https://godbolt.org/z/KG9z6cq68>`__): + + .. code:: c++ + + #include <variant> + #include <fmt/std.h> + + int main() { + auto v = std::variant<int, std::string>(42); + fmt::print("{}\n", v); + } + + prints:: + + variant(42) + + Thanks `@jehelset <https://github.com/jehelset>`_. + +* Added experimental ``std::filesystem::path`` formatting support + (`#2865 <https://github.com/fmtlib/fmt/issues/2865>`_, + `#2902 <https://github.com/fmtlib/fmt/pull/2902>`_, + `#2917 <https://github.com/fmtlib/fmt/issues/2917>`_, + `#2918 <https://github.com/fmtlib/fmt/pull/2918>`_). + For example (`godbolt <https://godbolt.org/z/o44dMexEb>`__): + + .. code:: c++ + + #include <filesystem> + #include <fmt/std.h> + + int main() { + fmt::print("There is no place like {}.", std::filesystem::path("/home")); + } + + prints:: + + There is no place like "/home". + + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Added a ``std::thread::id`` formatter to ``fmt/std.h``. + For example (`godbolt <https://godbolt.org/z/j1azbYf3E>`__): + + .. code:: c++ + + #include <thread> + #include <fmt/std.h> + + int main() { + fmt::print("Current thread id: {}\n", std::this_thread::get_id()); + } + +* Added ``fmt::styled`` that applies a text style to an individual argument + (`#2793 <https://github.com/fmtlib/fmt/pull/2793>`_). + For example (`godbolt <https://godbolt.org/z/vWGW7v5M6>`__): + + .. code:: c++ + + #include <fmt/chrono.h> + #include <fmt/color.h> + + int main() { + auto now = std::chrono::system_clock::now(); + fmt::print( + "[{}] {}: {}\n", + fmt::styled(now, fmt::emphasis::bold), + fmt::styled("error", fg(fmt::color::red)), + "something went wrong"); + } + + prints + + .. image:: https://user-images.githubusercontent.com/576385/ + 175071215-12809244-dab0-4005-96d8-7cd911c964d5.png + + Thanks `@rbrugo (Riccardo Brugo) <https://github.com/rbrugo>`_. + +* Made ``fmt::print`` overload for text styles correctly handle UTF-8 + (`#2681 <https://github.com/fmtlib/fmt/issues/2681>`_, + `#2701 <https://github.com/fmtlib/fmt/pull/2701>`_). + Thanks `@AlexGuteniev (Alex Guteniev) <https://github.com/AlexGuteniev>`_. + +* Fixed Unicode handling when writing to an ostream. + +* Added support for nested specifiers to range formatting + (`#2673 <https://github.com/fmtlib/fmt/pull/2673>`_). + For example (`godbolt <https://godbolt.org/z/xd3Gj38cf>`__): + + .. code:: c++ + + #include <vector> + #include <fmt/ranges.h> + + int main() { + fmt::print("{::#x}\n", std::vector{10, 20, 30}); + } + + prints ``[0xa, 0x14, 0x1e]``. + + Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_. + +* Implemented escaping of wide strings in ranges + (`#2904 <https://github.com/fmtlib/fmt/pull/2904>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Added support for ranges with ``begin`` / ``end`` found via the + argument-dependent lookup + (`#2807 <https://github.com/fmtlib/fmt/pull/2807>`_). + Thanks `@rbrugo (Riccardo Brugo) <https://github.com/rbrugo>`_. + +* Fixed formatting of certain kinds of ranges of ranges + (`#2787 <https://github.com/fmtlib/fmt/pull/2787>`_). + Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_. + +* Fixed handling of maps with element types other than ``std::pair`` + (`#2944 <https://github.com/fmtlib/fmt/pull/2944>`_). + Thanks `@BrukerJWD (Jonathan W) <https://github.com/BrukerJWD>`_. + +* Made tuple formatter enabled only if elements are formattable + (`#2939 <https://github.com/fmtlib/fmt/issues/2939>`_, + `#2940 <https://github.com/fmtlib/fmt/pull/2940>`_). + Thanks `@jehelset <https://github.com/jehelset>`_. + +* Made ``fmt::join`` compatible with format string compilation + (`#2719 <https://github.com/fmtlib/fmt/issues/2719>`_, + `#2720 <https://github.com/fmtlib/fmt/pull/2720>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Made compile-time checks work with named arguments of custom types and + ``std::ostream`` ``print`` overloads + (`#2816 <https://github.com/fmtlib/fmt/issues/2816>`_, + `#2817 <https://github.com/fmtlib/fmt/issues/2817>`_, + `#2819 <https://github.com/fmtlib/fmt/pull/2819>`_). + Thanks `@timsong-cpp <https://github.com/timsong-cpp>`_. + +* Removed ``make_args_checked`` because it is no longer needed for compile-time + checks (`#2760 <https://github.com/fmtlib/fmt/pull/2760>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Removed the following deprecated APIs: ``_format``, ``arg_join``, + the ``format_to`` overload that takes a memory buffer, + ``[v]fprintf`` that takes an ``ostream``. + +* Removed the deprecated implicit conversion of ``[const] signed char*`` and + ``[const] unsigned char*`` to C strings. + +* Removed the deprecated ``fmt/locale.h``. + +* Replaced the deprecated ``fileno()`` with ``descriptor()`` in + ``buffered_file``. + +* Moved ``to_string_view`` to the ``detail`` namespace since it's an + implementation detail. + +* Made access mode of a created file consistent with ``fopen`` by setting + ``S_IWGRP`` and ``S_IWOTH`` + (`#2733 <https://github.com/fmtlib/fmt/pull/2733>`_). + Thanks `@arogge (Andreas Rogge) <https://github.com/arogge>`_. + +* Removed a redundant buffer resize when formatting to ``std::ostream`` + (`#2842 <https://github.com/fmtlib/fmt/issues/2842>`_, + `#2843 <https://github.com/fmtlib/fmt/pull/2843>`_). + Thanks `@jcelerier (Jean-Michaël Celerier) <https://github.com/jcelerier>`_. + +* Made precision computation for strings consistent with width + (`#2888 <https://github.com/fmtlib/fmt/issues/2888>`_). + +* Fixed handling of locale separators in floating point formatting + (`#2830 <https://github.com/fmtlib/fmt/issues/2830>`_). + +* Made sign specifiers work with ``__int128_t`` + (`#2773 <https://github.com/fmtlib/fmt/issues/2773>`_). + +* Improved support for systems such as CHERI with extra data stored in pointers + (`#2932 <https://github.com/fmtlib/fmt/pull/2932>`_). + Thanks `@davidchisnall (David Chisnall) <https://github.com/davidchisnall>`_. + +* Improved documentation + (`#2706 <https://github.com/fmtlib/fmt/pull/2706>`_, + `#2712 <https://github.com/fmtlib/fmt/pull/2712>`_, + `#2789 <https://github.com/fmtlib/fmt/pull/2789>`_, + `#2803 <https://github.com/fmtlib/fmt/pull/2803>`_, + `#2805 <https://github.com/fmtlib/fmt/pull/2805>`_, + `#2815 <https://github.com/fmtlib/fmt/pull/2815>`_, + `#2924 <https://github.com/fmtlib/fmt/pull/2924>`_). + Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_, + `@Pokechu22 <https://github.com/Pokechu22>`_, + `@setoye (Alta) <https://github.com/setoye>`_, + `@rtobar <https://github.com/rtobar>`_, + `@rbrugo (Riccardo Brugo) <https://github.com/rbrugo>`_, + `@anoonD (cre) <https://github.com/anoonD>`_, + `@leha-bot (Alex) <https://github.com/leha-bot>`_. + +* Improved build configuration + (`#2766 <https://github.com/fmtlib/fmt/pull/2766>`_, + `#2772 <https://github.com/fmtlib/fmt/pull/2772>`_, + `#2836 <https://github.com/fmtlib/fmt/pull/2836>`_, + `#2852 <https://github.com/fmtlib/fmt/pull/2852>`_, + `#2907 <https://github.com/fmtlib/fmt/pull/2907>`_, + `#2913 <https://github.com/fmtlib/fmt/pull/2913>`_, + `#2914 <https://github.com/fmtlib/fmt/pull/2914>`_). + Thanks `@kambala-decapitator (Andrey Filipenkov) + <https://github.com/kambala-decapitator>`_, + `@mattiasljungstrom (Mattias Ljungström) + <https://github.com/mattiasljungstrom>`_, + `@kieselnb (Nick Kiesel) <https://github.com/kieselnb>`_, + `@nathannaveen <https://github.com/nathannaveen>`_, + `@Vertexwahn <https://github.com/Vertexwahn>`_. + +* Fixed various warnings and compilation issues + (`#2408 <https://github.com/fmtlib/fmt/issues/2408>`_, + `#2507 <https://github.com/fmtlib/fmt/issues/2507>`_, + `#2697 <https://github.com/fmtlib/fmt/issues/2697>`_, + `#2715 <https://github.com/fmtlib/fmt/issues/2715>`_, + `#2717 <https://github.com/fmtlib/fmt/issues/2717>`_, + `#2722 <https://github.com/fmtlib/fmt/pull/2722>`_, + `#2724 <https://github.com/fmtlib/fmt/pull/2724>`_, + `#2725 <https://github.com/fmtlib/fmt/pull/2725>`_, + `#2726 <https://github.com/fmtlib/fmt/issues/2726>`_, + `#2728 <https://github.com/fmtlib/fmt/pull/2728>`_, + `#2732 <https://github.com/fmtlib/fmt/pull/2732>`_, + `#2738 <https://github.com/fmtlib/fmt/issues/2738>`_, + `#2742 <https://github.com/fmtlib/fmt/pull/2742>`_, + `#2744 <https://github.com/fmtlib/fmt/issues/2744>`_, + `#2745 <https://github.com/fmtlib/fmt/issues/2745>`_, + `#2746 <https://github.com/fmtlib/fmt/issues/2746>`_, + `#2754 <https://github.com/fmtlib/fmt/issues/2754>`_, + `#2755 <https://github.com/fmtlib/fmt/pull/2755>`_, + `#2757 <https://github.com/fmtlib/fmt/issues/2757>`_, + `#2758 <https://github.com/fmtlib/fmt/pull/2758>`_, + `#2761 <https://github.com/fmtlib/fmt/issues/2761>`_, + `#2762 <https://github.com/fmtlib/fmt/pull/2762>`_, + `#2763 <https://github.com/fmtlib/fmt/issues/2763>`_, + `#2765 <https://github.com/fmtlib/fmt/pull/2765>`_, + `#2769 <https://github.com/fmtlib/fmt/issues/2769>`_, + `#2770 <https://github.com/fmtlib/fmt/pull/2770>`_, + `#2771 <https://github.com/fmtlib/fmt/issues/2771>`_, + `#2777 <https://github.com/fmtlib/fmt/issues/2777>`_, + `#2779 <https://github.com/fmtlib/fmt/pull/2779>`_, + `#2782 <https://github.com/fmtlib/fmt/pull/2782>`_, + `#2783 <https://github.com/fmtlib/fmt/pull/2783>`_, + `#2794 <https://github.com/fmtlib/fmt/issues/2794>`_, + `#2796 <https://github.com/fmtlib/fmt/issues/2796>`_, + `#2797 <https://github.com/fmtlib/fmt/pull/2797>`_, + `#2801 <https://github.com/fmtlib/fmt/pull/2801>`_, + `#2802 <https://github.com/fmtlib/fmt/pull/2802>`_, + `#2808 <https://github.com/fmtlib/fmt/issues/2808>`_, + `#2818 <https://github.com/fmtlib/fmt/issues/2818>`_, + `#2819 <https://github.com/fmtlib/fmt/pull/2819>`_, + `#2829 <https://github.com/fmtlib/fmt/issues/2829>`_, + `#2835 <https://github.com/fmtlib/fmt/issues/2835>`_, + `#2848 <https://github.com/fmtlib/fmt/issues/2848>`_, + `#2860 <https://github.com/fmtlib/fmt/issues/2860>`_, + `#2861 <https://github.com/fmtlib/fmt/pull/2861>`_, + `#2882 <https://github.com/fmtlib/fmt/pull/2882>`_, + `#2886 <https://github.com/fmtlib/fmt/issues/2886>`_, + `#2891 <https://github.com/fmtlib/fmt/issues/2891>`_, + `#2892 <https://github.com/fmtlib/fmt/pull/2892>`_, + `#2895 <https://github.com/fmtlib/fmt/issues/2895>`_, + `#2896 <https://github.com/fmtlib/fmt/issues/2896>`_, + `#2903 <https://github.com/fmtlib/fmt/pull/2903>`_, + `#2906 <https://github.com/fmtlib/fmt/issues/2906>`_, + `#2908 <https://github.com/fmtlib/fmt/issues/2908>`_, + `#2909 <https://github.com/fmtlib/fmt/pull/2909>`_, + `#2920 <https://github.com/fmtlib/fmt/issues/2920>`_, + `#2922 <https://github.com/fmtlib/fmt/pull/2922>`_, + `#2927 <https://github.com/fmtlib/fmt/pull/2927>`_, + `#2929 <https://github.com/fmtlib/fmt/pull/2929>`_, + `#2936 <https://github.com/fmtlib/fmt/issues/2936>`_, + `#2937 <https://github.com/fmtlib/fmt/pull/2937>`_, + `#2938 <https://github.com/fmtlib/fmt/pull/2938>`_, + `#2951 <https://github.com/fmtlib/fmt/pull/2951>`_, + `#2954 <https://github.com/fmtlib/fmt/issues/2954>`_, + `#2957 <https://github.com/fmtlib/fmt/pull/2957>`_, + `#2958 <https://github.com/fmtlib/fmt/issues/2958>`_, + `#2960 <https://github.com/fmtlib/fmt/pull/2960>`_). + Thanks `@matrackif <https://github.com/matrackif>`_ + `@Tobi823 (Tobias Hellmann) <https://github.com/Tobi823>`_, + `@ivan-volnov (Ivan Volnov) <https://github.com/ivan-volnov>`_, + `@VasiliPupkin256 <https://github.com/VasiliPupkin256>`_, + `@federico-busato (Federico) <https://github.com/federico-busato>`_, + `@barcharcraz (Charlie Barto) <https://github.com/barcharcraz>`_, + `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_, + `@HazardyKnusperkeks (Björn Schäpers) + <https://github.com/HazardyKnusperkeks>`_, + `@dalboris (Boris Dalstein) <https://github.com/dalboris>`_, + `@seanm (Sean McBride) <https://github.com/seanm>`_, + `@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_, + `@timsong-cpp <https://github.com/timsong-cpp>`_, + `@seanm (Sean McBride) <https://github.com/seanm>`_, + `@frithrah <https://github.com/frithrah>`_, + `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_, + `@Agga <https://github.com/Agga>`_, + `@madmaxoft (Mattes D) <https://github.com/madmaxoft>`_, + `@JurajX (Juraj) <https://github.com/JurajX>`_, + `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_, + `@Dani-Hub (Daniel Krügler) <https://github.com/Dani-Hub>`_. + +8.1.1 - 2022-01-06 +------------------ + +* Restored ABI compatibility with version 8.0.x + (`#2695 <https://github.com/fmtlib/fmt/issues/2695>`_, + `#2696 <https://github.com/fmtlib/fmt/pull/2696>`_). + Thanks `@saraedum (Julian Rüth) <https://github.com/saraedum>`_. + +* Fixed chrono formatting on big endian systems + (`#2698 <https://github.com/fmtlib/fmt/issues/2698>`_, + `#2699 <https://github.com/fmtlib/fmt/pull/2699>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_ and + `@xvitaly (Vitaly Zaitsev) <https://github.com/xvitaly>`_. + +* Fixed a linkage error with mingw + (`#2691 <https://github.com/fmtlib/fmt/issues/2691>`_, + `#2692 <https://github.com/fmtlib/fmt/pull/2692>`_). + Thanks `@rbberger (Richard Berger) <https://github.com/rbberger>`_. + +8.1.0 - 2022-01-02 +------------------ + +* Optimized chrono formatting + (`#2500 <https://github.com/fmtlib/fmt/pull/2500>`_, + `#2537 <https://github.com/fmtlib/fmt/pull/2537>`_, + `#2541 <https://github.com/fmtlib/fmt/issues/2541>`_, + `#2544 <https://github.com/fmtlib/fmt/pull/2544>`_, + `#2550 <https://github.com/fmtlib/fmt/pull/2550>`_, + `#2551 <https://github.com/fmtlib/fmt/pull/2551>`_, + `#2576 <https://github.com/fmtlib/fmt/pull/2576>`_, + `#2577 <https://github.com/fmtlib/fmt/issues/2577>`_, + `#2586 <https://github.com/fmtlib/fmt/pull/2586>`_, + `#2591 <https://github.com/fmtlib/fmt/pull/2591>`_, + `#2594 <https://github.com/fmtlib/fmt/pull/2594>`_, + `#2602 <https://github.com/fmtlib/fmt/pull/2602>`_, + `#2617 <https://github.com/fmtlib/fmt/pull/2617>`_, + `#2628 <https://github.com/fmtlib/fmt/issues/2628>`_, + `#2633 <https://github.com/fmtlib/fmt/pull/2633>`_, + `#2670 <https://github.com/fmtlib/fmt/issues/2670>`_, + `#2671 <https://github.com/fmtlib/fmt/pull/2671>`_). + + Processing of some specifiers such as ``%z`` and ``%Y`` is now up to 10-20 + times faster, for example on GCC 11 with libstdc++:: + + ---------------------------------------------------------------------------- + Benchmark Before After + ---------------------------------------------------------------------------- + FMTFormatter_z 261 ns 26.3 ns + FMTFormatterCompile_z 246 ns 11.6 ns + FMTFormatter_Y 263 ns 26.1 ns + FMTFormatterCompile_Y 244 ns 10.5 ns + ---------------------------------------------------------------------------- + + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_ and + `@toughengineer (Pavel Novikov) <https://github.com/toughengineer>`_. + +* Implemented subsecond formatting for chrono durations + (`#2623 <https://github.com/fmtlib/fmt/pull/2623>`_). + For example (`godbolt <https://godbolt.org/z/es7vWTETe>`__): + + .. code:: c++ + + #include <fmt/chrono.h> + + int main() { + fmt::print("{:%S}", std::chrono::milliseconds(1234)); + } + + prints "01.234". + + Thanks `@matrackif <https://github.com/matrackif>`_. + +* Fixed handling of precision 0 when formatting chrono durations + (`#2587 <https://github.com/fmtlib/fmt/issues/2587>`_, + `#2588 <https://github.com/fmtlib/fmt/pull/2588>`_). + Thanks `@lukester1975 <https://github.com/lukester1975>`_. + +* Fixed an overflow on invalid inputs in the ``tm`` formatter + (`#2564 <https://github.com/fmtlib/fmt/pull/2564>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Added ``fmt::group_digits`` that formats integers with a non-localized digit + separator (comma) for groups of three digits. + For example (`godbolt <https://godbolt.org/z/TxGxG9Poq>`__): + + .. code:: c++ + + #include <fmt/format.h> + + int main() { + fmt::print("{} dollars", fmt::group_digits(1000000)); + } + + prints "1,000,000 dollars". + +* Added support for faint, conceal, reverse and blink text styles + (`#2394 <https://github.com/fmtlib/fmt/pull/2394>`_): + + https://user-images.githubusercontent.com/576385/147710227-c68f5317-f8fa-42c3-9123-7c4ba3c398cb.mp4 + + Thanks `@benit8 (Benoît Lormeau) <https://github.com/benit8>`_ and + `@data-man (Dmitry Atamanov) <https://github.com/data-man>`_. + +* Added experimental support for compile-time floating point formatting + (`#2426 <https://github.com/fmtlib/fmt/pull/2426>`_, + `#2470 <https://github.com/fmtlib/fmt/pull/2470>`_). + It is currently limited to the header-only mode. + Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_. + +* Added UDL-based named argument support to compile-time format string checks + (`#2640 <https://github.com/fmtlib/fmt/issues/2640>`_, + `#2649 <https://github.com/fmtlib/fmt/pull/2649>`_). + For example (`godbolt <https://godbolt.org/z/ohGbbvonv>`__): + + .. code:: c++ + + #include <fmt/format.h> + + int main() { + using namespace fmt::literals; + fmt::print("{answer:s}", "answer"_a=42); + } + + gives a compile-time error on compilers with C++20 ``consteval`` and non-type + template parameter support (gcc 10+) because ``s`` is not a valid format + specifier for an integer. + + Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_. + +* Implemented escaping of string range elements. + For example (`godbolt <https://godbolt.org/z/rKvM1vKf3>`__): + + .. code:: c++ + + #include <fmt/ranges.h> + #include <vector> + + int main() { + fmt::print("{}", std::vector<std::string>{"\naan"}); + } + + is now printed as:: + + ["\naan"] + + instead of:: + + [" + aan"] + +* Added an experimental ``?`` specifier for escaping strings. + (`#2674 <https://github.com/fmtlib/fmt/pull/2674>`_). + Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_. + +* Switched to JSON-like representation of maps and sets for consistency with + Python's ``str.format``. + For example (`godbolt <https://godbolt.org/z/seKjoY9W5>`__): + + .. code:: c++ + + #include <fmt/ranges.h> + #include <map> + + int main() { + fmt::print("{}", std::map<std::string, int>{{"answer", 42}}); + } + + is now printed as:: + + {"answer": 42} + +* Extended ``fmt::join`` to support C++20-only ranges + (`#2549 <https://github.com/fmtlib/fmt/pull/2549>`_). + Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_. + +* Optimized handling of non-const-iterable ranges and implemented initial + support for non-const-formattable types. + +* Disabled implicit conversions of scoped enums to integers that was + accidentally introduced in earlier versions + (`#1841 <https://github.com/fmtlib/fmt/pull/1841>`_). + +* Deprecated implicit conversion of ``[const] signed char*`` and + ``[const] unsigned char*`` to C strings. + +* Deprecated ``_format``, a legacy UDL-based format API + (`#2646 <https://github.com/fmtlib/fmt/pull/2646>`_). + Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_. + +* Marked ``format``, ``formatted_size`` and ``to_string`` as ``[[nodiscard]]`` + (`#2612 <https://github.com/fmtlib/fmt/pull/2612>`_). + `@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_. + +* Added missing diagnostic when trying to format function and member pointers + as well as objects convertible to pointers which is explicitly disallowed + (`#2598 <https://github.com/fmtlib/fmt/issues/2598>`_, + `#2609 <https://github.com/fmtlib/fmt/pull/2609>`_, + `#2610 <https://github.com/fmtlib/fmt/pull/2610>`_). + Thanks `@AlexGuteniev (Alex Guteniev) <https://github.com/AlexGuteniev>`_. + +* Optimized writing to a contiguous buffer with ``format_to_n`` + (`#2489 <https://github.com/fmtlib/fmt/pull/2489>`_). + Thanks `@Roman-Koshelev <https://github.com/Roman-Koshelev>`_. + +* Optimized writing to non-``char`` buffers + (`#2477 <https://github.com/fmtlib/fmt/pull/2477>`_). + Thanks `@Roman-Koshelev <https://github.com/Roman-Koshelev>`_. + +* Decimal point is now localized when using the ``L`` specifier. + +* Improved floating point formatter implementation + (`#2498 <https://github.com/fmtlib/fmt/pull/2498>`_, + `#2499 <https://github.com/fmtlib/fmt/pull/2499>`_). + Thanks `@Roman-Koshelev <https://github.com/Roman-Koshelev>`_. + +* Fixed handling of very large precision in fixed format + (`#2616 <https://github.com/fmtlib/fmt/pull/2616>`_). + +* Made a table of cached powers used in FP formatting static + (`#2509 <https://github.com/fmtlib/fmt/pull/2509>`_). + Thanks `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_. + +* Resolved a lookup ambiguity with C++20 format-related functions due to ADL + (`#2639 <https://github.com/fmtlib/fmt/issues/2639>`_, + `#2641 <https://github.com/fmtlib/fmt/pull/2641>`_). + Thanks `@mkurdej (Marek Kurdej) <https://github.com/mkurdej>`_. + +* Removed unnecessary inline namespace qualification + (`#2642 <https://github.com/fmtlib/fmt/issues/2642>`_, + `#2643 <https://github.com/fmtlib/fmt/pull/2643>`_). + Thanks `@mkurdej (Marek Kurdej) <https://github.com/mkurdej>`_. + +* Implemented argument forwarding in ``format_to_n`` + (`#2462 <https://github.com/fmtlib/fmt/issues/2462>`_, + `#2463 <https://github.com/fmtlib/fmt/pull/2463>`_). + Thanks `@owent (WenTao Ou) <https://github.com/owent>`_. + +* Fixed handling of implicit conversions in ``fmt::to_string`` and format string + compilation (`#2565 <https://github.com/fmtlib/fmt/issues/2565>`_). + +* Changed the default access mode of files created by ``fmt::output_file`` to + ``-rw-r--r--`` for consistency with ``fopen`` + (`#2530 <https://github.com/fmtlib/fmt/issues/2530>`_). + +* Make ``fmt::ostream::flush`` public + (`#2435 <https://github.com/fmtlib/fmt/issues/2435>`_). + +* Improved C++14/17 attribute detection + (`#2615 <https://github.com/fmtlib/fmt/pull/2615>`_). + Thanks `@AlexGuteniev (Alex Guteniev) <https://github.com/AlexGuteniev>`_. + +* Improved ``consteval`` detection for MSVC + (`#2559 <https://github.com/fmtlib/fmt/pull/2559>`_). + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. + +* Improved documentation + (`#2406 <https://github.com/fmtlib/fmt/issues/2406>`_, + `#2446 <https://github.com/fmtlib/fmt/pull/2446>`_, + `#2493 <https://github.com/fmtlib/fmt/issues/2493>`_, + `#2513 <https://github.com/fmtlib/fmt/issues/2513>`_, + `#2515 <https://github.com/fmtlib/fmt/pull/2515>`_, + `#2522 <https://github.com/fmtlib/fmt/issues/2522>`_, + `#2562 <https://github.com/fmtlib/fmt/pull/2562>`_, + `#2575 <https://github.com/fmtlib/fmt/pull/2575>`_, + `#2606 <https://github.com/fmtlib/fmt/pull/2606>`_, + `#2620 <https://github.com/fmtlib/fmt/pull/2620>`_, + `#2676 <https://github.com/fmtlib/fmt/issues/2676>`_). + Thanks `@sobolevn (Nikita Sobolev) <https://github.com/sobolevn>`_, + `@UnePierre (Max FERGER) <https://github.com/UnePierre>`_, + `@zhsj <https://github.com/zhsj>`_, + `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_, + `@ericcurtin (Eric Curtin) <https://github.com/ericcurtin>`_, + `@Lounarok <https://github.com/Lounarok>`_. + +* Improved fuzzers and added a fuzzer for chrono timepoint formatting + (`#2461 <https://github.com/fmtlib/fmt/pull/2461>`_, + `#2469 <https://github.com/fmtlib/fmt/pull/2469>`_). + `@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_, + +* Added the ``FMT_SYSTEM_HEADERS`` CMake option setting which marks {fmt}'s + headers as system. It can be used to suppress warnings + (`#2644 <https://github.com/fmtlib/fmt/issues/2644>`_, + `#2651 <https://github.com/fmtlib/fmt/pull/2651>`_). + Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_. + +* Added the Bazel build system support + (`#2505 <https://github.com/fmtlib/fmt/pull/2505>`_, + `#2516 <https://github.com/fmtlib/fmt/pull/2516>`_). + Thanks `@Vertexwahn <https://github.com/Vertexwahn>`_. + +* Improved build configuration and tests + (`#2437 <https://github.com/fmtlib/fmt/issues/2437>`_, + `#2558 <https://github.com/fmtlib/fmt/pull/2558>`_, + `#2648 <https://github.com/fmtlib/fmt/pull/2648>`_, + `#2650 <https://github.com/fmtlib/fmt/pull/2650>`_, + `#2663 <https://github.com/fmtlib/fmt/pull/2663>`_, + `#2677 <https://github.com/fmtlib/fmt/pull/2677>`_). + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_, + `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_, + `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Fixed various warnings and compilation issues + (`#2353 <https://github.com/fmtlib/fmt/pull/2353>`_, + `#2356 <https://github.com/fmtlib/fmt/pull/2356>`_, + `#2399 <https://github.com/fmtlib/fmt/pull/2399>`_, + `#2408 <https://github.com/fmtlib/fmt/issues/2408>`_, + `#2414 <https://github.com/fmtlib/fmt/pull/2414>`_, + `#2427 <https://github.com/fmtlib/fmt/pull/2427>`_, + `#2432 <https://github.com/fmtlib/fmt/pull/2432>`_, + `#2442 <https://github.com/fmtlib/fmt/pull/2442>`_, + `#2434 <https://github.com/fmtlib/fmt/pull/2434>`_, + `#2439 <https://github.com/fmtlib/fmt/issues/2439>`_, + `#2447 <https://github.com/fmtlib/fmt/pull/2447>`_, + `#2450 <https://github.com/fmtlib/fmt/pull/2450>`_, + `#2455 <https://github.com/fmtlib/fmt/issues/2455>`_, + `#2465 <https://github.com/fmtlib/fmt/issues/2465>`_, + `#2472 <https://github.com/fmtlib/fmt/issues/2472>`_, + `#2474 <https://github.com/fmtlib/fmt/issues/2474>`_, + `#2476 <https://github.com/fmtlib/fmt/pull/2476>`_, + `#2478 <https://github.com/fmtlib/fmt/issues/2478>`_, + `#2479 <https://github.com/fmtlib/fmt/issues/2479>`_, + `#2481 <https://github.com/fmtlib/fmt/issues/2481>`_, + `#2482 <https://github.com/fmtlib/fmt/pull/2482>`_, + `#2483 <https://github.com/fmtlib/fmt/pull/2483>`_, + `#2490 <https://github.com/fmtlib/fmt/issues/2490>`_, + `#2491 <https://github.com/fmtlib/fmt/pull/2491>`_, + `#2510 <https://github.com/fmtlib/fmt/pull/2510>`_, + `#2518 <https://github.com/fmtlib/fmt/pull/2518>`_, + `#2528 <https://github.com/fmtlib/fmt/issues/2528>`_, + `#2529 <https://github.com/fmtlib/fmt/pull/2529>`_, + `#2539 <https://github.com/fmtlib/fmt/pull/2539>`_, + `#2540 <https://github.com/fmtlib/fmt/issues/2540>`_, + `#2545 <https://github.com/fmtlib/fmt/pull/2545>`_, + `#2555 <https://github.com/fmtlib/fmt/pull/2555>`_, + `#2557 <https://github.com/fmtlib/fmt/issues/2557>`_, + `#2570 <https://github.com/fmtlib/fmt/issues/2570>`_, + `#2573 <https://github.com/fmtlib/fmt/pull/2573>`_, + `#2582 <https://github.com/fmtlib/fmt/pull/2582>`_, + `#2605 <https://github.com/fmtlib/fmt/issues/2605>`_, + `#2611 <https://github.com/fmtlib/fmt/pull/2611>`_, + `#2647 <https://github.com/fmtlib/fmt/pull/2647>`_, + `#2627 <https://github.com/fmtlib/fmt/issues/2627>`_, + `#2630 <https://github.com/fmtlib/fmt/pull/2630>`_, + `#2635 <https://github.com/fmtlib/fmt/issues/2635>`_, + `#2638 <https://github.com/fmtlib/fmt/issues/2638>`_, + `#2653 <https://github.com/fmtlib/fmt/issues/2653>`_, + `#2654 <https://github.com/fmtlib/fmt/issues/2654>`_, + `#2661 <https://github.com/fmtlib/fmt/issues/2661>`_, + `#2664 <https://github.com/fmtlib/fmt/pull/2664>`_, + `#2684 <https://github.com/fmtlib/fmt/pull/2684>`_). + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_, + `@mwinterb <https://github.com/mwinterb>`_, + `@cdacamar (Cameron DaCamara) <https://github.com/cdacamar>`_, + `@TrebledJ (Johnathan) <https://github.com/TrebledJ>`_, + `@bodomartin (brm) <https://github.com/bodomartin>`_, + `@cquammen (Cory Quammen) <https://github.com/cquammen>`_, + `@white238 (Chris White) <https://github.com/white238>`_, + `@mmarkeloff (Max) <https://github.com/mmarkeloff>`_, + `@palacaze (Pierre-Antoine Lacaze) <https://github.com/palacaze>`_, + `@jcelerier (Jean-Michaël Celerier) <https://github.com/jcelerier>`_, + `@mborn-adi (Mathias Born) <https://github.com/mborn-adi>`_, + `@BrukerJWD (Jonathan W) <https://github.com/BrukerJWD>`_, + `@spyridon97 (Spiros Tsalikis) <https://github.com/spyridon97>`_, + `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_, + `@oliverlee (Oliver Lee) <https://github.com/oliverlee>`_, + `@joshessman-llnl (Josh Essman) <https://github.com/joshessman-llnl>`_, + `@akohlmey (Axel Kohlmeyer) <https://github.com/akohlmey>`_, + `@timkalu <https://github.com/timkalu>`_, + `@olupton (Olli Lupton) <https://github.com/olupton>`_, + `@Acretock <https://github.com/Acretock>`_, + `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_, + `@andrewcorrigan (Andrew Corrigan) <https://github.com/andrewcorrigan>`_, + `@lucpelletier <https://github.com/lucpelletier>`_, + `@HazardyKnusperkeks (Björn Schäpers) + <https://github.com/HazardyKnusperkeks>`_. + +8.0.1 - 2021-07-02 +------------------ + +* Fixed the version number in the inline namespace + (`#2374 <https://github.com/fmtlib/fmt/issues/2374>`_). + +* Added a missing presentation type check for ``std::string`` + (`#2402 <https://github.com/fmtlib/fmt/issues/2402>`_). + +* Fixed a linkage error when mixing code built with clang and gcc + (`#2377 <https://github.com/fmtlib/fmt/issues/2377>`_). + +* Fixed documentation issues + (`#2396 <https://github.com/fmtlib/fmt/pull/2396>`_, + `#2403 <https://github.com/fmtlib/fmt/issues/2403>`_, + `#2406 <https://github.com/fmtlib/fmt/issues/2406>`_). + Thanks `@mkurdej (Marek Kurdej) <https://github.com/mkurdej>`_. + +* Removed dead code in FP formatter ( + `#2398 <https://github.com/fmtlib/fmt/pull/2398>`_). + Thanks `@javierhonduco (Javier Honduvilla Coto) + <https://github.com/javierhonduco>`_. + +* Fixed various warnings and compilation issues + (`#2351 <https://github.com/fmtlib/fmt/issues/2351>`_, + `#2359 <https://github.com/fmtlib/fmt/issues/2359>`_, + `#2365 <https://github.com/fmtlib/fmt/pull/2365>`_, + `#2368 <https://github.com/fmtlib/fmt/issues/2368>`_, + `#2370 <https://github.com/fmtlib/fmt/pull/2370>`_, + `#2376 <https://github.com/fmtlib/fmt/pull/2376>`_, + `#2381 <https://github.com/fmtlib/fmt/pull/2381>`_, + `#2382 <https://github.com/fmtlib/fmt/pull/2382>`_, + `#2386 <https://github.com/fmtlib/fmt/issues/2386>`_, + `#2389 <https://github.com/fmtlib/fmt/pull/2389>`_, + `#2395 <https://github.com/fmtlib/fmt/pull/2395>`_, + `#2397 <https://github.com/fmtlib/fmt/pull/2397>`_, + `#2400 <https://github.com/fmtlib/fmt/issues/2400>`_, + `#2401 <https://github.com/fmtlib/fmt/issues/2401>`_, + `#2407 <https://github.com/fmtlib/fmt/pull/2407>`_). + Thanks `@zx2c4 (Jason A. Donenfeld) <https://github.com/zx2c4>`_, + `@AidanSun05 (Aidan Sun) <https://github.com/AidanSun05>`_, + `@mattiasljungstrom (Mattias Ljungström) + <https://github.com/mattiasljungstrom>`_, + `@joemmett (Jonathan Emmett) <https://github.com/joemmett>`_, + `@erengy (Eren Okka) <https://github.com/erengy>`_, + `@patlkli (Patrick Geltinger) <https://github.com/patlkli>`_, + `@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_, + `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +8.0.0 - 2021-06-21 +------------------ + +* Enabled compile-time format string checks by default. + For example (`godbolt <https://godbolt.org/z/sMxcohGjz>`__): + + .. code:: c++ + + #include <fmt/core.h> + + int main() { + fmt::print("{:d}", "I am not a number"); + } + + gives a compile-time error on compilers with C++20 ``consteval`` support + (gcc 10+, clang 11+) because ``d`` is not a valid format specifier for a + string. + + To pass a runtime string wrap it in ``fmt::runtime``: + + .. code:: c++ + + fmt::print(fmt::runtime("{:d}"), "I am not a number"); + +* Added compile-time formatting + (`#2019 <https://github.com/fmtlib/fmt/pull/2019>`_, + `#2044 <https://github.com/fmtlib/fmt/pull/2044>`_, + `#2056 <https://github.com/fmtlib/fmt/pull/2056>`_, + `#2072 <https://github.com/fmtlib/fmt/pull/2072>`_, + `#2075 <https://github.com/fmtlib/fmt/pull/2075>`_, + `#2078 <https://github.com/fmtlib/fmt/issues/2078>`_, + `#2129 <https://github.com/fmtlib/fmt/pull/2129>`_, + `#2326 <https://github.com/fmtlib/fmt/pull/2326>`_). + For example (`godbolt <https://godbolt.org/z/Mxx9d89jM>`__): + + .. code:: c++ + + #include <fmt/compile.h> + + consteval auto compile_time_itoa(int value) -> std::array<char, 10> { + auto result = std::array<char, 10>(); + fmt::format_to(result.data(), FMT_COMPILE("{}"), value); + return result; + } + + constexpr auto answer = compile_time_itoa(42); + + Most of the formatting functionality is available at compile time with a + notable exception of floating-point numbers and pointers. + Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_. + +* Optimized handling of format specifiers during format string compilation. + For example, hexadecimal formatting (``"{:x}"``) is now 3-7x faster than + before when using ``format_to`` with format string compilation and a + stack-allocated buffer (`#1944 <https://github.com/fmtlib/fmt/issues/1944>`_). + + Before (7.1.3):: + + ---------------------------------------------------------------------------- + Benchmark Time CPU Iterations + ---------------------------------------------------------------------------- + FMTCompileOld/0 15.5 ns 15.5 ns 43302898 + FMTCompileOld/42 16.6 ns 16.6 ns 43278267 + FMTCompileOld/273123 18.7 ns 18.6 ns 37035861 + FMTCompileOld/9223372036854775807 19.4 ns 19.4 ns 35243000 + ---------------------------------------------------------------------------- + + After (8.x):: + + ---------------------------------------------------------------------------- + Benchmark Time CPU Iterations + ---------------------------------------------------------------------------- + FMTCompileNew/0 1.99 ns 1.99 ns 360523686 + FMTCompileNew/42 2.33 ns 2.33 ns 279865664 + FMTCompileNew/273123 3.72 ns 3.71 ns 190230315 + FMTCompileNew/9223372036854775807 5.28 ns 5.26 ns 130711631 + ---------------------------------------------------------------------------- + + It is even faster than ``std::to_chars`` from libc++ compiled with clang on + macOS:: + + ---------------------------------------------------------------------------- + Benchmark Time CPU Iterations + ---------------------------------------------------------------------------- + ToChars/0 4.42 ns 4.41 ns 160196630 + ToChars/42 5.00 ns 4.98 ns 140735201 + ToChars/273123 7.26 ns 7.24 ns 95784130 + ToChars/9223372036854775807 8.77 ns 8.75 ns 75872534 + ---------------------------------------------------------------------------- + + In other cases, especially involving ``std::string`` construction, the + speed up is usually lower because handling format specifiers takes a smaller + fraction of the total time. + +* Added the ``_cf`` user-defined literal to represent a compiled format string. + It can be used instead of the ``FMT_COMPILE`` macro + (`#2043 <https://github.com/fmtlib/fmt/pull/2043>`_, + `#2242 <https://github.com/fmtlib/fmt/pull/2242>`_): + + .. code:: c++ + + #include <fmt/compile.h> + + using namespace fmt::literals; + auto s = fmt::format(FMT_COMPILE("{}"), 42); // 🙁 not modern + auto s = fmt::format("{}"_cf, 42); // 🙂 modern as hell + + It requires compiler support for class types in non-type template parameters + (a C++20 feature) which is available in GCC 9.3+. + Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_. + +* Format string compilation now requires ``format`` functions of ``formatter`` + specializations for user-defined types to be ``const``: + + .. code:: c++ + + template <> struct fmt::formatter<my_type>: formatter<string_view> { + template <typename FormatContext> + auto format(my_type obj, FormatContext& ctx) const { // Note const here. + // ... + } + }; + +* Added UDL-based named argument support to format string compilation + (`#2243 <https://github.com/fmtlib/fmt/pull/2243>`_, + `#2281 <https://github.com/fmtlib/fmt/pull/2281>`_). For example: + + .. code:: c++ + + #include <fmt/compile.h> + + using namespace fmt::literals; + auto s = fmt::format(FMT_COMPILE("{answer}"), "answer"_a = 42); + + Here the argument named "answer" is resolved at compile time with no + runtime overhead. + Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_. + +* Added format string compilation support to ``fmt::print`` + (`#2280 <https://github.com/fmtlib/fmt/issues/2280>`_, + `#2304 <https://github.com/fmtlib/fmt/pull/2304>`_). + Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_. + +* Added initial support for compiling {fmt} as a C++20 module + (`#2235 <https://github.com/fmtlib/fmt/pull/2235>`_, + `#2240 <https://github.com/fmtlib/fmt/pull/2240>`_, + `#2260 <https://github.com/fmtlib/fmt/pull/2260>`_, + `#2282 <https://github.com/fmtlib/fmt/pull/2282>`_, + `#2283 <https://github.com/fmtlib/fmt/pull/2283>`_, + `#2288 <https://github.com/fmtlib/fmt/pull/2288>`_, + `#2298 <https://github.com/fmtlib/fmt/pull/2298>`_, + `#2306 <https://github.com/fmtlib/fmt/pull/2306>`_, + `#2307 <https://github.com/fmtlib/fmt/pull/2307>`_, + `#2309 <https://github.com/fmtlib/fmt/pull/2309>`_, + `#2318 <https://github.com/fmtlib/fmt/pull/2318>`_, + `#2324 <https://github.com/fmtlib/fmt/pull/2324>`_, + `#2332 <https://github.com/fmtlib/fmt/pull/2332>`_, + `#2340 <https://github.com/fmtlib/fmt/pull/2340>`_). + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. + +* Made symbols private by default reducing shared library size + (`#2301 <https://github.com/fmtlib/fmt/pull/2301>`_). For example there was + a ~15% reported reduction on one platform. + Thanks `@sergiud (Sergiu Deitsch) <https://github.com/sergiud>`_. + +* Optimized includes making the result of preprocessing ``fmt/format.h`` + ~20% smaller with libstdc++/C++20 and slightly improving build times + (`#1998 <https://github.com/fmtlib/fmt/issues/1998>`_). + +* Added support of ranges with non-const ``begin`` / ``end`` + (`#1953 <https://github.com/fmtlib/fmt/pull/1953>`_). + Thanks `@kitegi (sarah) <https://github.com/kitegi>`_. + +* Added support of ``std::byte`` and other formattable types to ``fmt::join`` + (`#1981 <https://github.com/fmtlib/fmt/issues/1981>`_, + `#2040 <https://github.com/fmtlib/fmt/issues/2040>`_, + `#2050 <https://github.com/fmtlib/fmt/pull/2050>`_, + `#2262 <https://github.com/fmtlib/fmt/issues/2262>`_). For example: + + .. code:: c++ + + #include <fmt/format.h> + #include <cstddef> + #include <vector> + + int main() { + auto bytes = std::vector{std::byte(4), std::byte(2)}; + fmt::print("{}", fmt::join(bytes, "")); + } + + prints "42". + + Thanks `@kamibo (Camille Bordignon) <https://github.com/kamibo>`_. + +* Implemented the default format for ``std::chrono::system_clock`` + (`#2319 <https://github.com/fmtlib/fmt/issues/2319>`_, + `#2345 <https://github.com/fmtlib/fmt/pull/2345>`_). For example: + + .. code:: c++ + + #include <fmt/chrono.h> + + int main() { + fmt::print("{}", std::chrono::system_clock::now()); + } + + prints "2021-06-18 15:22:00" (the output depends on the current date and + time). Thanks `@sunmy2019 <https://github.com/sunmy2019>`_. + +* Made more chrono specifiers locale independent by default. Use the ``'L'`` + specifier to get localized formatting. For example: + + .. code:: c++ + + #include <fmt/chrono.h> + + int main() { + std::locale::global(std::locale("ru_RU.UTF-8")); + auto monday = std::chrono::weekday(1); + fmt::print("{}\n", monday); // prints "Mon" + fmt::print("{:L}\n", monday); // prints "пн" + } + +* Improved locale handling in chrono formatting + (`#2337 <https://github.com/fmtlib/fmt/issues/2337>`_, + `#2349 <https://github.com/fmtlib/fmt/pull/2349>`_, + `#2350 <https://github.com/fmtlib/fmt/pull/2350>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Deprecated ``fmt/locale.h`` moving the formatting functions that take a + locale to ``fmt/format.h`` (``char``) and ``fmt/xchar`` (other overloads). + This doesn't introduce a dependency on ``<locale>`` so there is virtually no + compile time effect. + +* Deprecated an undocumented ``format_to`` overload that takes + ``basic_memory_buffer``. + +* Made parameter order in ``vformat_to`` consistent with ``format_to`` + (`#2327 <https://github.com/fmtlib/fmt/issues/2327>`_). + +* Added support for time points with arbitrary durations + (`#2208 <https://github.com/fmtlib/fmt/issues/2208>`_). For example: + + .. code:: c++ + + #include <fmt/chrono.h> + + int main() { + using tp = std::chrono::time_point< + std::chrono::system_clock, std::chrono::seconds>; + fmt::print("{:%S}", tp(std::chrono::seconds(42))); + } + + prints "42". + +* Formatting floating-point numbers no longer produces trailing zeros by default + for consistency with ``std::format``. For example: + + .. code:: c++ + + #include <fmt/core.h> + + int main() { + fmt::print("{0:.3}", 1.1); + } + + prints "1.1". Use the ``'#'`` specifier to keep trailing zeros. + +* Dropped a limit on the number of elements in a range and replaced ``{}`` with + ``[]`` as range delimiters for consistency with Python's ``str.format``. + +* The ``'L'`` specifier for locale-specific numeric formatting can now be + combined with presentation specifiers as in ``std::format``. For example: + + .. code:: c++ + + #include <fmt/core.h> + #include <locale> + + int main() { + std::locale::global(std::locale("fr_FR.UTF-8")); + fmt::print("{0:.2Lf}", 0.42); + } + + prints "0,42". The deprecated ``'n'`` specifier has been removed. + +* Made the ``0`` specifier ignored for infinity and NaN + (`#2305 <https://github.com/fmtlib/fmt/issues/2305>`_, + `#2310 <https://github.com/fmtlib/fmt/pull/2310>`_). + Thanks `@Liedtke (Matthias Liedtke) <https://github.com/Liedtke>`_. + +* Made the hexfloat formatting use the right alignment by default + (`#2308 <https://github.com/fmtlib/fmt/issues/2308>`_, + `#2317 <https://github.com/fmtlib/fmt/pull/2317>`_). + Thanks `@Liedtke (Matthias Liedtke) <https://github.com/Liedtke>`_. + +* Removed the deprecated numeric alignment (``'='``). Use the ``'0'`` specifier + instead. + +* Removed the deprecated ``fmt/posix.h`` header that has been replaced with + ``fmt/os.h``. + +* Removed the deprecated ``format_to_n_context``, ``format_to_n_args`` and + ``make_format_to_n_args``. They have been replaced with ``format_context``, + ``format_args` and ``make_format_args`` respectively. + +* Moved ``wchar_t``-specific functions and types to ``fmt/xchar.h``. + You can define ``FMT_DEPRECATED_INCLUDE_XCHAR`` to automatically include + ``fmt/xchar.h`` from ``fmt/format.h`` but this will be disabled in the next + major release. + +* Fixed handling of the ``'+'`` specifier in localized formatting + (`#2133 <https://github.com/fmtlib/fmt/issues/2133>`_). + +* Added support for the ``'s'`` format specifier that gives textual + representation of ``bool`` + (`#2094 <https://github.com/fmtlib/fmt/issues/2094>`_, + `#2109 <https://github.com/fmtlib/fmt/pull/2109>`_). For example: + + .. code:: c++ + + #include <fmt/core.h> + + int main() { + fmt::print("{:s}", true); + } + + prints "true". + Thanks `@powercoderlol (Ivan Polyakov) <https://github.com/powercoderlol>`_. + +* Made ``fmt::ptr`` work with function pointers + (`#2131 <https://github.com/fmtlib/fmt/pull/2131>`_). For example: + + .. code:: c++ + + #include <fmt/format.h> + + int main() { + fmt::print("My main: {}\n", fmt::ptr(main)); + } + + Thanks `@mikecrowe (Mike Crowe) <https://github.com/mikecrowe>`_. + +* The undocumented support for specializing ``formatter`` for pointer types + has been removed. + +* Fixed ``fmt::formatted_size`` with format string compilation + (`#2141 <https://github.com/fmtlib/fmt/pull/2141>`_, + `#2161 <https://github.com/fmtlib/fmt/pull/2161>`_). + Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_. + +* Fixed handling of empty format strings during format string compilation + (`#2042 <https://github.com/fmtlib/fmt/issues/2042>`_): + + .. code:: c++ + + auto s = fmt::format(FMT_COMPILE("")); + + Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_. + +* Fixed handling of enums in ``fmt::to_string`` + (`#2036 <https://github.com/fmtlib/fmt/issues/2036>`_). + +* Improved width computation + (`#2033 <https://github.com/fmtlib/fmt/issues/2033>`_, + `#2091 <https://github.com/fmtlib/fmt/issues/2091>`_). For example: + + .. code:: c++ + + #include <fmt/core.h> + + int main() { + fmt::print("{:-<10}{}\n", "你好", "世界"); + fmt::print("{:-<10}{}\n", "hello", "world"); + } + + prints + + .. image:: https://user-images.githubusercontent.com/576385/ + 119840373-cea3ca80-beb9-11eb-91e0-54266c48e181.png + + on a modern terminal. + +* The experimental fast output stream (``fmt::ostream``) is now truncated by + default for consistency with ``fopen`` + (`#2018 <https://github.com/fmtlib/fmt/issues/2018>`_). For example: + + .. code:: c++ + + #include <fmt/os.h> + + int main() { + fmt::ostream out1 = fmt::output_file("guide"); + out1.print("Zaphod"); + out1.close(); + fmt::ostream out2 = fmt::output_file("guide"); + out2.print("Ford"); + } + + writes "Ford" to the file "guide". To preserve the old file content if any + pass ``fmt::file::WRONLY | fmt::file::CREATE`` flags to ``fmt::output_file``. + +* Fixed moving of ``fmt::ostream`` that holds buffered data + (`#2197 <https://github.com/fmtlib/fmt/issues/2197>`_, + `#2198 <https://github.com/fmtlib/fmt/pull/2198>`_). + Thanks `@vtta <https://github.com/vtta>`_. + +* Replaced the ``fmt::system_error`` exception with a function of the same + name that constructs ``std::system_error`` + (`#2266 <https://github.com/fmtlib/fmt/issues/2266>`_). + +* Replaced the ``fmt::windows_error`` exception with a function of the same + name that constructs ``std::system_error`` with the category returned by + ``fmt::system_category()`` + (`#2274 <https://github.com/fmtlib/fmt/issues/2274>`_, + `#2275 <https://github.com/fmtlib/fmt/pull/2275>`_). + The latter is similar to ``std::sytem_category`` but correctly handles UTF-8. + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Replaced ``fmt::error_code`` with ``std::error_code`` and made it formattable + (`#2269 <https://github.com/fmtlib/fmt/issues/2269>`_, + `#2270 <https://github.com/fmtlib/fmt/pull/2270>`_, + `#2273 <https://github.com/fmtlib/fmt/pull/2273>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Added speech synthesis support + (`#2206 <https://github.com/fmtlib/fmt/pull/2206>`_). + +* Made ``format_to`` work with a memory buffer that has a custom allocator + (`#2300 <https://github.com/fmtlib/fmt/pull/2300>`_). + Thanks `@voxmea <https://github.com/voxmea>`_. + +* Added ``Allocator::max_size`` support to ``basic_memory_buffer``. + (`#1960 <https://github.com/fmtlib/fmt/pull/1960>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Added wide string support to ``fmt::join`` + (`#2236 <https://github.com/fmtlib/fmt/pull/2236>`_). + Thanks `@crbrz <https://github.com/crbrz>`_. + +* Made iterators passed to ``formatter`` specializations via a format context + satisfy C++20 ``std::output_iterator`` requirements + (`#2156 <https://github.com/fmtlib/fmt/issues/2156>`_, + `#2158 <https://github.com/fmtlib/fmt/pull/2158>`_, + `#2195 <https://github.com/fmtlib/fmt/issues/2195>`_, + `#2204 <https://github.com/fmtlib/fmt/pull/2204>`_). + Thanks `@randomnetcat (Jason Cobb) <https://github.com/randomnetcat>`_. + +* Optimized the ``printf`` implementation + (`#1982 <https://github.com/fmtlib/fmt/pull/1982>`_, + `#1984 <https://github.com/fmtlib/fmt/pull/1984>`_, + `#2016 <https://github.com/fmtlib/fmt/pull/2016>`_, + `#2164 <https://github.com/fmtlib/fmt/pull/2164>`_). + Thanks `@rimathia <https://github.com/rimathia>`_ and + `@moiwi <https://github.com/moiwi>`_. + +* Improved detection of ``constexpr`` ``char_traits`` + (`#2246 <https://github.com/fmtlib/fmt/pull/2246>`_, + `#2257 <https://github.com/fmtlib/fmt/pull/2257>`_). + Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_. + +* Fixed writing to ``stdout`` when it is redirected to ``NUL`` on Windows + (`#2080 <https://github.com/fmtlib/fmt/issues/2080>`_). + +* Fixed exception propagation from iterators + (`#2097 <https://github.com/fmtlib/fmt/issues/2097>`_). + +* Improved ``strftime`` error handling + (`#2238 <https://github.com/fmtlib/fmt/issues/2238>`_, + `#2244 <https://github.com/fmtlib/fmt/pull/2244>`_). + Thanks `@yumeyao <https://github.com/yumeyao>`_. + +* Stopped using deprecated GCC UDL template extension. + +* Added ``fmt/args.h`` to the install target + (`#2096 <https://github.com/fmtlib/fmt/issues/2096>`_). + +* Error messages are now passed to assert when exceptions are disabled + (`#2145 <https://github.com/fmtlib/fmt/pull/2145>`_). + Thanks `@NobodyXu (Jiahao XU) <https://github.com/NobodyXu>`_. + +* Added the ``FMT_MASTER_PROJECT`` CMake option to control build and install + targets when {fmt} is included via ``add_subdirectory`` + (`#2098 <https://github.com/fmtlib/fmt/issues/2098>`_, + `#2100 <https://github.com/fmtlib/fmt/pull/2100>`_). + Thanks `@randomizedthinking <https://github.com/randomizedthinking>`_. + +* Improved build configuration + (`#2026 <https://github.com/fmtlib/fmt/pull/2026>`_, + `#2122 <https://github.com/fmtlib/fmt/pull/2122>`_). + Thanks `@luncliff (Park DongHa) <https://github.com/luncliff>`_ and + `@ibaned (Dan Ibanez) <https://github.com/ibaned>`_. + +* Fixed various warnings and compilation issues + (`#1947 <https://github.com/fmtlib/fmt/issues/1947>`_, + `#1959 <https://github.com/fmtlib/fmt/pull/1959>`_, + `#1963 <https://github.com/fmtlib/fmt/pull/1963>`_, + `#1965 <https://github.com/fmtlib/fmt/pull/1965>`_, + `#1966 <https://github.com/fmtlib/fmt/issues/1966>`_, + `#1974 <https://github.com/fmtlib/fmt/pull/1974>`_, + `#1975 <https://github.com/fmtlib/fmt/pull/1975>`_, + `#1990 <https://github.com/fmtlib/fmt/pull/1990>`_, + `#2000 <https://github.com/fmtlib/fmt/issues/2000>`_, + `#2001 <https://github.com/fmtlib/fmt/pull/2001>`_, + `#2002 <https://github.com/fmtlib/fmt/issues/2002>`_, + `#2004 <https://github.com/fmtlib/fmt/issues/2004>`_, + `#2006 <https://github.com/fmtlib/fmt/pull/2006>`_, + `#2009 <https://github.com/fmtlib/fmt/pull/2009>`_, + `#2010 <https://github.com/fmtlib/fmt/pull/2010>`_, + `#2038 <https://github.com/fmtlib/fmt/issues/2038>`_, + `#2039 <https://github.com/fmtlib/fmt/issues/2039>`_, + `#2047 <https://github.com/fmtlib/fmt/issues/2047>`_, + `#2053 <https://github.com/fmtlib/fmt/pull/2053>`_, + `#2059 <https://github.com/fmtlib/fmt/issues/2059>`_, + `#2065 <https://github.com/fmtlib/fmt/pull/2065>`_, + `#2067 <https://github.com/fmtlib/fmt/pull/2067>`_, + `#2068 <https://github.com/fmtlib/fmt/pull/2068>`_, + `#2073 <https://github.com/fmtlib/fmt/pull/2073>`_, + `#2103 <https://github.com/fmtlib/fmt/issues/2103>`_, + `#2105 <https://github.com/fmtlib/fmt/issues/2105>`_, + `#2106 <https://github.com/fmtlib/fmt/pull/2106>`_, + `#2107 <https://github.com/fmtlib/fmt/pull/2107>`_, + `#2116 <https://github.com/fmtlib/fmt/issues/2116>`_, + `#2117 <https://github.com/fmtlib/fmt/pull/2117>`_, + `#2118 <https://github.com/fmtlib/fmt/issues/2118>`_, + `#2119 <https://github.com/fmtlib/fmt/pull/2119>`_, + `#2127 <https://github.com/fmtlib/fmt/issues/2127>`_, + `#2128 <https://github.com/fmtlib/fmt/pull/2128>`_, + `#2140 <https://github.com/fmtlib/fmt/issues/2140>`_, + `#2142 <https://github.com/fmtlib/fmt/issues/2142>`_, + `#2143 <https://github.com/fmtlib/fmt/pull/2143>`_, + `#2144 <https://github.com/fmtlib/fmt/pull/2144>`_, + `#2147 <https://github.com/fmtlib/fmt/issues/2147>`_, + `#2148 <https://github.com/fmtlib/fmt/issues/2148>`_, + `#2149 <https://github.com/fmtlib/fmt/issues/2149>`_, + `#2152 <https://github.com/fmtlib/fmt/pull/2152>`_, + `#2160 <https://github.com/fmtlib/fmt/pull/2160>`_, + `#2170 <https://github.com/fmtlib/fmt/issues/2170>`_, + `#2175 <https://github.com/fmtlib/fmt/issues/2175>`_, + `#2176 <https://github.com/fmtlib/fmt/issues/2176>`_, + `#2177 <https://github.com/fmtlib/fmt/pull/2177>`_, + `#2178 <https://github.com/fmtlib/fmt/issues/2178>`_, + `#2179 <https://github.com/fmtlib/fmt/pull/2179>`_, + `#2180 <https://github.com/fmtlib/fmt/issues/2180>`_, + `#2181 <https://github.com/fmtlib/fmt/issues/2181>`_, + `#2183 <https://github.com/fmtlib/fmt/pull/2183>`_, + `#2184 <https://github.com/fmtlib/fmt/issues/2184>`_, + `#2185 <https://github.com/fmtlib/fmt/issues/2185>`_, + `#2186 <https://github.com/fmtlib/fmt/pull/2186>`_, + `#2187 <https://github.com/fmtlib/fmt/pull/2187>`_, + `#2190 <https://github.com/fmtlib/fmt/pull/2190>`_, + `#2192 <https://github.com/fmtlib/fmt/pull/2192>`_, + `#2194 <https://github.com/fmtlib/fmt/pull/2194>`_, + `#2205 <https://github.com/fmtlib/fmt/pull/2205>`_, + `#2210 <https://github.com/fmtlib/fmt/issues/2210>`_, + `#2211 <https://github.com/fmtlib/fmt/pull/2211>`_, + `#2215 <https://github.com/fmtlib/fmt/pull/2215>`_, + `#2216 <https://github.com/fmtlib/fmt/pull/2216>`_, + `#2218 <https://github.com/fmtlib/fmt/pull/2218>`_, + `#2220 <https://github.com/fmtlib/fmt/pull/2220>`_, + `#2228 <https://github.com/fmtlib/fmt/issues/2228>`_, + `#2229 <https://github.com/fmtlib/fmt/pull/2229>`_, + `#2230 <https://github.com/fmtlib/fmt/pull/2230>`_, + `#2233 <https://github.com/fmtlib/fmt/issues/2233>`_, + `#2239 <https://github.com/fmtlib/fmt/pull/2239>`_, + `#2248 <https://github.com/fmtlib/fmt/issues/2248>`_, + `#2252 <https://github.com/fmtlib/fmt/issues/2252>`_, + `#2253 <https://github.com/fmtlib/fmt/pull/2253>`_, + `#2255 <https://github.com/fmtlib/fmt/pull/2255>`_, + `#2261 <https://github.com/fmtlib/fmt/issues/2261>`_, + `#2278 <https://github.com/fmtlib/fmt/issues/2278>`_, + `#2284 <https://github.com/fmtlib/fmt/issues/2284>`_, + `#2287 <https://github.com/fmtlib/fmt/pull/2287>`_, + `#2289 <https://github.com/fmtlib/fmt/pull/2289>`_, + `#2290 <https://github.com/fmtlib/fmt/pull/2290>`_, + `#2293 <https://github.com/fmtlib/fmt/pull/2293>`_, + `#2295 <https://github.com/fmtlib/fmt/issues/2295>`_, + `#2296 <https://github.com/fmtlib/fmt/pull/2296>`_, + `#2297 <https://github.com/fmtlib/fmt/pull/2297>`_, + `#2311 <https://github.com/fmtlib/fmt/issues/2311>`_, + `#2313 <https://github.com/fmtlib/fmt/pull/2313>`_, + `#2315 <https://github.com/fmtlib/fmt/pull/2315>`_, + `#2320 <https://github.com/fmtlib/fmt/issues/2320>`_, + `#2321 <https://github.com/fmtlib/fmt/pull/2321>`_, + `#2323 <https://github.com/fmtlib/fmt/pull/2323>`_, + `#2328 <https://github.com/fmtlib/fmt/issues/2328>`_, + `#2329 <https://github.com/fmtlib/fmt/pull/2329>`_, + `#2333 <https://github.com/fmtlib/fmt/pull/2333>`_, + `#2338 <https://github.com/fmtlib/fmt/pull/2338>`_, + `#2341 <https://github.com/fmtlib/fmt/pull/2341>`_). + Thanks `@darklukee <https://github.com/darklukee>`_, + `@fagg (Ashton Fagg) <https://github.com/fagg>`_, + `@killerbot242 (Lieven de Cock) <https://github.com/killerbot242>`_, + `@jgopel (Jonathan Gopel) <https://github.com/jgopel>`_, + `@yeswalrus (Walter Gray) <https://github.com/yeswalrus>`_, + `@Finkman <https://github.com/Finkman>`_, + `@HazardyKnusperkeks (Björn Schäpers) <https://github.com/HazardyKnusperkeks>`_, + `@dkavolis (Daumantas Kavolis) <https://github.com/dkavolis>`_, + `@concatime (Issam Maghni) <https://github.com/concatime>`_, + `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_, + `@summivox (Yin Zhong) <https://github.com/summivox>`_, + `@yNeo <https://github.com/yNeo>`_, + `@Apache-HB (Elliot) <https://github.com/Apache-HB>`_, + `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_, + `@toojays (John Steele Scott) <https://github.com/toojays>`_, + `@Brainy0207 <https://github.com/Brainy0207>`_, + `@vadz (VZ) <https://github.com/vadz>`_, + `@imsherlock (Ryan Sherlock) <https://github.com/imsherlock>`_, + `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_, + `@white238 (Chris White) <https://github.com/white238>`_, + `@yafshar (Yaser Afshar) <https://github.com/yafshar>`_, + `@BillyDonahue (Billy Donahue) <https://github.com/BillyDonahue>`_, + `@jstaahl <https://github.com/jstaahl>`_, + `@denchat <https://github.com/denchat>`_, + `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_, + `@ilyakurdyukov (Ilya Kurdyukov) <https://github.com/ilyakurdyukov>`_, + `@ilmai <https://github.com/ilmai>`_, + `@JessyDL (Jessy De Lannoit) <https://github.com/JessyDL>`_, + `@sergiud (Sergiu Deitsch) <https://github.com/sergiud>`_, + `@mwinterb <https://github.com/mwinterb>`_, + `@sven-herrmann <https://github.com/sven-herrmann>`_, + `@jmelas (John Melas) <https://github.com/jmelas>`_, + `@twoixter (Jose Miguel Pérez) <https://github.com/twoixter>`_, + `@crbrz <https://github.com/crbrz>`_, + `@upsj (Tobias Ribizel) <https://github.com/upsj>`_. + +* Improved documentation + (`#1986 <https://github.com/fmtlib/fmt/issues/1986>`_, + `#2051 <https://github.com/fmtlib/fmt/pull/2051>`_, + `#2057 <https://github.com/fmtlib/fmt/issues/2057>`_, + `#2081 <https://github.com/fmtlib/fmt/pull/2081>`_, + `#2084 <https://github.com/fmtlib/fmt/issues/2084>`_, + `#2312 <https://github.com/fmtlib/fmt/pull/2312>`_). + Thanks `@imba-tjd (谭九鼎) <https://github.com/imba-tjd>`_, + `@0x416c69 (AlιAѕѕaѕѕιN) <https://github.com/0x416c69>`_, + `@mordante <https://github.com/mordante>`_. + +* Continuous integration and test improvements + (`#1969 <https://github.com/fmtlib/fmt/issues/1969>`_, + `#1991 <https://github.com/fmtlib/fmt/pull/1991>`_, + `#2020 <https://github.com/fmtlib/fmt/pull/2020>`_, + `#2110 <https://github.com/fmtlib/fmt/pull/2110>`_, + `#2114 <https://github.com/fmtlib/fmt/pull/2114>`_, + `#2196 <https://github.com/fmtlib/fmt/issues/2196>`_, + `#2217 <https://github.com/fmtlib/fmt/pull/2217>`_, + `#2247 <https://github.com/fmtlib/fmt/pull/2247>`_, + `#2256 <https://github.com/fmtlib/fmt/pull/2256>`_, + `#2336 <https://github.com/fmtlib/fmt/pull/2336>`_, + `#2346 <https://github.com/fmtlib/fmt/pull/2346>`_). + Thanks `@jgopel (Jonathan Gopel) <https://github.com/jgopel>`_, + `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_ and + `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. + +7.1.3 - 2020-11-24 +------------------ + +* Fixed handling of buffer boundaries in ``format_to_n`` + (`#1996 <https://github.com/fmtlib/fmt/issues/1996>`_, + `#2029 <https://github.com/fmtlib/fmt/issues/2029>`_). + +* Fixed linkage errors when linking with a shared library + (`#2011 <https://github.com/fmtlib/fmt/issues/2011>`_). + +* Reintroduced ostream support to range formatters + (`#2014 <https://github.com/fmtlib/fmt/issues/2014>`_). + +* Worked around an issue with mixing std versions in gcc + (`#2017 <https://github.com/fmtlib/fmt/issues/2017>`_). + +7.1.2 - 2020-11-04 +------------------ + +* Fixed floating point formatting with large precision + (`#1976 <https://github.com/fmtlib/fmt/issues/1976>`_). + +7.1.1 - 2020-11-01 +------------------ + +* Fixed ABI compatibility with 7.0.x + (`#1961 <https://github.com/fmtlib/fmt/issues/1961>`_). + +* Added the ``FMT_ARM_ABI_COMPATIBILITY`` macro to work around ABI + incompatibility between GCC and Clang on ARM + (`#1919 <https://github.com/fmtlib/fmt/issues/1919>`_). + +* Worked around a SFINAE bug in GCC 8 + (`#1957 <https://github.com/fmtlib/fmt/issues/1957>`_). + +* Fixed linkage errors when building with GCC's LTO + (`#1955 <https://github.com/fmtlib/fmt/issues/1955>`_). + +* Fixed a compilation error when building without ``__builtin_clz`` or equivalent + (`#1968 <https://github.com/fmtlib/fmt/pull/1968>`_). + Thanks `@tohammer (Tobias Hammer) <https://github.com/tohammer>`_. + +* Fixed a sign conversion warning + (`#1964 <https://github.com/fmtlib/fmt/pull/1964>`_). + Thanks `@OptoCloud <https://github.com/OptoCloud>`_. + +7.1.0 - 2020-10-25 +------------------ + +* Switched from `Grisu3 + <https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf>`_ + to `Dragonbox <https://github.com/jk-jeon/dragonbox>`_ for the default + floating-point formatting which gives the shortest decimal representation + with round-trip guarantee and correct rounding + (`#1882 <https://github.com/fmtlib/fmt/pull/1882>`_, + `#1887 <https://github.com/fmtlib/fmt/pull/1887>`_, + `#1894 <https://github.com/fmtlib/fmt/pull/1894>`_). This makes {fmt} up to + 20-30x faster than common implementations of ``std::ostringstream`` and + ``sprintf`` on `dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_ + and faster than double-conversion and Ryū: + + .. image:: https://user-images.githubusercontent.com/576385/ + 95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png + + It is possible to get even better performance at the cost of larger binary + size by compiling with the ``FMT_USE_FULL_CACHE_DRAGONBOX`` macro set to 1. + + Thanks `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_. + +* Added an experimental unsynchronized file output API which, together with + `format string compilation <https://fmt.dev/latest/api.html#compile-api>`_, + can give `5-9 times speed up compared to fprintf + <https://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html>`_ + on common platforms (`godbolt <https://godbolt.org/z/nsTcG8>`__): + + .. code:: c++ + + #include <fmt/os.h> + + int main() { + auto f = fmt::output_file("guide"); + f.print("The answer is {}.", 42); + } + +* Added a formatter for ``std::chrono::time_point<system_clock>`` + (`#1819 <https://github.com/fmtlib/fmt/issues/1819>`_, + `#1837 <https://github.com/fmtlib/fmt/pull/1837>`_). For example + (`godbolt <https://godbolt.org/z/c4M6fh>`__): + + .. code:: c++ + + #include <fmt/chrono.h> + + int main() { + auto now = std::chrono::system_clock::now(); + fmt::print("The time is {:%H:%M:%S}.\n", now); + } + + Thanks `@adamburgess (Adam Burgess) <https://github.com/adamburgess>`_. + +* Added support for ranges with non-const ``begin``/``end`` to ``fmt::join`` + (`#1784 <https://github.com/fmtlib/fmt/issues/1784>`_, + `#1786 <https://github.com/fmtlib/fmt/pull/1786>`_). For example + (`godbolt <https://godbolt.org/z/jP63Tv>`__): + + .. code:: c++ + + #include <fmt/ranges.h> + #include <range/v3/view/filter.hpp> + + int main() { + using std::literals::string_literals::operator""s; + auto strs = std::array{"a"s, "bb"s, "ccc"s}; + auto range = strs | ranges::views::filter( + [] (const std::string &x) { return x.size() != 2; } + ); + fmt::print("{}\n", fmt::join(range, "")); + } + + prints "accc". + + Thanks `@tonyelewis (Tony E Lewis) <https://github.com/tonyelewis>`_. + +* Added a ``memory_buffer::append`` overload that takes a range + (`#1806 <https://github.com/fmtlib/fmt/pull/1806>`_). + Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_. + +* Improved handling of single code units in ``FMT_COMPILE``. For example: + + .. code:: c++ + + #include <fmt/compile.h> + + char* f(char* buf) { + return fmt::format_to(buf, FMT_COMPILE("x{}"), 42); + } + + compiles to just (`godbolt <https://godbolt.org/z/5vncz3>`__): + + .. code:: asm + + _Z1fPc: + movb $120, (%rdi) + xorl %edx, %edx + cmpl $42, _ZN3fmt2v76detail10basic_dataIvE23zero_or_powers_of_10_32E+8(%rip) + movl $3, %eax + seta %dl + subl %edx, %eax + movzwl _ZN3fmt2v76detail10basic_dataIvE6digitsE+84(%rip), %edx + cltq + addq %rdi, %rax + movw %dx, -2(%rax) + ret + + Here a single ``mov`` instruction writes ``'x'`` (``$120``) to the output + buffer. + +* Added dynamic width support to format string compilation + (`#1809 <https://github.com/fmtlib/fmt/issues/1809>`_). + +* Improved error reporting for unformattable types: now you'll get the type name + directly in the error message instead of the note: + + .. code:: c++ + + #include <fmt/core.h> + + struct how_about_no {}; + + int main() { + fmt::print("{}", how_about_no()); + } + + Error (`godbolt <https://godbolt.org/z/GoxM4e>`__): + + ``fmt/core.h:1438:3: error: static_assert failed due to requirement + 'fmt::v7::formattable<how_about_no>()' "Cannot format an argument. + To make type T formattable provide a formatter<T> specialization: + https://fmt.dev/latest/api.html#udt" + ...`` + +* Added the `make_args_checked <https://fmt.dev/7.1.0/api.html#argument-lists>`_ + function template that allows you to write formatting functions with + compile-time format string checks and avoid binary code bloat + (`godbolt <https://godbolt.org/z/PEf9qr>`__): + + .. code:: c++ + + void vlog(const char* file, int line, fmt::string_view format, + fmt::format_args args) { + fmt::print("{}: {}: ", file, line); + fmt::vprint(format, args); + } + + template <typename S, typename... Args> + void log(const char* file, int line, const S& format, Args&&... args) { + vlog(file, line, format, + fmt::make_args_checked<Args...>(format, args...)); + } + + #define MY_LOG(format, ...) \ + log(__FILE__, __LINE__, FMT_STRING(format), __VA_ARGS__) + + MY_LOG("invalid squishiness: {}", 42); + +* Replaced ``snprintf`` fallback with a faster internal IEEE 754 ``float`` and + ``double`` formatter for arbitrary precision. For example + (`godbolt <https://godbolt.org/z/dPhWvj>`__): + + .. code:: c++ + + #include <fmt/core.h> + + int main() { + fmt::print("{:.500}\n", 4.9406564584124654E-324); + } + + prints + + ``4.9406564584124654417656879286822137236505980261432476442558568250067550727020875186529983636163599237979656469544571773092665671035593979639877479601078187812630071319031140452784581716784898210368871863605699873072305000638740915356498438731247339727316961514003171538539807412623856559117102665855668676818703956031062493194527159149245532930545654440112748012970999954193198940908041656332452475714786901472678015935523861155013480352649347201937902681071074917033322268447533357208324319360923829e-324``. + +* Made ``format_to_n`` and ``formatted_size`` part of the `core API + <https://fmt.dev/latest/api.html#core-api>`__ + (`godbolt <https://godbolt.org/z/sPjY1K>`__): + + .. code:: c++ + + #include <fmt/core.h> + + int main() { + char buffer[10]; + auto result = fmt::format_to_n(buffer, sizeof(buffer), "{}", 42); + } + +* Added ``fmt::format_to_n`` overload with format string compilation + (`#1764 <https://github.com/fmtlib/fmt/issues/1764>`_, + `#1767 <https://github.com/fmtlib/fmt/pull/1767>`_, + `#1869 <https://github.com/fmtlib/fmt/pull/1869>`_). For example + (`godbolt <https://godbolt.org/z/93h86q>`__): + + .. code:: c++ + + #include <fmt/compile.h> + + int main() { + char buffer[8]; + fmt::format_to_n(buffer, sizeof(buffer), FMT_COMPILE("{}"), 42); + } + + Thanks `@Kurkin (Dmitry Kurkin) <https://github.com/Kurkin>`_, + `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_. + +* Added ``fmt::format_to`` overload that take ``text_style`` + (`#1593 <https://github.com/fmtlib/fmt/issues/1593>`_, + `#1842 <https://github.com/fmtlib/fmt/issues/1842>`_, + `#1843 <https://github.com/fmtlib/fmt/pull/1843>`_). For example + (`godbolt <https://godbolt.org/z/91153r>`__): + + .. code:: c++ + + #include <fmt/color.h> + + int main() { + std::string out; + fmt::format_to(std::back_inserter(out), + fmt::emphasis::bold | fg(fmt::color::red), + "The answer is {}.", 42); + } + + Thanks `@Naios (Denis Blank) <https://github.com/Naios>`_. + +* Made the ``'#'`` specifier emit trailing zeros in addition to the decimal + point (`#1797 <https://github.com/fmtlib/fmt/issues/1797>`_). For example + (`godbolt <https://godbolt.org/z/bhdcW9>`__): + + .. code:: c++ + + #include <fmt/core.h> + + int main() { + fmt::print("{:#.2g}", 0.5); + } + + prints ``0.50``. + +* Changed the default floating point format to not include ``.0`` for + consistency with ``std::format`` and ``std::to_chars`` + (`#1893 <https://github.com/fmtlib/fmt/issues/1893>`_, + `#1943 <https://github.com/fmtlib/fmt/issues/1943>`_). It is possible to get + the decimal point and trailing zero with the ``#`` specifier. + +* Fixed an issue with floating-point formatting that could result in addition of + a non-significant trailing zero in rare cases e.g. ``1.00e-34`` instead of + ``1.0e-34`` (`#1873 <https://github.com/fmtlib/fmt/issues/1873>`_, + `#1917 <https://github.com/fmtlib/fmt/issues/1917>`_). + +* Made ``fmt::to_string`` fallback on ``ostream`` insertion operator if + the ``formatter`` specialization is not provided + (`#1815 <https://github.com/fmtlib/fmt/issues/1815>`_, + `#1829 <https://github.com/fmtlib/fmt/pull/1829>`_). + Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_. + +* Added support for the append mode to the experimental file API and + improved ``fcntl.h`` detection. + (`#1847 <https://github.com/fmtlib/fmt/pull/1847>`_, + `#1848 <https://github.com/fmtlib/fmt/pull/1848>`_). + Thanks `@t-wiser <https://github.com/t-wiser>`_. + +* Fixed handling of types that have both an implicit conversion operator and + an overloaded ``ostream`` insertion operator + (`#1766 <https://github.com/fmtlib/fmt/issues/1766>`_). + +* Fixed a slicing issue in an internal iterator type + (`#1822 <https://github.com/fmtlib/fmt/pull/1822>`_). + Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_. + +* Fixed an issue in locale-specific integer formatting + (`#1927 <https://github.com/fmtlib/fmt/issues/1927>`_). + +* Fixed handling of exotic code unit types + (`#1870 <https://github.com/fmtlib/fmt/issues/1870>`_, + `#1932 <https://github.com/fmtlib/fmt/issues/1932>`_). + +* Improved ``FMT_ALWAYS_INLINE`` + (`#1878 <https://github.com/fmtlib/fmt/pull/1878>`_). + Thanks `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_. + +* Removed dependency on ``windows.h`` + (`#1900 <https://github.com/fmtlib/fmt/pull/1900>`_). + Thanks `@bernd5 (Bernd Baumanns) <https://github.com/bernd5>`_. + +* Optimized counting of decimal digits on MSVC + (`#1890 <https://github.com/fmtlib/fmt/pull/1890>`_). + Thanks `@mwinterb <https://github.com/mwinterb>`_. + +* Improved documentation + (`#1772 <https://github.com/fmtlib/fmt/issues/1772>`_, + `#1775 <https://github.com/fmtlib/fmt/pull/1775>`_, + `#1792 <https://github.com/fmtlib/fmt/pull/1792>`_, + `#1838 <https://github.com/fmtlib/fmt/pull/1838>`_, + `#1888 <https://github.com/fmtlib/fmt/pull/1888>`_, + `#1918 <https://github.com/fmtlib/fmt/pull/1918>`_, + `#1939 <https://github.com/fmtlib/fmt/pull/1939>`_). + Thanks `@leolchat (Léonard Gérard) <https://github.com/leolchat>`_, + `@pepsiman (Malcolm Parsons) <https://github.com/pepsiman>`_, + `@Klaim (Joël Lamotte) <https://github.com/Klaim>`_, + `@ravijanjam (Ravi J) <https://github.com/ravijanjam>`_, + `@francesco-st <https://github.com/francesco-st>`_, + `@udnaan (Adnan) <https://github.com/udnaan>`_. + +* Added the ``FMT_REDUCE_INT_INSTANTIATIONS`` CMake option that reduces the + binary code size at the cost of some integer formatting performance. This can + be useful for extremely memory-constrained embedded systems + (`#1778 <https://github.com/fmtlib/fmt/issues/1778>`_, + `#1781 <https://github.com/fmtlib/fmt/pull/1781>`_). + Thanks `@kammce (Khalil Estell) <https://github.com/kammce>`_. + +* Added the ``FMT_USE_INLINE_NAMESPACES`` macro to control usage of inline + namespaces (`#1945 <https://github.com/fmtlib/fmt/pull/1945>`_). + Thanks `@darklukee <https://github.com/darklukee>`_. + +* Improved build configuration + (`#1760 <https://github.com/fmtlib/fmt/pull/1760>`_, + `#1770 <https://github.com/fmtlib/fmt/pull/1770>`_, + `#1779 <https://github.com/fmtlib/fmt/issues/1779>`_, + `#1783 <https://github.com/fmtlib/fmt/pull/1783>`_, + `#1823 <https://github.com/fmtlib/fmt/pull/1823>`_). + Thanks `@dvetutnev (Dmitriy Vetutnev) <https://github.com/dvetutnev>`_, + `@xvitaly (Vitaly Zaitsev) <https://github.com/xvitaly>`_, + `@tambry (Raul Tambre) <https://github.com/tambry>`_, + `@medithe <https://github.com/medithe>`_, + `@martinwuehrer (Martin Wührer) <https://github.com/martinwuehrer>`_. + +* Fixed various warnings and compilation issues + (`#1790 <https://github.com/fmtlib/fmt/pull/1790>`_, + `#1802 <https://github.com/fmtlib/fmt/pull/1802>`_, + `#1808 <https://github.com/fmtlib/fmt/pull/1808>`_, + `#1810 <https://github.com/fmtlib/fmt/issues/1810>`_, + `#1811 <https://github.com/fmtlib/fmt/issues/1811>`_, + `#1812 <https://github.com/fmtlib/fmt/pull/1812>`_, + `#1814 <https://github.com/fmtlib/fmt/pull/1814>`_, + `#1816 <https://github.com/fmtlib/fmt/pull/1816>`_, + `#1817 <https://github.com/fmtlib/fmt/pull/1817>`_, + `#1818 <https://github.com/fmtlib/fmt/pull/1818>`_, + `#1825 <https://github.com/fmtlib/fmt/issues/1825>`_, + `#1836 <https://github.com/fmtlib/fmt/pull/1836>`_, + `#1855 <https://github.com/fmtlib/fmt/pull/1855>`_, + `#1856 <https://github.com/fmtlib/fmt/pull/1856>`_, + `#1860 <https://github.com/fmtlib/fmt/pull/1860>`_, + `#1877 <https://github.com/fmtlib/fmt/pull/1877>`_, + `#1879 <https://github.com/fmtlib/fmt/pull/1879>`_, + `#1880 <https://github.com/fmtlib/fmt/pull/1880>`_, + `#1896 <https://github.com/fmtlib/fmt/issues/1896>`_, + `#1897 <https://github.com/fmtlib/fmt/pull/1897>`_, + `#1898 <https://github.com/fmtlib/fmt/pull/1898>`_, + `#1904 <https://github.com/fmtlib/fmt/issues/1904>`_, + `#1908 <https://github.com/fmtlib/fmt/pull/1908>`_, + `#1911 <https://github.com/fmtlib/fmt/issues/1911>`_, + `#1912 <https://github.com/fmtlib/fmt/issues/1912>`_, + `#1928 <https://github.com/fmtlib/fmt/issues/1928>`_, + `#1929 <https://github.com/fmtlib/fmt/pull/1929>`_, + `#1935 <https://github.com/fmtlib/fmt/issues/1935>`_, + `#1937 <https://github.com/fmtlib/fmt/pull/1937>`_, + `#1942 <https://github.com/fmtlib/fmt/pull/1942>`_, + `#1949 <https://github.com/fmtlib/fmt/issues/1949>`_). + Thanks `@TheQwertiest <https://github.com/TheQwertiest>`_, + `@medithe <https://github.com/medithe>`_, + `@martinwuehrer (Martin Wührer) <https://github.com/martinwuehrer>`_, + `@n16h7hunt3r <https://github.com/n16h7hunt3r>`_, + `@Othereum (Seokjin Lee) <https://github.com/Othereum>`_, + `@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_, + `@AlexanderLanin (Alexander Lanin) <https://github.com/AlexanderLanin>`_, + `@gcerretani (Giovanni Cerretani) <https://github.com/gcerretani>`_, + `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_, + `@noizefloor (Jan Schwers) <https://github.com/noizefloor>`_, + `@akohlmey (Axel Kohlmeyer) <https://github.com/akohlmey>`_, + `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_, + `@rimathia <https://github.com/rimathia>`_, + `@rglarix (Riccardo Ghetta (larix)) <https://github.com/rglarix>`_, + `@moiwi <https://github.com/moiwi>`_, + `@heckad (Kazantcev Andrey) <https://github.com/heckad>`_, + `@MarcDirven <https://github.com/MarcDirven>`_. + `@BartSiwek (Bart Siwek) <https://github.com/BartSiwek>`_, + `@darklukee <https://github.com/darklukee>`_. + +7.0.3 - 2020-08-06 +------------------ + +* Worked around broken ``numeric_limits`` for 128-bit integers + (`#1787 <https://github.com/fmtlib/fmt/issues/1787>`_). + +* Added error reporting on missing named arguments + (`#1796 <https://github.com/fmtlib/fmt/issues/1796>`_). + +* Stopped using 128-bit integers with clang-cl + (`#1800 <https://github.com/fmtlib/fmt/pull/1800>`_). + Thanks `@Kingcom <https://github.com/Kingcom>`_. + +* Fixed issues in locale-specific integer formatting + (`#1782 <https://github.com/fmtlib/fmt/issues/1782>`_, + `#1801 <https://github.com/fmtlib/fmt/issues/1801>`_). + +7.0.2 - 2020-07-29 +------------------ + +* Worked around broken ``numeric_limits`` for 128-bit integers + (`#1725 <https://github.com/fmtlib/fmt/issues/1725>`_). + +* Fixed compatibility with CMake 3.4 + (`#1779 <https://github.com/fmtlib/fmt/issues/1779>`_). + +* Fixed handling of digit separators in locale-specific formatting + (`#1782 <https://github.com/fmtlib/fmt/issues/1782>`_). + +7.0.1 - 2020-07-07 +------------------ + +* Updated the inline version namespace name. + +* Worked around a gcc bug in mangling of alias templates + (`#1753 <https://github.com/fmtlib/fmt/issues/1753>`_). + +* Fixed a linkage error on Windows + (`#1757 <https://github.com/fmtlib/fmt/issues/1757>`_). + Thanks `@Kurkin (Dmitry Kurkin) <https://github.com/Kurkin>`_. + +* Fixed minor issues with the documentation. + +7.0.0 - 2020-07-05 +------------------ + +* Reduced the library size. For example, on macOS a stripped test binary + statically linked with {fmt} `shrank from ~368k to less than 100k + <http://www.zverovich.net/2020/05/21/reducing-library-size.html>`_. + +* Added a simpler and more efficient `format string compilation API + <https://fmt.dev/7.0.0/api.html#compile-api>`_: + + .. code:: c++ + + #include <fmt/compile.h> + + // Converts 42 into std::string using the most efficient method and no + // runtime format string processing. + std::string s = fmt::format(FMT_COMPILE("{}"), 42); + + The old ``fmt::compile`` API is now deprecated. + +* Optimized integer formatting: ``format_to`` with format string compilation + and a stack-allocated buffer is now `faster than to_chars on both + libc++ and libstdc++ + <http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_. + +* Optimized handling of small format strings. For example, + + .. code:: c++ + + fmt::format("Result: {}: ({},{},{},{})", str1, str2, str3, str4, str5) + + is now ~40% faster (`#1685 <https://github.com/fmtlib/fmt/issues/1685>`_). + +* Applied extern templates to improve compile times when using the core API + and ``fmt/format.h`` (`#1452 <https://github.com/fmtlib/fmt/issues/1452>`_). + For example, on macOS with clang the compile time of a test translation unit + dropped from 2.3s to 0.3s with ``-O2`` and from 0.6s to 0.3s with the default + settings (``-O0``). + + Before (``-O2``):: + + % time c++ -c test.cc -I include -std=c++17 -O2 + c++ -c test.cc -I include -std=c++17 -O2 2.22s user 0.08s system 99% cpu 2.311 total + + After (``-O2``):: + + % time c++ -c test.cc -I include -std=c++17 -O2 + c++ -c test.cc -I include -std=c++17 -O2 0.26s user 0.04s system 98% cpu 0.303 total + + Before (default):: + + % time c++ -c test.cc -I include -std=c++17 + c++ -c test.cc -I include -std=c++17 0.53s user 0.06s system 98% cpu 0.601 total + + After (default):: + + % time c++ -c test.cc -I include -std=c++17 + c++ -c test.cc -I include -std=c++17 0.24s user 0.06s system 98% cpu 0.301 total + + It is still recommended to use ``fmt/core.h`` instead of ``fmt/format.h`` but + the compile time difference is now smaller. Thanks + `@alex3d <https://github.com/alex3d>`_ for the suggestion. + +* Named arguments are now stored on stack (no dynamic memory allocations) and + the compiled code is more compact and efficient. For example + + .. code:: c++ + + #include <fmt/core.h> + + int main() { + fmt::print("The answer is {answer}\n", fmt::arg("answer", 42)); + } + + compiles to just (`godbolt <https://godbolt.org/z/NcfEp_>`__) + + .. code:: asm + + .LC0: + .string "answer" + .LC1: + .string "The answer is {answer}\n" + main: + sub rsp, 56 + mov edi, OFFSET FLAT:.LC1 + mov esi, 23 + movabs rdx, 4611686018427387905 + lea rax, [rsp+32] + lea rcx, [rsp+16] + mov QWORD PTR [rsp+8], 1 + mov QWORD PTR [rsp], rax + mov DWORD PTR [rsp+16], 42 + mov QWORD PTR [rsp+32], OFFSET FLAT:.LC0 + mov DWORD PTR [rsp+40], 0 + call fmt::v6::vprint(fmt::v6::basic_string_view<char>, + fmt::v6::format_args) + xor eax, eax + add rsp, 56 + ret + + .L.str.1: + .asciz "answer" + +* Implemented compile-time checks for dynamic width and precision + (`#1614 <https://github.com/fmtlib/fmt/issues/1614>`_): + + .. code:: c++ + + #include <fmt/format.h> + + int main() { + fmt::print(FMT_STRING("{0:{1}}"), 42); + } + + now gives a compilation error because argument 1 doesn't exist:: + + In file included from test.cc:1: + include/fmt/format.h:2726:27: error: constexpr variable 'invalid_format' must be + initialized by a constant expression + FMT_CONSTEXPR_DECL bool invalid_format = + ^ + ... + include/fmt/core.h:569:26: note: in call to + '&checker(s, {}).context_->on_error(&"argument not found"[0])' + if (id >= num_args_) on_error("argument not found"); + ^ + +* Added sentinel support to ``fmt::join`` + (`#1689 <https://github.com/fmtlib/fmt/pull/1689>`_) + + .. code:: c++ + + struct zstring_sentinel {}; + bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; } + bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; } + + struct zstring { + const char* p; + const char* begin() const { return p; } + zstring_sentinel end() const { return {}; } + }; + + auto s = fmt::format("{}", fmt::join(zstring{"hello"}, "_")); + // s == "h_e_l_l_o" + + Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_. + +* Added support for named arguments, ``clear`` and ``reserve`` to + ``dynamic_format_arg_store`` + (`#1655 <https://github.com/fmtlib/fmt/issues/1655>`_, + `#1663 <https://github.com/fmtlib/fmt/pull/1663>`_, + `#1674 <https://github.com/fmtlib/fmt/pull/1674>`_, + `#1677 <https://github.com/fmtlib/fmt/pull/1677>`_). + Thanks `@vsolontsov-ll (Vladimir Solontsov) + <https://github.com/vsolontsov-ll>`_. + +* Added support for the ``'c'`` format specifier to integral types for + compatibility with ``std::format`` + (`#1652 <https://github.com/fmtlib/fmt/issues/1652>`_). + +* Replaced the ``'n'`` format specifier with ``'L'`` for compatibility with + ``std::format`` (`#1624 <https://github.com/fmtlib/fmt/issues/1624>`_). + The ``'n'`` specifier can be enabled via the ``FMT_DEPRECATED_N_SPECIFIER`` + macro. + +* The ``'='`` format specifier is now disabled by default for compatibility with + ``std::format``. It can be enabled via the ``FMT_DEPRECATED_NUMERIC_ALIGN`` + macro. + +* Removed the following deprecated APIs: + + * ``FMT_STRING_ALIAS`` and ``fmt`` macros - replaced by ``FMT_STRING`` + * ``fmt::basic_string_view::char_type`` - replaced by + ``fmt::basic_string_view::value_type`` + * ``convert_to_int`` + * ``format_arg_store::types`` + * ``*parse_context`` - replaced by ``*format_parse_context`` + * ``FMT_DEPRECATED_INCLUDE_OS`` + * ``FMT_DEPRECATED_PERCENT`` - incompatible with ``std::format`` + * ``*writer`` - replaced by compiled format API + +* Renamed the ``internal`` namespace to ``detail`` + (`#1538 <https://github.com/fmtlib/fmt/issues/1538>`_). The former is still + provided as an alias if the ``FMT_USE_INTERNAL`` macro is defined. + +* Improved compatibility between ``fmt::printf`` with the standard specs + (`#1595 <https://github.com/fmtlib/fmt/issues/1595>`_, + `#1682 <https://github.com/fmtlib/fmt/pull/1682>`_, + `#1683 <https://github.com/fmtlib/fmt/pull/1683>`_, + `#1687 <https://github.com/fmtlib/fmt/pull/1687>`_, + `#1699 <https://github.com/fmtlib/fmt/pull/1699>`_). + Thanks `@rimathia <https://github.com/rimathia>`_. + +* Fixed handling of ``operator<<`` overloads that use ``copyfmt`` + (`#1666 <https://github.com/fmtlib/fmt/issues/1666>`_). + +* Added the ``FMT_OS`` CMake option to control inclusion of OS-specific APIs + in the fmt target. This can be useful for embedded platforms + (`#1654 <https://github.com/fmtlib/fmt/issues/1654>`_, + `#1656 <https://github.com/fmtlib/fmt/pull/1656>`_). + Thanks `@kwesolowski (Krzysztof Wesolowski) + <https://github.com/kwesolowski>`_. + +* Replaced ``FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION`` with the ``FMT_FUZZ`` + macro to prevent interferring with fuzzing of projects using {fmt} + (`#1650 <https://github.com/fmtlib/fmt/pull/1650>`_). + Thanks `@asraa (Asra Ali) <https://github.com/asraa>`_. + +* Fixed compatibility with emscripten + (`#1636 <https://github.com/fmtlib/fmt/issues/1636>`_, + `#1637 <https://github.com/fmtlib/fmt/pull/1637>`_). + Thanks `@ArthurSonzogni (Arthur Sonzogni) + <https://github.com/ArthurSonzogni>`_. + +* Improved documentation + (`#704 <https://github.com/fmtlib/fmt/issues/704>`_, + `#1643 <https://github.com/fmtlib/fmt/pull/1643>`_, + `#1660 <https://github.com/fmtlib/fmt/pull/1660>`_, + `#1681 <https://github.com/fmtlib/fmt/pull/1681>`_, + `#1691 <https://github.com/fmtlib/fmt/pull/1691>`_, + `#1706 <https://github.com/fmtlib/fmt/pull/1706>`_, + `#1714 <https://github.com/fmtlib/fmt/pull/1714>`_, + `#1721 <https://github.com/fmtlib/fmt/pull/1721>`_, + `#1739 <https://github.com/fmtlib/fmt/pull/1739>`_, + `#1740 <https://github.com/fmtlib/fmt/pull/1740>`_, + `#1741 <https://github.com/fmtlib/fmt/pull/1741>`_, + `#1751 <https://github.com/fmtlib/fmt/pull/1751>`_). + Thanks `@senior7515 (Alexander Gallego) <https://github.com/senior7515>`_, + `@lsr0 (Lindsay Roberts) <https://github.com/lsr0>`_, + `@puetzk (Kevin Puetz) <https://github.com/puetzk>`_, + `@fpelliccioni (Fernando Pelliccioni) <https://github.com/fpelliccioni>`_, + Alexey Kuzmenko, `@jelly (jelle van der Waa) <https://github.com/jelly>`_, + `@claremacrae (Clare Macrae) <https://github.com/claremacrae>`_, + `@jiapengwen (文佳鹏) <https://github.com/jiapengwen>`_, + `@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_, + `@alexey-milovidov <https://github.com/alexey-milovidov>`_. + +* Implemented various build configuration fixes and improvements + (`#1603 <https://github.com/fmtlib/fmt/pull/1603>`_, + `#1657 <https://github.com/fmtlib/fmt/pull/1657>`_, + `#1702 <https://github.com/fmtlib/fmt/pull/1702>`_, + `#1728 <https://github.com/fmtlib/fmt/pull/1728>`_). + Thanks `@scramsby (Scott Ramsby) <https://github.com/scramsby>`_, + `@jtojnar (Jan Tojnar) <https://github.com/jtojnar>`_, + `@orivej (Orivej Desh) <https://github.com/orivej>`_, + `@flagarde <https://github.com/flagarde>`_. + +* Fixed various warnings and compilation issues + (`#1616 <https://github.com/fmtlib/fmt/pull/1616>`_, + `#1620 <https://github.com/fmtlib/fmt/issues/1620>`_, + `#1622 <https://github.com/fmtlib/fmt/issues/1622>`_, + `#1625 <https://github.com/fmtlib/fmt/issues/1625>`_, + `#1627 <https://github.com/fmtlib/fmt/pull/1627>`_, + `#1628 <https://github.com/fmtlib/fmt/issues/1628>`_, + `#1629 <https://github.com/fmtlib/fmt/pull/1629>`_, + `#1631 <https://github.com/fmtlib/fmt/issues/1631>`_, + `#1633 <https://github.com/fmtlib/fmt/pull/1633>`_, + `#1649 <https://github.com/fmtlib/fmt/pull/1649>`_, + `#1658 <https://github.com/fmtlib/fmt/issues/1658>`_, + `#1661 <https://github.com/fmtlib/fmt/pull/1661>`_, + `#1667 <https://github.com/fmtlib/fmt/pull/1667>`_, + `#1668 <https://github.com/fmtlib/fmt/issues/1668>`_, + `#1669 <https://github.com/fmtlib/fmt/pull/1669>`_, + `#1692 <https://github.com/fmtlib/fmt/issues/1692>`_, + `#1696 <https://github.com/fmtlib/fmt/pull/1696>`_, + `#1697 <https://github.com/fmtlib/fmt/pull/1697>`_, + `#1707 <https://github.com/fmtlib/fmt/issues/1707>`_, + `#1712 <https://github.com/fmtlib/fmt/pull/1712>`_, + `#1716 <https://github.com/fmtlib/fmt/pull/1716>`_, + `#1722 <https://github.com/fmtlib/fmt/pull/1722>`_, + `#1724 <https://github.com/fmtlib/fmt/issues/1724>`_, + `#1729 <https://github.com/fmtlib/fmt/pull/1729>`_, + `#1738 <https://github.com/fmtlib/fmt/pull/1738>`_, + `#1742 <https://github.com/fmtlib/fmt/issues/1742>`_, + `#1743 <https://github.com/fmtlib/fmt/issues/1743>`_, + `#1744 <https://github.com/fmtlib/fmt/pull/1744>`_, + `#1747 <https://github.com/fmtlib/fmt/issues/1747>`_, + `#1750 <https://github.com/fmtlib/fmt/pull/1750>`_). + Thanks `@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_, + `@gabime (Gabi Melman) <https://github.com/gabime>`_, + `@johnor (Johan) <https://github.com/johnor>`_, + `@Kurkin (Dmitry Kurkin) <https://github.com/Kurkin>`_, + `@invexed (James Beach) <https://github.com/invexed>`_, + `@peterbell10 <https://github.com/peterbell10>`_, + `@daixtrose (Markus Werle) <https://github.com/daixtrose>`_, + `@petrutlucian94 (Lucian Petrut) <https://github.com/petrutlucian94>`_, + `@Neargye (Daniil Goncharov) <https://github.com/Neargye>`_, + `@ambitslix (Attila M. Szilagyi) <https://github.com/ambitslix>`_, + `@gabime (Gabi Melman) <https://github.com/gabime>`_, + `@erthink (Leonid Yuriev) <https://github.com/erthink>`_, + `@tohammer (Tobias Hammer) <https://github.com/tohammer>`_, + `@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_. + +6.2.1 - 2020-05-09 +------------------ + +* Fixed ostream support in ``sprintf`` + (`#1631 <https://github.com/fmtlib/fmt/issues/1631>`_). + +* Fixed type detection when using implicit conversion to ``string_view`` and + ostream ``operator<<`` inconsistently + (`#1662 <https://github.com/fmtlib/fmt/issues/1662>`_). + +6.2.0 - 2020-04-05 +------------------ + +* Improved error reporting when trying to format an object of a non-formattable + type: + + .. code:: c++ + + fmt::format("{}", S()); + + now gives:: + + include/fmt/core.h:1015:5: error: static_assert failed due to requirement + 'formattable' "Cannot format argument. To make type T formattable provide a + formatter<T> specialization: + https://fmt.dev/latest/api.html#formatting-user-defined-types" + static_assert( + ^ + ... + note: in instantiation of function template specialization + 'fmt::v6::format<char [3], S, char>' requested here + fmt::format("{}", S()); + ^ + + if ``S`` is not formattable. + +* Reduced the library size by ~10%. + +* Always print decimal point if ``#`` is specified + (`#1476 <https://github.com/fmtlib/fmt/issues/1476>`_, + `#1498 <https://github.com/fmtlib/fmt/issues/1498>`_): + + .. code:: c++ + + fmt::print("{:#.0f}", 42.0); + + now prints ``42.`` + +* Implemented the ``'L'`` specifier for locale-specific numeric formatting to + improve compatibility with ``std::format``. The ``'n'`` specifier is now + deprecated and will be removed in the next major release. + +* Moved OS-specific APIs such as ``windows_error`` from ``fmt/format.h`` to + ``fmt/os.h``. You can define ``FMT_DEPRECATED_INCLUDE_OS`` to automatically + include ``fmt/os.h`` from ``fmt/format.h`` for compatibility but this will be + disabled in the next major release. + +* Added precision overflow detection in floating-point formatting. + +* Implemented detection of invalid use of ``fmt::arg``. + +* Used ``type_identity`` to block unnecessary template argument deduction. + Thanks Tim Song. + +* Improved UTF-8 handling + (`#1109 <https://github.com/fmtlib/fmt/issues/1109>`_): + + .. code:: c++ + + fmt::print("┌{0:─^{2}}┐\n" + "│{1: ^{2}}│\n" + "└{0:─^{2}}┘\n", "", "Привет, мир!", 20); + + now prints:: + + ┌────────────────────┐ + │ Привет, мир! │ + └────────────────────┘ + + on systems that support Unicode. + +* Added experimental dynamic argument storage + (`#1170 <https://github.com/fmtlib/fmt/issues/1170>`_, + `#1584 <https://github.com/fmtlib/fmt/pull/1584>`_): + + .. code:: c++ + + fmt::dynamic_format_arg_store<fmt::format_context> store; + store.push_back("answer"); + store.push_back(42); + fmt::vprint("The {} is {}.\n", store); + + prints:: + + The answer is 42. + + Thanks `@vsolontsov-ll (Vladimir Solontsov) + <https://github.com/vsolontsov-ll>`_. + +* Made ``fmt::join`` accept ``initializer_list`` + (`#1591 <https://github.com/fmtlib/fmt/pull/1591>`_). + Thanks `@Rapotkinnik (Nikolay Rapotkin) <https://github.com/Rapotkinnik>`_. + +* Fixed handling of empty tuples + (`#1588 <https://github.com/fmtlib/fmt/issues/1588>`_). + +* Fixed handling of output iterators in ``format_to_n`` + (`#1506 <https://github.com/fmtlib/fmt/issues/1506>`_). + +* Fixed formatting of ``std::chrono::duration`` types to wide output + (`#1533 <https://github.com/fmtlib/fmt/pull/1533>`_). + Thanks `@zeffy (pilao) <https://github.com/zeffy>`_. + +* Added const ``begin`` and ``end`` overload to buffers + (`#1553 <https://github.com/fmtlib/fmt/pull/1553>`_). + Thanks `@dominicpoeschko <https://github.com/dominicpoeschko>`_. + +* Added the ability to disable floating-point formatting via ``FMT_USE_FLOAT``, + ``FMT_USE_DOUBLE`` and ``FMT_USE_LONG_DOUBLE`` macros for extremely + memory-constrained embedded system + (`#1590 <https://github.com/fmtlib/fmt/pull/1590>`_). + Thanks `@albaguirre (Alberto Aguirre) <https://github.com/albaguirre>`_. + +* Made ``FMT_STRING`` work with ``constexpr`` ``string_view`` + (`#1589 <https://github.com/fmtlib/fmt/pull/1589>`_). + Thanks `@scramsby (Scott Ramsby) <https://github.com/scramsby>`_. + +* Implemented a minor optimization in the format string parser + (`#1560 <https://github.com/fmtlib/fmt/pull/1560>`_). + Thanks `@IkarusDeveloper <https://github.com/IkarusDeveloper>`_. + +* Improved attribute detection + (`#1469 <https://github.com/fmtlib/fmt/pull/1469>`_, + `#1475 <https://github.com/fmtlib/fmt/pull/1475>`_, + `#1576 <https://github.com/fmtlib/fmt/pull/1576>`_). + Thanks `@federico-busato (Federico) <https://github.com/federico-busato>`_, + `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_, + `@refnum <https://github.com/refnum>`_. + +* Improved documentation + (`#1481 <https://github.com/fmtlib/fmt/pull/1481>`_, + `#1523 <https://github.com/fmtlib/fmt/pull/1523>`_). + Thanks `@JackBoosY (Jack·Boos·Yu) <https://github.com/JackBoosY>`_, + `@imba-tjd (谭九鼎) <https://github.com/imba-tjd>`_. + +* Fixed symbol visibility on Linux when compiling with ``-fvisibility=hidden`` + (`#1535 <https://github.com/fmtlib/fmt/pull/1535>`_). + Thanks `@milianw (Milian Wolff) <https://github.com/milianw>`_. + +* Implemented various build configuration fixes and improvements + (`#1264 <https://github.com/fmtlib/fmt/issues/1264>`_, + `#1460 <https://github.com/fmtlib/fmt/issues/1460>`_, + `#1534 <https://github.com/fmtlib/fmt/pull/1534>`_, + `#1536 <https://github.com/fmtlib/fmt/issues/1536>`_, + `#1545 <https://github.com/fmtlib/fmt/issues/1545>`_, + `#1546 <https://github.com/fmtlib/fmt/pull/1546>`_, + `#1566 <https://github.com/fmtlib/fmt/issues/1566>`_, + `#1582 <https://github.com/fmtlib/fmt/pull/1582>`_, + `#1597 <https://github.com/fmtlib/fmt/issues/1597>`_, + `#1598 <https://github.com/fmtlib/fmt/pull/1598>`_). + Thanks `@ambitslix (Attila M. Szilagyi) <https://github.com/ambitslix>`_, + `@jwillikers (Jordan Williams) <https://github.com/jwillikers>`_, + `@stac47 (Laurent Stacul) <https://github.com/stac47>`_. + +* Fixed various warnings and compilation issues + (`#1433 <https://github.com/fmtlib/fmt/pull/1433>`_, + `#1461 <https://github.com/fmtlib/fmt/issues/1461>`_, + `#1470 <https://github.com/fmtlib/fmt/pull/1470>`_, + `#1480 <https://github.com/fmtlib/fmt/pull/1480>`_, + `#1485 <https://github.com/fmtlib/fmt/pull/1485>`_, + `#1492 <https://github.com/fmtlib/fmt/pull/1492>`_, + `#1493 <https://github.com/fmtlib/fmt/issues/1493>`_, + `#1504 <https://github.com/fmtlib/fmt/issues/1504>`_, + `#1505 <https://github.com/fmtlib/fmt/pull/1505>`_, + `#1512 <https://github.com/fmtlib/fmt/pull/1512>`_, + `#1515 <https://github.com/fmtlib/fmt/issues/1515>`_, + `#1516 <https://github.com/fmtlib/fmt/pull/1516>`_, + `#1518 <https://github.com/fmtlib/fmt/pull/1518>`_, + `#1519 <https://github.com/fmtlib/fmt/pull/1519>`_, + `#1520 <https://github.com/fmtlib/fmt/pull/1520>`_, + `#1521 <https://github.com/fmtlib/fmt/pull/1521>`_, + `#1522 <https://github.com/fmtlib/fmt/pull/1522>`_, + `#1524 <https://github.com/fmtlib/fmt/issues/1524>`_, + `#1530 <https://github.com/fmtlib/fmt/pull/1530>`_, + `#1531 <https://github.com/fmtlib/fmt/issues/1531>`_, + `#1532 <https://github.com/fmtlib/fmt/pull/1532>`_, + `#1539 <https://github.com/fmtlib/fmt/issues/1539>`_, + `#1547 <https://github.com/fmtlib/fmt/issues/1547>`_, + `#1548 <https://github.com/fmtlib/fmt/issues/1548>`_, + `#1554 <https://github.com/fmtlib/fmt/pull/1554>`_, + `#1567 <https://github.com/fmtlib/fmt/issues/1567>`_, + `#1568 <https://github.com/fmtlib/fmt/pull/1568>`_, + `#1569 <https://github.com/fmtlib/fmt/pull/1569>`_, + `#1571 <https://github.com/fmtlib/fmt/pull/1571>`_, + `#1573 <https://github.com/fmtlib/fmt/pull/1573>`_, + `#1575 <https://github.com/fmtlib/fmt/pull/1575>`_, + `#1581 <https://github.com/fmtlib/fmt/pull/1581>`_, + `#1583 <https://github.com/fmtlib/fmt/issues/1583>`_, + `#1586 <https://github.com/fmtlib/fmt/issues/1586>`_, + `#1587 <https://github.com/fmtlib/fmt/issues/1587>`_, + `#1594 <https://github.com/fmtlib/fmt/issues/1594>`_, + `#1596 <https://github.com/fmtlib/fmt/pull/1596>`_, + `#1604 <https://github.com/fmtlib/fmt/issues/1604>`_, + `#1606 <https://github.com/fmtlib/fmt/pull/1606>`_, + `#1607 <https://github.com/fmtlib/fmt/issues/1607>`_, + `#1609 <https://github.com/fmtlib/fmt/issues/1609>`_). + Thanks `@marti4d (Chris Martin) <https://github.com/marti4d>`_, + `@iPherian <https://github.com/iPherian>`_, + `@parkertomatoes <https://github.com/parkertomatoes>`_, + `@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_, + `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_, + `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_, + `@torsten48 <https://github.com/torsten48>`_, + `@tohammer (Tobias Hammer) <https://github.com/tohammer>`_, + `@lefticus (Jason Turner) <https://github.com/lefticus>`_, + `@ryusakki (Haise) <https://github.com/ryusakki>`_, + `@adnsv (Alex Denisov) <https://github.com/adnsv>`_, + `@fghzxm <https://github.com/fghzxm>`_, + `@refnum <https://github.com/refnum>`_, + `@pramodk (Pramod Kumbhar) <https://github.com/pramodk>`_, + `@Spirrwell <https://github.com/Spirrwell>`_, + `@scramsby (Scott Ramsby) <https://github.com/scramsby>`_. + +6.1.2 - 2019-12-11 +------------------ + +* Fixed ABI compatibility with ``libfmt.so.6.0.0`` + (`#1471 <https://github.com/fmtlib/fmt/issues/1471>`_). + +* Fixed handling types convertible to ``std::string_view`` + (`#1451 <https://github.com/fmtlib/fmt/pull/1451>`_). + Thanks `@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_. + +* Made CUDA test an opt-in enabled via the ``FMT_CUDA_TEST`` CMake option. + +* Fixed sign conversion warnings + (`#1440 <https://github.com/fmtlib/fmt/pull/1440>`_). + Thanks `@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_. + +6.1.1 - 2019-12-04 +------------------ + +* Fixed shared library build on Windows + (`#1443 <https://github.com/fmtlib/fmt/pull/1443>`_, + `#1445 <https://github.com/fmtlib/fmt/issues/1445>`_, + `#1446 <https://github.com/fmtlib/fmt/pull/1446>`_, + `#1450 <https://github.com/fmtlib/fmt/issues/1450>`_). + Thanks `@egorpugin (Egor Pugin) <https://github.com/egorpugin>`_, + `@bbolli (Beat Bolli) <https://github.com/bbolli>`_. + +* Added a missing decimal point in exponent notation with trailing zeros. + +* Removed deprecated ``format_arg_store::TYPES``. + +6.1.0 - 2019-12-01 +------------------ + +* {fmt} now formats IEEE 754 ``float`` and ``double`` using the shortest decimal + representation with correct rounding by default: + + .. code:: c++ + + #include <cmath> + #include <fmt/core.h> + + int main() { + fmt::print("{}", M_PI); + } + + prints ``3.141592653589793``. + +* Made the fast binary to decimal floating-point formatter the default, + simplified it and improved performance. {fmt} is now 15 times faster than + libc++'s ``std::ostringstream``, 11 times faster than ``printf`` and 10% + faster than double-conversion on `dtoa-benchmark + <https://github.com/fmtlib/dtoa-benchmark>`_: + + ================== ========= ======= + Function Time (ns) Speedup + ================== ========= ======= + ostringstream 1,346.30 1.00x + ostrstream 1,195.74 1.13x + sprintf 995.08 1.35x + doubleconv 99.10 13.59x + fmt 88.34 15.24x + ================== ========= ======= + + .. image:: https://user-images.githubusercontent.com/576385/ + 69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png + +* {fmt} no longer converts ``float`` arguments to ``double``. In particular this + improves the default (shortest) representation of floats and makes + ``fmt::format`` consistent with ``std::format`` specs + (`#1336 <https://github.com/fmtlib/fmt/issues/1336>`_, + `#1353 <https://github.com/fmtlib/fmt/issues/1353>`_, + `#1360 <https://github.com/fmtlib/fmt/pull/1360>`_, + `#1361 <https://github.com/fmtlib/fmt/pull/1361>`_): + + .. code:: c++ + + fmt::print("{}", 0.1f); + + prints ``0.1`` instead of ``0.10000000149011612``. + + Thanks `@orivej (Orivej Desh) <https://github.com/orivej>`_. + +* Made floating-point formatting output consistent with ``printf``/iostreams + (`#1376 <https://github.com/fmtlib/fmt/issues/1376>`_, + `#1417 <https://github.com/fmtlib/fmt/issues/1417>`_). + +* Added support for 128-bit integers + (`#1287 <https://github.com/fmtlib/fmt/pull/1287>`_): + + .. code:: c++ + + fmt::print("{}", std::numeric_limits<__int128_t>::max()); + + prints ``170141183460469231731687303715884105727``. + + Thanks `@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_. + +* The overload of ``print`` that takes ``text_style`` is now atomic, i.e. the + output from different threads doesn't interleave + (`#1351 <https://github.com/fmtlib/fmt/pull/1351>`_). + Thanks `@tankiJong (Tanki Zhang) <https://github.com/tankiJong>`_. + +* Made compile time in the header-only mode ~20% faster by reducing the number + of template instantiations. ``wchar_t`` overload of ``vprint`` was moved from + ``fmt/core.h`` to ``fmt/format.h``. + +* Added an overload of ``fmt::join`` that works with tuples + (`#1322 <https://github.com/fmtlib/fmt/issues/1322>`_, + `#1330 <https://github.com/fmtlib/fmt/pull/1330>`_): + + .. code:: c++ + + #include <tuple> + #include <fmt/ranges.h> + + int main() { + std::tuple<char, int, float> t{'a', 1, 2.0f}; + fmt::print("{}", t); + } + + prints ``('a', 1, 2.0)``. + + Thanks `@jeremyong (Jeremy Ong) <https://github.com/jeremyong>`_. + +* Changed formatting of octal zero with prefix from "00" to "0": + + .. code:: c++ + + fmt::print("{:#o}", 0); + + prints ``0``. + +* The locale is now passed to ostream insertion (``<<``) operators + (`#1406 <https://github.com/fmtlib/fmt/pull/1406>`_): + + .. code:: c++ + + #include <fmt/locale.h> + #include <fmt/ostream.h> + + struct S { + double value; + }; + + std::ostream& operator<<(std::ostream& os, S s) { + return os << s.value; + } + + int main() { + auto s = fmt::format(std::locale("fr_FR.UTF-8"), "{}", S{0.42}); + // s == "0,42" + } + + Thanks `@dlaugt (Daniel Laügt) <https://github.com/dlaugt>`_. + +* Locale-specific number formatting now uses grouping + (`#1393 <https://github.com/fmtlib/fmt/issues/1393>`_ + `#1394 <https://github.com/fmtlib/fmt/pull/1394>`_). + Thanks `@skrdaniel <https://github.com/skrdaniel>`_. + +* Fixed handling of types with deleted implicit rvalue conversion to + ``const char**`` (`#1421 <https://github.com/fmtlib/fmt/issues/1421>`_): + + .. code:: c++ + + struct mystring { + operator const char*() const&; + operator const char*() &; + operator const char*() const&& = delete; + operator const char*() && = delete; + }; + mystring str; + fmt::print("{}", str); // now compiles + +* Enums are now mapped to correct underlying types instead of ``int`` + (`#1286 <https://github.com/fmtlib/fmt/pull/1286>`_). + Thanks `@agmt (Egor Seredin) <https://github.com/agmt>`_. + +* Enum classes are no longer implicitly converted to ``int`` + (`#1424 <https://github.com/fmtlib/fmt/issues/1424>`_). + +* Added ``basic_format_parse_context`` for consistency with C++20 + ``std::format`` and deprecated ``basic_parse_context``. + +* Fixed handling of UTF-8 in precision + (`#1389 <https://github.com/fmtlib/fmt/issues/1389>`_, + `#1390 <https://github.com/fmtlib/fmt/pull/1390>`_). + Thanks `@tajtiattila (Attila Tajti) <https://github.com/tajtiattila>`_. + +* {fmt} can now be installed on Linux, macOS and Windows with + `Conda <https://docs.conda.io/en/latest/>`__ using its + `conda-forge <https://conda-forge.org>`__ + `package <https://github.com/conda-forge/fmt-feedstock>`__ + (`#1410 <https://github.com/fmtlib/fmt/pull/1410>`_):: + + conda install -c conda-forge fmt + + Thanks `@tdegeus (Tom de Geus) <https://github.com/tdegeus>`_. + +* Added a CUDA test (`#1285 <https://github.com/fmtlib/fmt/pull/1285>`_, + `#1317 <https://github.com/fmtlib/fmt/pull/1317>`_). + Thanks `@luncliff (Park DongHa) <https://github.com/luncliff>`_ and + `@risa2000 <https://github.com/risa2000>`_. + +* Improved documentation (`#1276 <https://github.com/fmtlib/fmt/pull/1276>`_, + `#1291 <https://github.com/fmtlib/fmt/issues/1291>`_, + `#1296 <https://github.com/fmtlib/fmt/issues/1296>`_, + `#1315 <https://github.com/fmtlib/fmt/pull/1315>`_, + `#1332 <https://github.com/fmtlib/fmt/pull/1332>`_, + `#1337 <https://github.com/fmtlib/fmt/pull/1337>`_, + `#1395 <https://github.com/fmtlib/fmt/issues/1395>`_ + `#1418 <https://github.com/fmtlib/fmt/pull/1418>`_). + Thanks + `@waywardmonkeys (Bruce Mitchener) <https://github.com/waywardmonkeys>`_, + `@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_, + `@jackoalan (Jack Andersen) <https://github.com/jackoalan>`_. + +* Various code improvements + (`#1358 <https://github.com/fmtlib/fmt/pull/1358>`_, + `#1407 <https://github.com/fmtlib/fmt/pull/1407>`_). + Thanks `@orivej (Orivej Desh) <https://github.com/orivej>`_, + `@dpacbach (David P. Sicilia) <https://github.com/dpacbach>`_, + +* Fixed compile-time format string checks for user-defined types + (`#1292 <https://github.com/fmtlib/fmt/issues/1292>`_). + +* Worked around a false positive in ``unsigned-integer-overflow`` sanitizer + (`#1377 <https://github.com/fmtlib/fmt/issues/1377>`_). + +* Fixed various warnings and compilation issues + (`#1273 <https://github.com/fmtlib/fmt/issues/1273>`_, + `#1278 <https://github.com/fmtlib/fmt/pull/1278>`_, + `#1280 <https://github.com/fmtlib/fmt/pull/1280>`_, + `#1281 <https://github.com/fmtlib/fmt/issues/1281>`_, + `#1288 <https://github.com/fmtlib/fmt/issues/1288>`_, + `#1290 <https://github.com/fmtlib/fmt/pull/1290>`_, + `#1301 <https://github.com/fmtlib/fmt/pull/1301>`_, + `#1305 <https://github.com/fmtlib/fmt/issues/1305>`_, + `#1306 <https://github.com/fmtlib/fmt/issues/1306>`_, + `#1309 <https://github.com/fmtlib/fmt/issues/1309>`_, + `#1312 <https://github.com/fmtlib/fmt/pull/1312>`_, + `#1313 <https://github.com/fmtlib/fmt/issues/1313>`_, + `#1316 <https://github.com/fmtlib/fmt/issues/1316>`_, + `#1319 <https://github.com/fmtlib/fmt/issues/1319>`_, + `#1320 <https://github.com/fmtlib/fmt/pull/1320>`_, + `#1326 <https://github.com/fmtlib/fmt/pull/1326>`_, + `#1328 <https://github.com/fmtlib/fmt/pull/1328>`_, + `#1344 <https://github.com/fmtlib/fmt/issues/1344>`_, + `#1345 <https://github.com/fmtlib/fmt/pull/1345>`_, + `#1347 <https://github.com/fmtlib/fmt/pull/1347>`_, + `#1349 <https://github.com/fmtlib/fmt/pull/1349>`_, + `#1354 <https://github.com/fmtlib/fmt/issues/1354>`_, + `#1362 <https://github.com/fmtlib/fmt/issues/1362>`_, + `#1366 <https://github.com/fmtlib/fmt/issues/1366>`_, + `#1364 <https://github.com/fmtlib/fmt/pull/1364>`_, + `#1370 <https://github.com/fmtlib/fmt/pull/1370>`_, + `#1371 <https://github.com/fmtlib/fmt/pull/1371>`_, + `#1385 <https://github.com/fmtlib/fmt/issues/1385>`_, + `#1388 <https://github.com/fmtlib/fmt/issues/1388>`_, + `#1397 <https://github.com/fmtlib/fmt/pull/1397>`_, + `#1414 <https://github.com/fmtlib/fmt/pull/1414>`_, + `#1416 <https://github.com/fmtlib/fmt/pull/1416>`_, + `#1422 <https://github.com/fmtlib/fmt/issues/1422>`_ + `#1427 <https://github.com/fmtlib/fmt/pull/1427>`_, + `#1431 <https://github.com/fmtlib/fmt/issues/1431>`_, + `#1433 <https://github.com/fmtlib/fmt/pull/1433>`_). + Thanks `@hhb <https://github.com/hhb>`_, + `@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_, + `@gabime (Gabi Melman) <https://github.com/gabime>`_, + `@neheb (Rosen Penev) <https://github.com/neheb>`_, + `@vedranmiletic (Vedran Miletić) <https://github.com/vedranmiletic>`_, + `@dkavolis (Daumantas Kavolis) <https://github.com/dkavolis>`_, + `@mwinterb <https://github.com/mwinterb>`_, + `@orivej (Orivej Desh) <https://github.com/orivej>`_, + `@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_ + `@leonklingele <https://github.com/leonklingele>`_, + `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_, + `@kent-tri <https://github.com/kent-tri>`_, + `@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_, + `@marti4d (Chris Martin) <https://github.com/marti4d>`_. + +6.0.0 - 2019-08-26 +------------------ + +* Switched to the `MIT license + <https://github.com/fmtlib/fmt/blob/5a4b24613ba16cc689977c3b5bd8274a3ba1dd1f/LICENSE.rst>`_ + with an optional exception that allows distributing binary code without + attribution. + +* Floating-point formatting is now locale-independent by default: + + .. code:: c++ + + #include <locale> + #include <fmt/core.h> + + int main() { + std::locale::global(std::locale("ru_RU.UTF-8")); + fmt::print("value = {}", 4.2); + } + + prints "value = 4.2" regardless of the locale. + + For locale-specific formatting use the ``n`` specifier: + + .. code:: c++ + + std::locale::global(std::locale("ru_RU.UTF-8")); + fmt::print("value = {:n}", 4.2); + + prints "value = 4,2". + +* Added an experimental Grisu floating-point formatting algorithm + implementation (disabled by default). To enable it compile with the + ``FMT_USE_GRISU`` macro defined to 1: + + .. code:: c++ + + #define FMT_USE_GRISU 1 + #include <fmt/format.h> + + auto s = fmt::format("{}", 4.2); // formats 4.2 using Grisu + + With Grisu enabled, {fmt} is 13x faster than ``std::ostringstream`` (libc++) + and 10x faster than ``sprintf`` on `dtoa-benchmark + <https://github.com/fmtlib/dtoa-benchmark>`_ (`full results + <https://fmt.dev/unknown_mac64_clang10.0.html>`_): + + .. image:: https://user-images.githubusercontent.com/576385/ + 54883977-9fe8c000-4e28-11e9-8bde-272d122e7c52.jpg + +* Separated formatting and parsing contexts for consistency with + `C++20 std::format <http://eel.is/c++draft/format>`_, removing the + undocumented ``basic_format_context::parse_context()`` function. + +* Added `oss-fuzz <https://github.com/google/oss-fuzz>`_ support + (`#1199 <https://github.com/fmtlib/fmt/pull/1199>`_). + Thanks `@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_. + +* ``formatter`` specializations now always take precedence over ``operator<<`` + (`#952 <https://github.com/fmtlib/fmt/issues/952>`_): + + .. code:: c++ + + #include <iostream> + #include <fmt/ostream.h> + + struct S {}; + + std::ostream& operator<<(std::ostream& os, S) { + return os << 1; + } + + template <> + struct fmt::formatter<S> : fmt::formatter<int> { + auto format(S, format_context& ctx) { + return formatter<int>::format(2, ctx); + } + }; + + int main() { + std::cout << S() << "\n"; // prints 1 using operator<< + fmt::print("{}\n", S()); // prints 2 using formatter + } + +* Introduced the experimental ``fmt::compile`` function that does format string + compilation (`#618 <https://github.com/fmtlib/fmt/issues/618>`_, + `#1169 <https://github.com/fmtlib/fmt/issues/1169>`_, + `#1171 <https://github.com/fmtlib/fmt/pull/1171>`_): + + .. code:: c++ + + #include <fmt/compile.h> + + auto f = fmt::compile<int>("{}"); + std::string s = fmt::format(f, 42); // can be called multiple times to + // format different values + // s == "42" + + It moves the cost of parsing a format string outside of the format function + which can be beneficial when identically formatting many objects of the same + types. Thanks `@stryku (Mateusz Janek) <https://github.com/stryku>`_. + +* Added experimental ``%`` format specifier that formats floating-point values + as percentages (`#1060 <https://github.com/fmtlib/fmt/pull/1060>`_, + `#1069 <https://github.com/fmtlib/fmt/pull/1069>`_, + `#1071 <https://github.com/fmtlib/fmt/pull/1071>`_): + + .. code:: c++ + + auto s = fmt::format("{:.1%}", 0.42); // s == "42.0%" + + Thanks `@gawain-bolton (Gawain Bolton) <https://github.com/gawain-bolton>`_. + +* Implemented precision for floating-point durations + (`#1004 <https://github.com/fmtlib/fmt/issues/1004>`_, + `#1012 <https://github.com/fmtlib/fmt/pull/1012>`_): + + .. code:: c++ + + auto s = fmt::format("{:.1}", std::chrono::duration<double>(1.234)); + // s == 1.2s + + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. + +* Implemented ``chrono`` format specifiers ``%Q`` and ``%q`` that give the value + and the unit respectively (`#1019 <https://github.com/fmtlib/fmt/pull/1019>`_): + + .. code:: c++ + + auto value = fmt::format("{:%Q}", 42s); // value == "42" + auto unit = fmt::format("{:%q}", 42s); // unit == "s" + + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. + +* Fixed handling of dynamic width in chrono formatter: + + .. code:: c++ + + auto s = fmt::format("{0:{1}%H:%M:%S}", std::chrono::seconds(12345), 12); + // ^ width argument index ^ width + // s == "03:25:45 " + + Thanks Howard Hinnant. + +* Removed deprecated ``fmt/time.h``. Use ``fmt/chrono.h`` instead. + +* Added ``fmt::format`` and ``fmt::vformat`` overloads that take ``text_style`` + (`#993 <https://github.com/fmtlib/fmt/issues/993>`_, + `#994 <https://github.com/fmtlib/fmt/pull/994>`_): + + .. code:: c++ + + #include <fmt/color.h> + + std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), + "The answer is {}.", 42); + + Thanks `@Naios (Denis Blank) <https://github.com/Naios>`_. + +* Removed the deprecated color API (``print_colored``). Use the new API, namely + ``print`` overloads that take ``text_style`` instead. + +* Made ``std::unique_ptr`` and ``std::shared_ptr`` formattable as pointers via + ``fmt::ptr`` (`#1121 <https://github.com/fmtlib/fmt/pull/1121>`_): + + .. code:: c++ + + std::unique_ptr<int> p = ...; + fmt::print("{}", fmt::ptr(p)); // prints p as a pointer + + Thanks `@sighingnow (Tao He) <https://github.com/sighingnow>`_. + +* Made ``print`` and ``vprint`` report I/O errors + (`#1098 <https://github.com/fmtlib/fmt/issues/1098>`_, + `#1099 <https://github.com/fmtlib/fmt/pull/1099>`_). + Thanks `@BillyDonahue (Billy Donahue) <https://github.com/BillyDonahue>`_. + +* Marked deprecated APIs with the ``[[deprecated]]`` attribute and removed + internal uses of deprecated APIs + (`#1022 <https://github.com/fmtlib/fmt/pull/1022>`_). + Thanks `@eliaskosunen (Elias Kosunen) <https://github.com/eliaskosunen>`_. + +* Modernized the codebase using more C++11 features and removing workarounds. + Most importantly, ``buffer_context`` is now an alias template, so + use ``buffer_context<T>`` instead of ``buffer_context<T>::type``. + These features require GCC 4.8 or later. + +* ``formatter`` specializations now always take precedence over implicit + conversions to ``int`` and the undocumented ``convert_to_int`` trait + is now deprecated. + +* Moved the undocumented ``basic_writer``, ``writer``, and ``wwriter`` types + to the ``internal`` namespace. + +* Removed deprecated ``basic_format_context::begin()``. Use ``out()`` instead. + +* Disallowed passing the result of ``join`` as an lvalue to prevent misuse. + +* Refactored the undocumented structs that represent parsed format specifiers + to simplify the API and allow multibyte fill. + +* Moved SFINAE to template parameters to reduce symbol sizes. + +* Switched to ``fputws`` for writing wide strings so that it's no longer + required to call ``_setmode`` on Windows + (`#1229 <https://github.com/fmtlib/fmt/issues/1229>`_, + `#1243 <https://github.com/fmtlib/fmt/pull/1243>`_). + Thanks `@jackoalan (Jack Andersen) <https://github.com/jackoalan>`_. + +* Improved literal-based API + (`#1254 <https://github.com/fmtlib/fmt/pull/1254>`_). + Thanks `@sylveon (Charles Milette) <https://github.com/sylveon>`_. + +* Added support for exotic platforms without ``uintptr_t`` such as IBM i + (AS/400) which has 128-bit pointers and only 64-bit integers + (`#1059 <https://github.com/fmtlib/fmt/issues/1059>`_). + +* Added `Sublime Text syntax highlighting config + <https://github.com/fmtlib/fmt/blob/master/support/C%2B%2B.sublime-syntax>`_ + (`#1037 <https://github.com/fmtlib/fmt/issues/1037>`_). + Thanks `@Kronuz (Germán Méndez Bravo) <https://github.com/Kronuz>`_. + +* Added the ``FMT_ENFORCE_COMPILE_STRING`` macro to enforce the use of + compile-time format strings + (`#1231 <https://github.com/fmtlib/fmt/pull/1231>`_). + Thanks `@jackoalan (Jack Andersen) <https://github.com/jackoalan>`_. + +* Stopped setting ``CMAKE_BUILD_TYPE`` if {fmt} is a subproject + (`#1081 <https://github.com/fmtlib/fmt/issues/1081>`_). + +* Various build improvements + (`#1039 <https://github.com/fmtlib/fmt/pull/1039>`_, + `#1078 <https://github.com/fmtlib/fmt/pull/1078>`_, + `#1091 <https://github.com/fmtlib/fmt/pull/1091>`_, + `#1103 <https://github.com/fmtlib/fmt/pull/1103>`_, + `#1177 <https://github.com/fmtlib/fmt/pull/1177>`_). + Thanks `@luncliff (Park DongHa) <https://github.com/luncliff>`_, + `@jasonszang (Jason Shuo Zang) <https://github.com/jasonszang>`_, + `@olafhering (Olaf Hering) <https://github.com/olafhering>`_, + `@Lecetem <https://github.com/Lectem>`_, + `@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_. + +* Improved documentation + (`#1049 <https://github.com/fmtlib/fmt/issues/1049>`_, + `#1051 <https://github.com/fmtlib/fmt/pull/1051>`_, + `#1083 <https://github.com/fmtlib/fmt/pull/1083>`_, + `#1113 <https://github.com/fmtlib/fmt/pull/1113>`_, + `#1114 <https://github.com/fmtlib/fmt/pull/1114>`_, + `#1146 <https://github.com/fmtlib/fmt/issues/1146>`_, + `#1180 <https://github.com/fmtlib/fmt/issues/1180>`_, + `#1250 <https://github.com/fmtlib/fmt/pull/1250>`_, + `#1252 <https://github.com/fmtlib/fmt/pull/1252>`_, + `#1265 <https://github.com/fmtlib/fmt/pull/1265>`_). + Thanks `@mikelui (Michael Lui) <https://github.com/mikelui>`_, + `@foonathan (Jonathan Müller) <https://github.com/foonathan>`_, + `@BillyDonahue (Billy Donahue) <https://github.com/BillyDonahue>`_, + `@jwakely (Jonathan Wakely) <https://github.com/jwakely>`_, + `@kaisbe (Kais Ben Salah) <https://github.com/kaisbe>`_, + `@sdebionne (Samuel Debionne) <https://github.com/sdebionne>`_. + +* Fixed ambiguous formatter specialization in ``fmt/ranges.h`` + (`#1123 <https://github.com/fmtlib/fmt/issues/1123>`_). + +* Fixed formatting of a non-empty ``std::filesystem::path`` which is an + infinitely deep range of its components + (`#1268 <https://github.com/fmtlib/fmt/issues/1268>`_). + +* Fixed handling of general output iterators when formatting characters + (`#1056 <https://github.com/fmtlib/fmt/issues/1056>`_, + `#1058 <https://github.com/fmtlib/fmt/pull/1058>`_). + Thanks `@abolz (Alexander Bolz) <https://github.com/abolz>`_. + +* Fixed handling of output iterators in ``formatter`` specialization for + ranges (`#1064 <https://github.com/fmtlib/fmt/issues/1064>`_). + +* Fixed handling of exotic character types + (`#1188 <https://github.com/fmtlib/fmt/issues/1188>`_). + +* Made chrono formatting work with exceptions disabled + (`#1062 <https://github.com/fmtlib/fmt/issues/1062>`_). + +* Fixed DLL visibility issues + (`#1134 <https://github.com/fmtlib/fmt/pull/1134>`_, + `#1147 <https://github.com/fmtlib/fmt/pull/1147>`_). + Thanks `@denchat <https://github.com/denchat>`_. + +* Disabled the use of UDL template extension on GCC 9 + (`#1148 <https://github.com/fmtlib/fmt/issues/1148>`_). + +* Removed misplaced ``format`` compile-time checks from ``printf`` + (`#1173 <https://github.com/fmtlib/fmt/issues/1173>`_). + +* Fixed issues in the experimental floating-point formatter + (`#1072 <https://github.com/fmtlib/fmt/issues/1072>`_, + `#1129 <https://github.com/fmtlib/fmt/issues/1129>`_, + `#1153 <https://github.com/fmtlib/fmt/issues/1153>`_, + `#1155 <https://github.com/fmtlib/fmt/pull/1155>`_, + `#1210 <https://github.com/fmtlib/fmt/issues/1210>`_, + `#1222 <https://github.com/fmtlib/fmt/issues/1222>`_). + Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_. + +* Fixed bugs discovered by fuzzing or during fuzzing integration + (`#1124 <https://github.com/fmtlib/fmt/issues/1124>`_, + `#1127 <https://github.com/fmtlib/fmt/issues/1127>`_, + `#1132 <https://github.com/fmtlib/fmt/issues/1132>`_, + `#1135 <https://github.com/fmtlib/fmt/pull/1135>`_, + `#1136 <https://github.com/fmtlib/fmt/issues/1136>`_, + `#1141 <https://github.com/fmtlib/fmt/issues/1141>`_, + `#1142 <https://github.com/fmtlib/fmt/issues/1142>`_, + `#1178 <https://github.com/fmtlib/fmt/issues/1178>`_, + `#1179 <https://github.com/fmtlib/fmt/issues/1179>`_, + `#1194 <https://github.com/fmtlib/fmt/issues/1194>`_). + Thanks `@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_. + +* Fixed building tests on FreeBSD and Hurd + (`#1043 <https://github.com/fmtlib/fmt/issues/1043>`_). + Thanks `@jackyf (Eugene V. Lyubimkin) <https://github.com/jackyf>`_. + +* Fixed various warnings and compilation issues + (`#998 <https://github.com/fmtlib/fmt/pull/998>`_, + `#1006 <https://github.com/fmtlib/fmt/pull/1006>`_, + `#1008 <https://github.com/fmtlib/fmt/issues/1008>`_, + `#1011 <https://github.com/fmtlib/fmt/issues/1011>`_, + `#1025 <https://github.com/fmtlib/fmt/issues/1025>`_, + `#1027 <https://github.com/fmtlib/fmt/pull/1027>`_, + `#1028 <https://github.com/fmtlib/fmt/pull/1028>`_, + `#1029 <https://github.com/fmtlib/fmt/pull/1029>`_, + `#1030 <https://github.com/fmtlib/fmt/pull/1030>`_, + `#1031 <https://github.com/fmtlib/fmt/pull/1031>`_, + `#1054 <https://github.com/fmtlib/fmt/pull/1054>`_, + `#1063 <https://github.com/fmtlib/fmt/issues/1063>`_, + `#1068 <https://github.com/fmtlib/fmt/pull/1068>`_, + `#1074 <https://github.com/fmtlib/fmt/pull/1074>`_, + `#1075 <https://github.com/fmtlib/fmt/pull/1075>`_, + `#1079 <https://github.com/fmtlib/fmt/pull/1079>`_, + `#1086 <https://github.com/fmtlib/fmt/pull/1086>`_, + `#1088 <https://github.com/fmtlib/fmt/issues/1088>`_, + `#1089 <https://github.com/fmtlib/fmt/pull/1089>`_, + `#1094 <https://github.com/fmtlib/fmt/pull/1094>`_, + `#1101 <https://github.com/fmtlib/fmt/issues/1101>`_, + `#1102 <https://github.com/fmtlib/fmt/pull/1102>`_, + `#1105 <https://github.com/fmtlib/fmt/issues/1105>`_, + `#1107 <https://github.com/fmtlib/fmt/pull/1107>`_, + `#1115 <https://github.com/fmtlib/fmt/issues/1115>`_, + `#1117 <https://github.com/fmtlib/fmt/issues/1117>`_, + `#1118 <https://github.com/fmtlib/fmt/issues/1118>`_, + `#1120 <https://github.com/fmtlib/fmt/issues/1120>`_, + `#1123 <https://github.com/fmtlib/fmt/issues/1123>`_, + `#1139 <https://github.com/fmtlib/fmt/pull/1139>`_, + `#1140 <https://github.com/fmtlib/fmt/issues/1140>`_, + `#1143 <https://github.com/fmtlib/fmt/issues/1143>`_, + `#1144 <https://github.com/fmtlib/fmt/pull/1144>`_, + `#1150 <https://github.com/fmtlib/fmt/pull/1150>`_, + `#1151 <https://github.com/fmtlib/fmt/pull/1151>`_, + `#1152 <https://github.com/fmtlib/fmt/issues/1152>`_, + `#1154 <https://github.com/fmtlib/fmt/issues/1154>`_, + `#1156 <https://github.com/fmtlib/fmt/issues/1156>`_, + `#1159 <https://github.com/fmtlib/fmt/pull/1159>`_, + `#1175 <https://github.com/fmtlib/fmt/issues/1175>`_, + `#1181 <https://github.com/fmtlib/fmt/issues/1181>`_, + `#1186 <https://github.com/fmtlib/fmt/issues/1186>`_, + `#1187 <https://github.com/fmtlib/fmt/pull/1187>`_, + `#1191 <https://github.com/fmtlib/fmt/pull/1191>`_, + `#1197 <https://github.com/fmtlib/fmt/issues/1197>`_, + `#1200 <https://github.com/fmtlib/fmt/issues/1200>`_, + `#1203 <https://github.com/fmtlib/fmt/issues/1203>`_, + `#1205 <https://github.com/fmtlib/fmt/issues/1205>`_, + `#1206 <https://github.com/fmtlib/fmt/pull/1206>`_, + `#1213 <https://github.com/fmtlib/fmt/issues/1213>`_, + `#1214 <https://github.com/fmtlib/fmt/issues/1214>`_, + `#1217 <https://github.com/fmtlib/fmt/pull/1217>`_, + `#1228 <https://github.com/fmtlib/fmt/issues/1228>`_, + `#1230 <https://github.com/fmtlib/fmt/pull/1230>`_, + `#1232 <https://github.com/fmtlib/fmt/issues/1232>`_, + `#1235 <https://github.com/fmtlib/fmt/pull/1235>`_, + `#1236 <https://github.com/fmtlib/fmt/pull/1236>`_, + `#1240 <https://github.com/fmtlib/fmt/issues/1240>`_). + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_, + `@mwinterb <https://github.com/mwinterb>`_, + `@eliaskosunen (Elias Kosunen) <https://github.com/eliaskosunen>`_, + `@morinmorin <https://github.com/morinmorin>`_, + `@ricco19 (Brian Ricciardelli) <https://github.com/ricco19>`_, + `@waywardmonkeys (Bruce Mitchener) <https://github.com/waywardmonkeys>`_, + `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_, + `@remyabel <https://github.com/remyabel>`_, + `@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_, + `@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_, + `@rcane (Ronny Krüger) <https://github.com/rcane>`_, + `@mocabe <https://github.com/mocabe>`_, + `@denchat <https://github.com/denchat>`_, + `@cjdb (Christopher Di Bella) <https://github.com/cjdb>`_, + `@HazardyKnusperkeks (Björn Schäpers) <https://github.com/HazardyKnusperkeks>`_, + `@vedranmiletic (Vedran Miletić) <https://github.com/vedranmiletic>`_, + `@jackoalan (Jack Andersen) <https://github.com/jackoalan>`_, + `@DaanDeMeyer (Daan De Meyer) <https://github.com/DaanDeMeyer>`_, + `@starkmapper (Mark Stapper) <https://github.com/starkmapper>`_. + +5.3.0 - 2018-12-28 +------------------ + +* Introduced experimental chrono formatting support: + + .. code:: c++ + + #include <fmt/chrono.h> + + int main() { + using namespace std::literals::chrono_literals; + fmt::print("Default format: {} {}\n", 42s, 100ms); + fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s); + } + + prints:: + + Default format: 42s 100ms + strftime-like format: 03:15:30 + +* Added experimental support for emphasis (bold, italic, underline, + strikethrough), colored output to a file stream, and improved colored + formatting API + (`#961 <https://github.com/fmtlib/fmt/pull/961>`_, + `#967 <https://github.com/fmtlib/fmt/pull/967>`_, + `#973 <https://github.com/fmtlib/fmt/pull/973>`_): + + .. code:: c++ + + #include <fmt/color.h> + + int main() { + print(fg(fmt::color::crimson) | fmt::emphasis::bold, + "Hello, {}!\n", "world"); + print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) | + fmt::emphasis::underline, "Hello, {}!\n", "мир"); + print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, + "Hello, {}!\n", "世界"); + } + + prints the following on modern terminals with RGB color support: + + .. image:: https://user-images.githubusercontent.com/576385/ + 50405788-b66e7500-076e-11e9-9592-7324d1f951d8.png + + Thanks `@Rakete1111 (Nicolas) <https://github.com/Rakete1111>`_. + +* Added support for 4-bit terminal colors + (`#968 <https://github.com/fmtlib/fmt/issues/968>`_, + `#974 <https://github.com/fmtlib/fmt/pull/974>`_) + + .. code:: c++ + + #include <fmt/color.h> + + int main() { + print(fg(fmt::terminal_color::red), "stop\n"); + } + + Note that these colors vary by terminal: + + .. image:: https://user-images.githubusercontent.com/576385/ + 50405925-dbfc7e00-0770-11e9-9b85-333fab0af9ac.png + + Thanks `@Rakete1111 (Nicolas) <https://github.com/Rakete1111>`_. + +* Parameterized formatting functions on the type of the format string + (`#880 <https://github.com/fmtlib/fmt/issues/880>`_, + `#881 <https://github.com/fmtlib/fmt/pull/881>`_, + `#883 <https://github.com/fmtlib/fmt/pull/883>`_, + `#885 <https://github.com/fmtlib/fmt/pull/885>`_, + `#897 <https://github.com/fmtlib/fmt/pull/897>`_, + `#920 <https://github.com/fmtlib/fmt/issues/920>`_). + Any object of type ``S`` that has an overloaded ``to_string_view(const S&)`` + returning ``fmt::string_view`` can be used as a format string: + + .. code:: c++ + + 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); + + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. + +* Made ``std::string_view`` work as a format string + (`#898 <https://github.com/fmtlib/fmt/pull/898>`_): + + .. code:: c++ + + auto message = fmt::format(std::string_view("The answer is {}."), 42); + + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. + +* Added wide string support to compile-time format string checks + (`#924 <https://github.com/fmtlib/fmt/pull/924>`_): + + .. code:: c++ + + print(fmt(L"{:f}"), 42); // compile-time error: invalid type specifier + + Thanks `@XZiar <https://github.com/XZiar>`_. + +* Made colored print functions work with wide strings + (`#867 <https://github.com/fmtlib/fmt/pull/867>`_): + + .. code:: c++ + + #include <fmt/color.h> + + int main() { + print(fg(fmt::color::red), L"{}\n", 42); + } + + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. + +* Introduced experimental Unicode support + (`#628 <https://github.com/fmtlib/fmt/issues/628>`_, + `#891 <https://github.com/fmtlib/fmt/pull/891>`_): + + .. code:: c++ + + using namespace fmt::literals; + auto s = fmt::format("{:*^5}"_u, "🤡"_u); // s == "**🤡**"_u + +* Improved locale support: + + .. code:: c++ + + #include <fmt/locale.h> + + struct numpunct : std::numpunct<char> { + protected: + char do_thousands_sep() const override { return '~'; } + }; + + std::locale loc; + auto s = fmt::format(std::locale(loc, new numpunct()), "{:n}", 1234567); + // s == "1~234~567" + +* Constrained formatting functions on proper iterator types + (`#921 <https://github.com/fmtlib/fmt/pull/921>`_). + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. + +* Added ``make_printf_args`` and ``make_wprintf_args`` functions + (`#934 <https://github.com/fmtlib/fmt/pull/934>`_). + Thanks `@tnovotny <https://github.com/tnovotny>`_. + +* Deprecated ``fmt::visit``, ``parse_context``, and ``wparse_context``. + Use ``fmt::visit_format_arg``, ``format_parse_context``, and + ``wformat_parse_context`` instead. + +* Removed undocumented ``basic_fixed_buffer`` which has been superseded by the + iterator-based API + (`#873 <https://github.com/fmtlib/fmt/issues/873>`_, + `#902 <https://github.com/fmtlib/fmt/pull/902>`_). + Thanks `@superfunc (hollywood programmer) <https://github.com/superfunc>`_. + +* Disallowed repeated leading zeros in an argument ID: + + .. code:: c++ + + fmt::print("{000}", 42); // error + +* Reintroduced support for gcc 4.4. + +* Fixed compilation on platforms with exotic ``double`` + (`#878 <https://github.com/fmtlib/fmt/issues/878>`_). + +* Improved documentation + (`#164 <https://github.com/fmtlib/fmt/issues/164>`_, + `#877 <https://github.com/fmtlib/fmt/issues/877>`_, + `#901 <https://github.com/fmtlib/fmt/pull/901>`_, + `#906 <https://github.com/fmtlib/fmt/pull/906>`_, + `#979 <https://github.com/fmtlib/fmt/pull/979>`_). + Thanks `@kookjr (Mathew Cucuzella) <https://github.com/kookjr>`_, + `@DarkDimius (Dmitry Petrashko) <https://github.com/DarkDimius>`_, + `@HecticSerenity <https://github.com/HecticSerenity>`_. + +* Added pkgconfig support which makes it easier to consume the library from + meson and other build systems + (`#916 <https://github.com/fmtlib/fmt/pull/916>`_). + Thanks `@colemickens (Cole Mickens) <https://github.com/colemickens>`_. + +* Various build improvements + (`#909 <https://github.com/fmtlib/fmt/pull/909>`_, + `#926 <https://github.com/fmtlib/fmt/pull/926>`_, + `#937 <https://github.com/fmtlib/fmt/pull/937>`_, + `#953 <https://github.com/fmtlib/fmt/pull/953>`_, + `#959 <https://github.com/fmtlib/fmt/pull/959>`_). + Thanks `@tchaikov (Kefu Chai) <https://github.com/tchaikov>`_, + `@luncliff (Park DongHa) <https://github.com/luncliff>`_, + `@AndreasSchoenle (Andreas Schönle) <https://github.com/AndreasSchoenle>`_, + `@hotwatermorning <https://github.com/hotwatermorning>`_, + `@Zefz (JohanJansen) <https://github.com/Zefz>`_. + +* Improved ``string_view`` construction performance + (`#914 <https://github.com/fmtlib/fmt/pull/914>`_). + Thanks `@gabime (Gabi Melman) <https://github.com/gabime>`_. + +* Fixed non-matching char types + (`#895 <https://github.com/fmtlib/fmt/pull/895>`_). + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. + +* Fixed ``format_to_n`` with ``std::back_insert_iterator`` + (`#913 <https://github.com/fmtlib/fmt/pull/913>`_). + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. + +* Fixed locale-dependent formatting + (`#905 <https://github.com/fmtlib/fmt/issues/905>`_). + +* Fixed various compiler warnings and errors + (`#882 <https://github.com/fmtlib/fmt/pull/882>`_, + `#886 <https://github.com/fmtlib/fmt/pull/886>`_, + `#933 <https://github.com/fmtlib/fmt/pull/933>`_, + `#941 <https://github.com/fmtlib/fmt/pull/941>`_, + `#931 <https://github.com/fmtlib/fmt/issues/931>`_, + `#943 <https://github.com/fmtlib/fmt/pull/943>`_, + `#954 <https://github.com/fmtlib/fmt/pull/954>`_, + `#956 <https://github.com/fmtlib/fmt/pull/956>`_, + `#962 <https://github.com/fmtlib/fmt/pull/962>`_, + `#965 <https://github.com/fmtlib/fmt/issues/965>`_, + `#977 <https://github.com/fmtlib/fmt/issues/977>`_, + `#983 <https://github.com/fmtlib/fmt/pull/983>`_, + `#989 <https://github.com/fmtlib/fmt/pull/989>`_). + Thanks `@Luthaf (Guillaume Fraux) <https://github.com/Luthaf>`_, + `@stevenhoving (Steven Hoving) <https://github.com/stevenhoving>`_, + `@christinaa (Kristina Brooks) <https://github.com/christinaa>`_, + `@lgritz (Larry Gritz) <https://github.com/lgritz>`_, + `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_, + `@0x8000-0000 (Sign Bit) <https://github.com/0x8000-0000>`_, + `@liuping1997 <https://github.com/liuping1997>`_. + +5.2.1 - 2018-09-21 +------------------ + +* Fixed ``visit`` lookup issues on gcc 7 & 8 + (`#870 <https://github.com/fmtlib/fmt/pull/870>`_). + Thanks `@medithe <https://github.com/medithe>`_. + +* Fixed linkage errors on older gcc. + +* Prevented ``fmt/range.h`` from specializing ``fmt::basic_string_view`` + (`#865 <https://github.com/fmtlib/fmt/issues/865>`_, + `#868 <https://github.com/fmtlib/fmt/pull/868>`_). + Thanks `@hhggit (dual) <https://github.com/hhggit>`_. + +* Improved error message when formatting unknown types + (`#872 <https://github.com/fmtlib/fmt/pull/872>`_). + Thanks `@foonathan (Jonathan Müller) <https://github.com/foonathan>`_, + +* Disabled templated user-defined literals when compiled under nvcc + (`#875 <https://github.com/fmtlib/fmt/pull/875>`_). + Thanks `@CandyGumdrop (Candy Gumdrop) <https://github.com/CandyGumdrop>`_, + +* Fixed ``format_to`` formatting to ``wmemory_buffer`` + (`#874 <https://github.com/fmtlib/fmt/issues/874>`_). + +5.2.0 - 2018-09-13 +------------------ + +* Optimized format string parsing and argument processing which resulted in up + to 5x speed up on long format strings and significant performance boost on + various benchmarks. For example, version 5.2 is 2.22x faster than 5.1 on + decimal integer formatting with ``format_to`` (macOS, clang-902.0.39.2): + + ================== ======= ======= + Method Time, s Speedup + ================== ======= ======= + fmt::format 5.1 0.58 + fmt::format 5.2 0.35 1.66x + fmt::format_to 5.1 0.51 + fmt::format_to 5.2 0.23 2.22x + sprintf 0.71 + std::to_string 1.01 + std::stringstream 1.73 + ================== ======= ======= + +* Changed the ``fmt`` macro from opt-out to opt-in to prevent name collisions. + To enable it define the ``FMT_STRING_ALIAS`` macro to 1 before including + ``fmt/format.h``: + + .. code:: c++ + + #define FMT_STRING_ALIAS 1 + #include <fmt/format.h> + std::string answer = format(fmt("{}"), 42); + +* Added compile-time format string checks to ``format_to`` overload that takes + ``fmt::memory_buffer`` (`#783 <https://github.com/fmtlib/fmt/issues/783>`_): + + .. code:: c++ + + fmt::memory_buffer buf; + // Compile-time error: invalid type specifier. + fmt::format_to(buf, fmt("{:d}"), "foo"); + +* Moved experimental color support to ``fmt/color.h`` and enabled the + new API by default. The old API can be enabled by defining the + ``FMT_DEPRECATED_COLORS`` macro. + +* Added formatting support for types explicitly convertible to + ``fmt::string_view``: + + .. code:: c++ + + struct foo { + explicit operator fmt::string_view() const { return "foo"; } + }; + auto s = format("{}", foo()); + + In particular, this makes formatting function work with + ``folly::StringPiece``. + +* Implemented preliminary support for ``char*_t`` by replacing the ``format`` + function overloads with a single function template parameterized on the string + type. + +* Added support for dynamic argument lists + (`#814 <https://github.com/fmtlib/fmt/issues/814>`_, + `#819 <https://github.com/fmtlib/fmt/pull/819>`_). + Thanks `@MikePopoloski (Michael Popoloski) + <https://github.com/MikePopoloski>`_. + +* Reduced executable size overhead for embedded targets using newlib nano by + making locale dependency optional + (`#839 <https://github.com/fmtlib/fmt/pull/839>`_). + Thanks `@teajay-fr (Thomas Benard) <https://github.com/teajay-fr>`_. + +* Keep ``noexcept`` specifier when exceptions are disabled + (`#801 <https://github.com/fmtlib/fmt/issues/801>`_, + `#810 <https://github.com/fmtlib/fmt/pull/810>`_). + Thanks `@qis (Alexej Harm) <https://github.com/qis>`_. + +* Fixed formatting of user-defined types providing ``operator<<`` with + ``format_to_n`` + (`#806 <https://github.com/fmtlib/fmt/pull/806>`_). + Thanks `@mkurdej (Marek Kurdej) <https://github.com/mkurdej>`_. + +* Fixed dynamic linkage of new symbols + (`#808 <https://github.com/fmtlib/fmt/issues/808>`_). + +* Fixed global initialization issue + (`#807 <https://github.com/fmtlib/fmt/issues/807>`_): + + .. code:: c++ + + // This works on compilers with constexpr support. + static const std::string answer = fmt::format("{}", 42); + +* Fixed various compiler warnings and errors + (`#804 <https://github.com/fmtlib/fmt/pull/804>`_, + `#809 <https://github.com/fmtlib/fmt/issues/809>`_, + `#811 <https://github.com/fmtlib/fmt/pull/811>`_, + `#822 <https://github.com/fmtlib/fmt/issues/822>`_, + `#827 <https://github.com/fmtlib/fmt/pull/827>`_, + `#830 <https://github.com/fmtlib/fmt/issues/830>`_, + `#838 <https://github.com/fmtlib/fmt/pull/838>`_, + `#843 <https://github.com/fmtlib/fmt/issues/843>`_, + `#844 <https://github.com/fmtlib/fmt/pull/844>`_, + `#851 <https://github.com/fmtlib/fmt/issues/851>`_, + `#852 <https://github.com/fmtlib/fmt/pull/852>`_, + `#854 <https://github.com/fmtlib/fmt/pull/854>`_). + Thanks `@henryiii (Henry Schreiner) <https://github.com/henryiii>`_, + `@medithe <https://github.com/medithe>`_, and + `@eliasdaler (Elias Daler) <https://github.com/eliasdaler>`_. + +5.1.0 - 2018-07-05 +------------------ + +* Added experimental support for RGB color output enabled with + the ``FMT_EXTENDED_COLORS`` macro: + + .. code:: c++ + + #define FMT_EXTENDED_COLORS + #define FMT_HEADER_ONLY // or compile fmt with FMT_EXTENDED_COLORS defined + #include <fmt/format.h> + + fmt::print(fmt::color::steel_blue, "Some beautiful text"); + + The old API (the ``print_colored`` and ``vprint_colored`` functions and the + ``color`` enum) is now deprecated. + (`#762 <https://github.com/fmtlib/fmt/issues/762>`_ + `#767 <https://github.com/fmtlib/fmt/pull/767>`_). + thanks `@Remotion (Remo) <https://github.com/Remotion>`_. + +* Added quotes to strings in ranges and tuples + (`#766 <https://github.com/fmtlib/fmt/pull/766>`_). + Thanks `@Remotion (Remo) <https://github.com/Remotion>`_. + +* Made ``format_to`` work with ``basic_memory_buffer`` + (`#776 <https://github.com/fmtlib/fmt/issues/776>`_). + +* Added ``vformat_to_n`` and ``wchar_t`` overload of ``format_to_n`` + (`#764 <https://github.com/fmtlib/fmt/issues/764>`_, + `#769 <https://github.com/fmtlib/fmt/issues/769>`_). + +* Made ``is_range`` and ``is_tuple_like`` part of public (experimental) API + to allow specialization for user-defined types + (`#751 <https://github.com/fmtlib/fmt/issues/751>`_, + `#759 <https://github.com/fmtlib/fmt/pull/759>`_). + Thanks `@drrlvn (Dror Levin) <https://github.com/drrlvn>`_. + +* Added more compilers to continuous integration and increased ``FMT_PEDANTIC`` + warning levels + (`#736 <https://github.com/fmtlib/fmt/pull/736>`_). + Thanks `@eliaskosunen (Elias Kosunen) <https://github.com/eliaskosunen>`_. + +* Fixed compilation with MSVC 2013. + +* Fixed handling of user-defined types in ``format_to`` + (`#793 <https://github.com/fmtlib/fmt/issues/793>`_). + +* Forced linking of inline ``vformat`` functions into the library + (`#795 <https://github.com/fmtlib/fmt/issues/795>`_). + +* Fixed incorrect call to on_align in ``'{:}='`` + (`#750 <https://github.com/fmtlib/fmt/issues/750>`_). + +* Fixed floating-point formatting to a non-back_insert_iterator with sign & + numeric alignment specified + (`#756 <https://github.com/fmtlib/fmt/issues/756>`_). + +* Fixed formatting to an array with ``format_to_n`` + (`#778 <https://github.com/fmtlib/fmt/issues/778>`_). + +* Fixed formatting of more than 15 named arguments + (`#754 <https://github.com/fmtlib/fmt/issues/754>`_). + +* Fixed handling of compile-time strings when including ``fmt/ostream.h``. + (`#768 <https://github.com/fmtlib/fmt/issues/768>`_). + +* Fixed various compiler warnings and errors + (`#742 <https://github.com/fmtlib/fmt/issues/742>`_, + `#748 <https://github.com/fmtlib/fmt/issues/748>`_, + `#752 <https://github.com/fmtlib/fmt/issues/752>`_, + `#770 <https://github.com/fmtlib/fmt/issues/770>`_, + `#775 <https://github.com/fmtlib/fmt/pull/775>`_, + `#779 <https://github.com/fmtlib/fmt/issues/779>`_, + `#780 <https://github.com/fmtlib/fmt/pull/780>`_, + `#790 <https://github.com/fmtlib/fmt/pull/790>`_, + `#792 <https://github.com/fmtlib/fmt/pull/792>`_, + `#800 <https://github.com/fmtlib/fmt/pull/800>`_). + Thanks `@Remotion (Remo) <https://github.com/Remotion>`_, + `@gabime (Gabi Melman) <https://github.com/gabime>`_, + `@foonathan (Jonathan Müller) <https://github.com/foonathan>`_, + `@Dark-Passenger (Dhruv Paranjape) <https://github.com/Dark-Passenger>`_, and + `@0x8000-0000 (Sign Bit) <https://github.com/0x8000-0000>`_. + +5.0.0 - 2018-05-21 +------------------ + +* Added a requirement for partial C++11 support, most importantly variadic + templates and type traits, and dropped ``FMT_VARIADIC_*`` emulation macros. + Variadic templates are available since GCC 4.4, Clang 2.9 and MSVC 18.0 (2013). + For older compilers use {fmt} `version 4.x + <https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which continues to be + maintained and works with C++98 compilers. + +* Renamed symbols to follow standard C++ naming conventions and proposed a subset + of the library for standardization in `P0645R2 Text Formatting + <https://wg21.link/P0645>`_. + +* Implemented ``constexpr`` parsing of format strings and `compile-time format + string checks + <https://fmt.dev/latest/api.html#compile-time-format-string-checks>`_. For + example + + .. code:: c++ + + #include <fmt/format.h> + + std::string s = format(fmt("{:d}"), "foo"); + + gives a compile-time error because ``d`` is an invalid specifier for strings + (`godbolt <https://godbolt.org/g/rnCy9Q>`__):: + + ... + <source>:4:19: note: in instantiation of function template specialization 'fmt::v5::format<S, char [4]>' requested here + std::string s = format(fmt("{:d}"), "foo"); + ^ + format.h:1337:13: note: non-constexpr function 'on_error' cannot be used in a constant expression + handler.on_error("invalid type specifier"); + + Compile-time checks require relaxed ``constexpr`` (C++14 feature) support. If + the latter is not available, checks will be performed at runtime. + +* Separated format string parsing and formatting in the extension API to enable + compile-time format string processing. For example + + .. code:: c++ + + struct Answer {}; + + namespace fmt { + template <> + struct formatter<Answer> { + constexpr auto parse(parse_context& ctx) { + auto it = ctx.begin(); + spec = *it; + if (spec != 'd' && spec != 's') + throw format_error("invalid specifier"); + return ++it; + } + + template <typename FormatContext> + auto format(Answer, FormatContext& ctx) { + return spec == 's' ? + format_to(ctx.begin(), "{}", "fourty-two") : + format_to(ctx.begin(), "{}", 42); + } + + char spec = 0; + }; + } + + std::string s = format(fmt("{:x}"), Answer()); + + gives a compile-time error due to invalid format specifier (`godbolt + <https://godbolt.org/g/2jQ1Dv>`__):: + + ... + <source>:12:45: error: expression '<throw-expression>' is not a constant expression + throw format_error("invalid specifier"); + +* Added `iterator support + <https://fmt.dev/latest/api.html#output-iterator-support>`_: + + .. code:: c++ + + #include <vector> + #include <fmt/format.h> + + std::vector<char> out; + fmt::format_to(std::back_inserter(out), "{}", 42); + +* Added the `format_to_n + <https://fmt.dev/latest/api.html#_CPPv2N3fmt11format_to_nE8OutputItNSt6size_tE11string_viewDpRK4Args>`_ + function that restricts the output to the specified number of characters + (`#298 <https://github.com/fmtlib/fmt/issues/298>`_): + + .. code:: c++ + + char out[4]; + fmt::format_to_n(out, sizeof(out), "{}", 12345); + // out == "1234" (without terminating '\0') + +* Added the `formatted_size + <https://fmt.dev/latest/api.html#_CPPv2N3fmt14formatted_sizeE11string_viewDpRK4Args>`_ + function for computing the output size: + + .. code:: c++ + + #include <fmt/format.h> + + auto size = fmt::formatted_size("{}", 12345); // size == 5 + +* Improved compile times by reducing dependencies on standard headers and + providing a lightweight `core API <https://fmt.dev/latest/api.html#core-api>`_: + + .. code:: c++ + + #include <fmt/core.h> + + fmt::print("The answer is {}.", 42); + + See `Compile time and code bloat + <https://github.com/fmtlib/fmt#compile-time-and-code-bloat>`_. + +* Added the `make_format_args + <https://fmt.dev/latest/api.html#_CPPv2N3fmt16make_format_argsEDpRK4Args>`_ + function for capturing formatting arguments: + + .. code:: c++ + + // Prints formatted error message. + void vreport_error(const char *format, fmt::format_args args) { + fmt::print("Error: "); + fmt::vprint(format, args); + } + template <typename... Args> + void report_error(const char *format, const Args & ... args) { + vreport_error(format, fmt::make_format_args(args...)); + } + +* Added the ``make_printf_args`` function for capturing ``printf`` arguments + (`#687 <https://github.com/fmtlib/fmt/issues/687>`_, + `#694 <https://github.com/fmtlib/fmt/pull/694>`_). + Thanks `@Kronuz (Germán Méndez Bravo) <https://github.com/Kronuz>`_. + +* Added prefix ``v`` to non-variadic functions taking ``format_args`` to + distinguish them from variadic ones: + + .. code:: c++ + + std::string vformat(string_view format_str, format_args args); + + template <typename... Args> + std::string format(string_view format_str, const Args & ... args); + +* Added experimental support for formatting ranges, containers and tuple-like + types in ``fmt/ranges.h`` (`#735 <https://github.com/fmtlib/fmt/pull/735>`_): + + .. code:: c++ + + #include <fmt/ranges.h> + + std::vector<int> v = {1, 2, 3}; + fmt::print("{}", v); // prints {1, 2, 3} + + Thanks `@Remotion (Remo) <https://github.com/Remotion>`_. + +* Implemented ``wchar_t`` date and time formatting + (`#712 <https://github.com/fmtlib/fmt/pull/712>`_): + + .. code:: c++ + + #include <fmt/time.h> + + std::time_t t = std::time(nullptr); + auto s = fmt::format(L"The date is {:%Y-%m-%d}.", *std::localtime(&t)); + + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. + +* Provided more wide string overloads + (`#724 <https://github.com/fmtlib/fmt/pull/724>`_). + Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. + +* Switched from a custom null-terminated string view class to ``string_view`` + in the format API and provided ``fmt::string_view`` which implements a subset + of ``std::string_view`` API for pre-C++17 systems. + +* Added support for ``std::experimental::string_view`` + (`#607 <https://github.com/fmtlib/fmt/pull/607>`_): + + .. code:: c++ + + #include <fmt/core.h> + #include <experimental/string_view> + + fmt::print("{}", std::experimental::string_view("foo")); + + Thanks `@virgiliofornazin (Virgilio Alexandre Fornazin) + <https://github.com/virgiliofornazin>`__. + +* Allowed mixing named and automatic arguments: + + .. code:: c++ + + fmt::format("{} {two}", 1, fmt::arg("two", 2)); + +* Removed the write API in favor of the `format API + <https://fmt.dev/latest/api.html#format-api>`_ with compile-time handling of + format strings. + +* Disallowed formatting of multibyte strings into a wide character target + (`#606 <https://github.com/fmtlib/fmt/pull/606>`_). + +* Improved documentation + (`#515 <https://github.com/fmtlib/fmt/pull/515>`_, + `#614 <https://github.com/fmtlib/fmt/issues/614>`_, + `#617 <https://github.com/fmtlib/fmt/pull/617>`_, + `#661 <https://github.com/fmtlib/fmt/pull/661>`_, + `#680 <https://github.com/fmtlib/fmt/pull/680>`_). + Thanks `@ibell (Ian Bell) <https://github.com/ibell>`_, + `@mihaitodor (Mihai Todor) <https://github.com/mihaitodor>`_, and + `@johnthagen <https://github.com/johnthagen>`_. + +* Implemented more efficient handling of large number of format arguments. + +* Introduced an inline namespace for symbol versioning. + +* Added debug postfix ``d`` to the ``fmt`` library name + (`#636 <https://github.com/fmtlib/fmt/issues/636>`_). + +* Removed unnecessary ``fmt/`` prefix in includes + (`#397 <https://github.com/fmtlib/fmt/pull/397>`_). + Thanks `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_. + +* Moved ``fmt/*.h`` to ``include/fmt/*.h`` to prevent irrelevant files and + directories appearing on the include search paths when fmt is used as a + subproject and moved source files to the ``src`` directory. + +* Added qmake project file ``support/fmt.pro`` + (`#641 <https://github.com/fmtlib/fmt/pull/641>`_). + Thanks `@cowo78 (Giuseppe Corbelli) <https://github.com/cowo78>`_. + +* Added Gradle build file ``support/build.gradle`` + (`#649 <https://github.com/fmtlib/fmt/pull/649>`_). + Thanks `@luncliff (Park DongHa) <https://github.com/luncliff>`_. + +* Removed ``FMT_CPPFORMAT`` CMake option. + +* Fixed a name conflict with the macro ``CHAR_WIDTH`` in glibc + (`#616 <https://github.com/fmtlib/fmt/pull/616>`_). + Thanks `@aroig (Abdó Roig-Maranges) <https://github.com/aroig>`_. + +* Fixed handling of nested braces in ``fmt::join`` + (`#638 <https://github.com/fmtlib/fmt/issues/638>`_). + +* Added ``SOURCELINK_SUFFIX`` for compatibility with Sphinx 1.5 + (`#497 <https://github.com/fmtlib/fmt/pull/497>`_). + Thanks `@ginggs (Graham Inggs) <https://github.com/ginggs>`_. + +* Added a missing ``inline`` in the header-only mode + (`#626 <https://github.com/fmtlib/fmt/pull/626>`_). + Thanks `@aroig (Abdó Roig-Maranges) <https://github.com/aroig>`_. + +* Fixed various compiler warnings + (`#640 <https://github.com/fmtlib/fmt/pull/640>`_, + `#656 <https://github.com/fmtlib/fmt/pull/656>`_, + `#679 <https://github.com/fmtlib/fmt/pull/679>`_, + `#681 <https://github.com/fmtlib/fmt/pull/681>`_, + `#705 <https://github.com/fmtlib/fmt/pull/705>`__, + `#715 <https://github.com/fmtlib/fmt/issues/715>`_, + `#717 <https://github.com/fmtlib/fmt/pull/717>`_, + `#720 <https://github.com/fmtlib/fmt/pull/720>`_, + `#723 <https://github.com/fmtlib/fmt/pull/723>`_, + `#726 <https://github.com/fmtlib/fmt/pull/726>`_, + `#730 <https://github.com/fmtlib/fmt/pull/730>`_, + `#739 <https://github.com/fmtlib/fmt/pull/739>`_). + Thanks `@peterbell10 <https://github.com/peterbell10>`_, + `@LarsGullik <https://github.com/LarsGullik>`_, + `@foonathan (Jonathan Müller) <https://github.com/foonathan>`_, + `@eliaskosunen (Elias Kosunen) <https://github.com/eliaskosunen>`_, + `@christianparpart (Christian Parpart) <https://github.com/christianparpart>`_, + `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_, + and `@mwinterb <https://github.com/mwinterb>`_. + +* Worked around an MSVC bug and fixed several warnings + (`#653 <https://github.com/fmtlib/fmt/pull/653>`_). + Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_. + +* Worked around GCC bug 67371 + (`#682 <https://github.com/fmtlib/fmt/issues/682>`_). + +* Fixed compilation with ``-fno-exceptions`` + (`#655 <https://github.com/fmtlib/fmt/pull/655>`_). + Thanks `@chenxiaolong (Andrew Gunnerson) <https://github.com/chenxiaolong>`_. + +* Made ``constexpr remove_prefix`` gcc version check tighter + (`#648 <https://github.com/fmtlib/fmt/issues/648>`_). + +* Renamed internal type enum constants to prevent collision with poorly written + C libraries (`#644 <https://github.com/fmtlib/fmt/issues/644>`_). + +* Added detection of ``wostream operator<<`` + (`#650 <https://github.com/fmtlib/fmt/issues/650>`_). + +* Fixed compilation on OpenBSD + (`#660 <https://github.com/fmtlib/fmt/pull/660>`_). + Thanks `@hubslave <https://github.com/hubslave>`_. + +* Fixed compilation on FreeBSD 12 + (`#732 <https://github.com/fmtlib/fmt/pull/732>`_). + Thanks `@dankm <https://github.com/dankm>`_. + +* Fixed compilation when there is a mismatch between ``-std`` options between + the library and user code + (`#664 <https://github.com/fmtlib/fmt/issues/664>`_). + +* Fixed compilation with GCC 7 and ``-std=c++11`` + (`#734 <https://github.com/fmtlib/fmt/issues/734>`_). + +* Improved generated binary code on GCC 7 and older + (`#668 <https://github.com/fmtlib/fmt/issues/668>`_). + +* Fixed handling of numeric alignment with no width + (`#675 <https://github.com/fmtlib/fmt/issues/675>`_). + +* Fixed handling of empty strings in UTF8/16 converters + (`#676 <https://github.com/fmtlib/fmt/pull/676>`_). + Thanks `@vgalka-sl (Vasili Galka) <https://github.com/vgalka-sl>`_. + +* Fixed formatting of an empty ``string_view`` + (`#689 <https://github.com/fmtlib/fmt/issues/689>`_). + +* Fixed detection of ``string_view`` on libc++ + (`#686 <https://github.com/fmtlib/fmt/issues/686>`_). + +* Fixed DLL issues (`#696 <https://github.com/fmtlib/fmt/pull/696>`_). + Thanks `@sebkoenig <https://github.com/sebkoenig>`_. + +* Fixed compile checks for mixing narrow and wide strings + (`#690 <https://github.com/fmtlib/fmt/issues/690>`_). + +* Disabled unsafe implicit conversion to ``std::string`` + (`#729 <https://github.com/fmtlib/fmt/issues/729>`_). + +* Fixed handling of reused format specs (as in ``fmt::join``) for pointers + (`#725 <https://github.com/fmtlib/fmt/pull/725>`_). + Thanks `@mwinterb <https://github.com/mwinterb>`_. + +* Fixed installation of ``fmt/ranges.h`` + (`#738 <https://github.com/fmtlib/fmt/pull/738>`_). + Thanks `@sv1990 <https://github.com/sv1990>`_. + +4.1.0 - 2017-12-20 +------------------ + +* Added ``fmt::to_wstring()`` in addition to ``fmt::to_string()`` + (`#559 <https://github.com/fmtlib/fmt/pull/559>`_). + Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_. + +* Added support for C++17 ``std::string_view`` + (`#571 <https://github.com/fmtlib/fmt/pull/571>`_ and + `#578 <https://github.com/fmtlib/fmt/pull/578>`_). + Thanks `@thelostt (Mário Feroldi) <https://github.com/thelostt>`_ and + `@mwinterb <https://github.com/mwinterb>`_. + +* Enabled stream exceptions to catch errors + (`#581 <https://github.com/fmtlib/fmt/issues/581>`_). + Thanks `@crusader-mike <https://github.com/crusader-mike>`_. + +* Allowed formatting of class hierarchies with ``fmt::format_arg()`` + (`#547 <https://github.com/fmtlib/fmt/pull/547>`_). + Thanks `@rollbear (Björn Fahller) <https://github.com/rollbear>`_. + +* Removed limitations on character types + (`#563 <https://github.com/fmtlib/fmt/pull/563>`_). + Thanks `@Yelnats321 (Elnar Dakeshov) <https://github.com/Yelnats321>`_. + +* Conditionally enabled use of ``std::allocator_traits`` + (`#583 <https://github.com/fmtlib/fmt/pull/583>`_). + Thanks `@mwinterb <https://github.com/mwinterb>`_. + +* Added support for ``const`` variadic member function emulation with + ``FMT_VARIADIC_CONST`` (`#591 <https://github.com/fmtlib/fmt/pull/591>`_). + Thanks `@ludekvodicka (Ludek Vodicka) <https://github.com/ludekvodicka>`_. + +* Various bugfixes: bad overflow check, unsupported implicit type conversion + when determining formatting function, test segfaults + (`#551 <https://github.com/fmtlib/fmt/issues/551>`_), ill-formed macros + (`#542 <https://github.com/fmtlib/fmt/pull/542>`_) and ambiguous overloads + (`#580 <https://github.com/fmtlib/fmt/issues/580>`_). + Thanks `@xylosper (Byoung-young Lee) <https://github.com/xylosper>`_. + +* Prevented warnings on MSVC (`#605 <https://github.com/fmtlib/fmt/pull/605>`_, + `#602 <https://github.com/fmtlib/fmt/pull/602>`_, and + `#545 <https://github.com/fmtlib/fmt/pull/545>`_), + clang (`#582 <https://github.com/fmtlib/fmt/pull/582>`_), + GCC (`#573 <https://github.com/fmtlib/fmt/issues/573>`_), + various conversion warnings (`#609 <https://github.com/fmtlib/fmt/pull/609>`_, + `#567 <https://github.com/fmtlib/fmt/pull/567>`_, + `#553 <https://github.com/fmtlib/fmt/pull/553>`_ and + `#553 <https://github.com/fmtlib/fmt/pull/553>`_), and added ``override`` and + ``[[noreturn]]`` (`#549 <https://github.com/fmtlib/fmt/pull/549>`_ and + `#555 <https://github.com/fmtlib/fmt/issues/555>`_). + Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_, + `@virgiliofornazin (Virgilio Alexandre Fornazin) + <https://gihtub.com/virgiliofornazin>`_, + `@alexanderbock (Alexander Bock) <https://github.com/alexanderbock>`_, + `@yumetodo <https://github.com/yumetodo>`_, + `@VaderY (Császár Mátyás) <https://github.com/VaderY>`_, + `@jpcima (JP Cimalando) <https://github.com/jpcima>`_, + `@thelostt (Mário Feroldi) <https://github.com/thelostt>`_, and + `@Manu343726 (Manu Sánchez) <https://github.com/Manu343726>`_. + +* Improved CMake: Used ``GNUInstallDirs`` to set installation location + (`#610 <https://github.com/fmtlib/fmt/pull/610>`_) and fixed warnings + (`#536 <https://github.com/fmtlib/fmt/pull/536>`_ and + `#556 <https://github.com/fmtlib/fmt/pull/556>`_). + Thanks `@mikecrowe (Mike Crowe) <https://github.com/mikecrowe>`_, + `@evgen231 <https://github.com/evgen231>`_ and + `@henryiii (Henry Schreiner) <https://github.com/henryiii>`_. + +4.0.0 - 2017-06-27 +------------------ + +* Removed old compatibility headers ``cppformat/*.h`` and CMake options + (`#527 <https://github.com/fmtlib/fmt/pull/527>`_). + Thanks `@maddinat0r (Alex Martin) <https://github.com/maddinat0r>`_. + +* Added ``string.h`` containing ``fmt::to_string()`` as alternative to + ``std::to_string()`` as well as other string writer functionality + (`#326 <https://github.com/fmtlib/fmt/issues/326>`_ and + `#441 <https://github.com/fmtlib/fmt/pull/441>`_): + + .. code:: c++ + + #include "fmt/string.h" + + std::string answer = fmt::to_string(42); + + Thanks to `@glebov-andrey (Andrey Glebov) + <https://github.com/glebov-andrey>`_. + +* Moved ``fmt::printf()`` to new ``printf.h`` header and allowed ``%s`` as + generic specifier (`#453 <https://github.com/fmtlib/fmt/pull/453>`_), + made ``%.f`` more conformant to regular ``printf()`` + (`#490 <https://github.com/fmtlib/fmt/pull/490>`_), added custom writer + support (`#476 <https://github.com/fmtlib/fmt/issues/476>`_) and implemented + missing custom argument formatting + (`#339 <https://github.com/fmtlib/fmt/pull/339>`_ and + `#340 <https://github.com/fmtlib/fmt/pull/340>`_): + + .. code:: c++ + + #include "fmt/printf.h" + + // %s format specifier can be used with any argument type. + fmt::printf("%s", 42); + + Thanks `@mojoBrendan <https://github.com/mojoBrendan>`_, + `@manylegged (Arthur Danskin) <https://github.com/manylegged>`_ and + `@spacemoose (Glen Stark) <https://github.com/spacemoose>`_. + See also `#360 <https://github.com/fmtlib/fmt/issues/360>`_, + `#335 <https://github.com/fmtlib/fmt/issues/335>`_ and + `#331 <https://github.com/fmtlib/fmt/issues/331>`_. + +* Added ``container.h`` containing a ``BasicContainerWriter`` + to write to containers like ``std::vector`` + (`#450 <https://github.com/fmtlib/fmt/pull/450>`_). + Thanks `@polyvertex (Jean-Charles Lefebvre) <https://github.com/polyvertex>`_. + +* Added ``fmt::join()`` function that takes a range and formats + its elements separated by a given string + (`#466 <https://github.com/fmtlib/fmt/pull/466>`_): + + .. code:: c++ + + #include "fmt/format.h" + + std::vector<double> v = {1.2, 3.4, 5.6}; + // Prints "(+01.20, +03.40, +05.60)". + fmt::print("({:+06.2f})", fmt::join(v.begin(), v.end(), ", ")); + + Thanks `@olivier80 <https://github.com/olivier80>`_. + +* Added support for custom formatting specifications to simplify customization + of built-in formatting (`#444 <https://github.com/fmtlib/fmt/pull/444>`_). + Thanks `@polyvertex (Jean-Charles Lefebvre) <https://github.com/polyvertex>`_. + See also `#439 <https://github.com/fmtlib/fmt/issues/439>`_. + +* Added ``fmt::format_system_error()`` for error code formatting + (`#323 <https://github.com/fmtlib/fmt/issues/323>`_ and + `#526 <https://github.com/fmtlib/fmt/pull/526>`_). + Thanks `@maddinat0r (Alex Martin) <https://github.com/maddinat0r>`_. + +* Added thread-safe ``fmt::localtime()`` and ``fmt::gmtime()`` + as replacement for the standard version to ``time.h`` + (`#396 <https://github.com/fmtlib/fmt/pull/396>`_). + Thanks `@codicodi <https://github.com/codicodi>`_. + +* Internal improvements to ``NamedArg`` and ``ArgLists`` + (`#389 <https://github.com/fmtlib/fmt/pull/389>`_ and + `#390 <https://github.com/fmtlib/fmt/pull/390>`_). + Thanks `@chronoxor <https://github.com/chronoxor>`_. + +* Fixed crash due to bug in ``FormatBuf`` + (`#493 <https://github.com/fmtlib/fmt/pull/493>`_). + Thanks `@effzeh <https://github.com/effzeh>`_. See also + `#480 <https://github.com/fmtlib/fmt/issues/480>`_ and + `#491 <https://github.com/fmtlib/fmt/issues/491>`_. + +* Fixed handling of wide strings in ``fmt::StringWriter``. + +* Improved compiler error messages + (`#357 <https://github.com/fmtlib/fmt/issues/357>`_). + +* Fixed various warnings and issues with various compilers + (`#494 <https://github.com/fmtlib/fmt/pull/494>`_, + `#499 <https://github.com/fmtlib/fmt/pull/499>`_, + `#483 <https://github.com/fmtlib/fmt/pull/483>`_, + `#485 <https://github.com/fmtlib/fmt/pull/485>`_, + `#482 <https://github.com/fmtlib/fmt/pull/482>`_, + `#475 <https://github.com/fmtlib/fmt/pull/475>`_, + `#473 <https://github.com/fmtlib/fmt/pull/473>`_ and + `#414 <https://github.com/fmtlib/fmt/pull/414>`_). + Thanks `@chronoxor <https://github.com/chronoxor>`_, + `@zhaohuaxishi <https://github.com/zhaohuaxishi>`_, + `@pkestene (Pierre Kestener) <https://github.com/pkestene>`_, + `@dschmidt (Dominik Schmidt) <https://github.com/dschmidt>`_ and + `@0x414c (Alexey Gorishny) <https://github.com/0x414c>`_ . + +* Improved CMake: targets are now namespaced + (`#511 <https://github.com/fmtlib/fmt/pull/511>`_ and + `#513 <https://github.com/fmtlib/fmt/pull/513>`_), supported header-only + ``printf.h`` (`#354 <https://github.com/fmtlib/fmt/pull/354>`_), fixed issue + with minimal supported library subset + (`#418 <https://github.com/fmtlib/fmt/issues/418>`_, + `#419 <https://github.com/fmtlib/fmt/pull/419>`_ and + `#420 <https://github.com/fmtlib/fmt/pull/420>`_). + Thanks `@bjoernthiel (Bjoern Thiel) <https://github.com/bjoernthiel>`_, + `@niosHD (Mario Werner) <https://github.com/niosHD>`_, + `@LogicalKnight (Sean LK) <https://github.com/LogicalKnight>`_ and + `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_. + +* Improved documentation. Thanks to + `@pwm1234 (Phil) <https://github.com/pwm1234>`_ for + `#393 <https://github.com/fmtlib/fmt/pull/393>`_. + +3.0.2 - 2017-06-14 +------------------ + +* Added ``FMT_VERSION`` macro + (`#411 <https://github.com/fmtlib/fmt/issues/411>`_). + +* Used ``FMT_NULL`` instead of literal ``0`` + (`#409 <https://github.com/fmtlib/fmt/pull/409>`_). + Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_. + +* Added extern templates for ``format_float`` + (`#413 <https://github.com/fmtlib/fmt/issues/413>`_). + +* Fixed implicit conversion issue + (`#507 <https://github.com/fmtlib/fmt/issues/507>`_). + +* Fixed signbit detection (`#423 <https://github.com/fmtlib/fmt/issues/423>`_). + +* Fixed naming collision (`#425 <https://github.com/fmtlib/fmt/issues/425>`_). + +* Fixed missing intrinsic for C++/CLI + (`#457 <https://github.com/fmtlib/fmt/pull/457>`_). + Thanks `@calumr (Calum Robinson) <https://github.com/calumr>`_ + +* Fixed Android detection (`#458 <https://github.com/fmtlib/fmt/pull/458>`_). + Thanks `@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_. + +* Use lean ``windows.h`` if not in header-only mode + (`#503 <https://github.com/fmtlib/fmt/pull/503>`_). + Thanks `@Quentin01 (Quentin Buathier) <https://github.com/Quentin01>`_. + +* Fixed issue with CMake exporting C++11 flag + (`#445 <https://github.com/fmtlib/fmt/pull/455>`_). + Thanks `@EricWF (Eric) <https://github.com/EricWF>`_. + +* Fixed issue with nvcc and MSVC compiler bug and MinGW + (`#505 <https://github.com/fmtlib/fmt/issues/505>`_). + +* Fixed DLL issues (`#469 <https://github.com/fmtlib/fmt/pull/469>`_ and + `#502 <https://github.com/fmtlib/fmt/pull/502>`_). + Thanks `@richardeakin (Richard Eakin) <https://github.com/richardeakin>`_ and + `@AndreasSchoenle (Andreas Schönle) <https://github.com/AndreasSchoenle>`_. + +* Fixed test compilation under FreeBSD + (`#433 <https://github.com/fmtlib/fmt/issues/433>`_). + +* Fixed various warnings (`#403 <https://github.com/fmtlib/fmt/pull/403>`_, + `#410 <https://github.com/fmtlib/fmt/pull/410>`_ and + `#510 <https://github.com/fmtlib/fmt/pull/510>`_). + Thanks `@Lecetem <https://github.com/Lectem>`_, + `@chenhayat (Chen Hayat) <https://github.com/chenhayat>`_ and + `@trozen <https://github.com/trozen>`_. + +* Worked around a broken ``__builtin_clz`` in clang with MS codegen + (`#519 <https://github.com/fmtlib/fmt/issues/519>`_). + +* Removed redundant include + (`#479 <https://github.com/fmtlib/fmt/issues/479>`_). + +* Fixed documentation issues. + +3.0.1 - 2016-11-01 +------------------ +* Fixed handling of thousands separator + (`#353 <https://github.com/fmtlib/fmt/issues/353>`_). + +* Fixed handling of ``unsigned char`` strings + (`#373 <https://github.com/fmtlib/fmt/issues/373>`_). + +* Corrected buffer growth when formatting time + (`#367 <https://github.com/fmtlib/fmt/issues/367>`_). + +* Removed warnings under MSVC and clang + (`#318 <https://github.com/fmtlib/fmt/issues/318>`_, + `#250 <https://github.com/fmtlib/fmt/issues/250>`_, also merged + `#385 <https://github.com/fmtlib/fmt/pull/385>`_ and + `#361 <https://github.com/fmtlib/fmt/pull/361>`_). + Thanks `@jcelerier (Jean-Michaël Celerier) <https://github.com/jcelerier>`_ + and `@nmoehrle (Nils Moehrle) <https://github.com/nmoehrle>`_. + +* Fixed compilation issues under Android + (`#327 <https://github.com/fmtlib/fmt/pull/327>`_, + `#345 <https://github.com/fmtlib/fmt/issues/345>`_ and + `#381 <https://github.com/fmtlib/fmt/pull/381>`_), + FreeBSD (`#358 <https://github.com/fmtlib/fmt/pull/358>`_), + Cygwin (`#388 <https://github.com/fmtlib/fmt/issues/388>`_), + MinGW (`#355 <https://github.com/fmtlib/fmt/issues/355>`_) as well as other + issues (`#350 <https://github.com/fmtlib/fmt/issues/350>`_, + `#366 <https://github.com/fmtlib/fmt/issues/355>`_, + `#348 <https://github.com/fmtlib/fmt/pull/348>`_, + `#402 <https://github.com/fmtlib/fmt/pull/402>`_, + `#405 <https://github.com/fmtlib/fmt/pull/405>`_). + Thanks to `@dpantele (Dmitry) <https://github.com/dpantele>`_, + `@hghwng (Hugh Wang) <https://github.com/hghwng>`_, + `@arvedarved (Tilman Keskinöz) <https://github.com/arvedarved>`_, + `@LogicalKnight (Sean) <https://github.com/LogicalKnight>`_ and + `@JanHellwig (Jan Hellwig) <https://github.com/janhellwig>`_. + +* Fixed some documentation issues and extended specification + (`#320 <https://github.com/fmtlib/fmt/issues/320>`_, + `#333 <https://github.com/fmtlib/fmt/pull/333>`_, + `#347 <https://github.com/fmtlib/fmt/issues/347>`_, + `#362 <https://github.com/fmtlib/fmt/pull/362>`_). + Thanks to `@smellman (Taro Matsuzawa aka. btm) + <https://github.com/smellman>`_. + +3.0.0 - 2016-05-07 +------------------ + +* The project has been renamed from C++ Format (cppformat) to fmt for + consistency with the used namespace and macro prefix + (`#307 <https://github.com/fmtlib/fmt/issues/307>`_). + Library headers are now located in the ``fmt`` directory: + + .. code:: c++ + + #include "fmt/format.h" + + Including ``format.h`` from the ``cppformat`` directory is deprecated + but works via a proxy header which will be removed in the next major version. + + The documentation is now available at https://fmt.dev. + +* Added support for `strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_-like + `date and time formatting <https://fmt.dev/3.0.0/api.html#date-and-time-formatting>`_ + (`#283 <https://github.com/fmtlib/fmt/issues/283>`_): + + .. code:: c++ + + #include "fmt/time.h" + + std::time_t t = std::time(nullptr); + // Prints "The date is 2016-04-29." (with the current date) + fmt::print("The date is {:%Y-%m-%d}.", *std::localtime(&t)); + +* ``std::ostream`` support including formatting of user-defined types that provide + overloaded ``operator<<`` has been moved to ``fmt/ostream.h``: + + .. code:: c++ + + #include "fmt/ostream.h" + + class Date { + int year_, month_, day_; + public: + Date(int year, int month, int day) : year_(year), month_(month), day_(day) {} + + friend std::ostream &operator<<(std::ostream &os, const Date &d) { + return os << d.year_ << '-' << d.month_ << '-' << d.day_; + } + }; + + std::string s = fmt::format("The date is {}", Date(2012, 12, 9)); + // s == "The date is 2012-12-9" + +* Added support for `custom argument formatters + <https://fmt.dev/3.0.0/api.html#argument-formatters>`_ + (`#235 <https://github.com/fmtlib/fmt/issues/235>`_). + +* Added support for locale-specific integer formatting with the ``n`` specifier + (`#305 <https://github.com/fmtlib/fmt/issues/305>`_): + + .. code:: c++ + + std::setlocale(LC_ALL, "en_US.utf8"); + fmt::print("cppformat: {:n}\n", 1234567); // prints 1,234,567 + +* Sign is now preserved when formatting an integer with an incorrect ``printf`` + format specifier (`#265 <https://github.com/fmtlib/fmt/issues/265>`_): + + .. code:: c++ + + fmt::printf("%lld", -42); // prints -42 + + Note that it would be an undefined behavior in ``std::printf``. + +* Length modifiers such as ``ll`` are now optional in printf formatting + functions and the correct type is determined automatically + (`#255 <https://github.com/fmtlib/fmt/issues/255>`_): + + .. code:: c++ + + fmt::printf("%d", std::numeric_limits<long long>::max()); + + Note that it would be an undefined behavior in ``std::printf``. + +* Added initial support for custom formatters + (`#231 <https://github.com/fmtlib/fmt/issues/231>`_). + +* Fixed detection of user-defined literal support on Intel C++ compiler + (`#311 <https://github.com/fmtlib/fmt/issues/311>`_, + `#312 <https://github.com/fmtlib/fmt/pull/312>`_). + Thanks to `@dean0x7d (Dean Moldovan) <https://github.com/dean0x7d>`_ and + `@speth (Ray Speth) <https://github.com/speth>`_. + +* Reduced compile time + (`#243 <https://github.com/fmtlib/fmt/pull/243>`_, + `#249 <https://github.com/fmtlib/fmt/pull/249>`_, + `#317 <https://github.com/fmtlib/fmt/issues/317>`_): + + .. image:: https://cloud.githubusercontent.com/assets/4831417/11614060/ + b9e826d2-9c36-11e5-8666-d4131bf503ef.png + + .. image:: https://cloud.githubusercontent.com/assets/4831417/11614080/ + 6ac903cc-9c37-11e5-8165-26df6efae364.png + + Thanks to `@dean0x7d (Dean Moldovan) <https://github.com/dean0x7d>`_. + +* Compile test fixes (`#313 <https://github.com/fmtlib/fmt/pull/313>`_). + Thanks to `@dean0x7d (Dean Moldovan) <https://github.com/dean0x7d>`_. + +* Documentation fixes (`#239 <https://github.com/fmtlib/fmt/pull/239>`_, + `#248 <https://github.com/fmtlib/fmt/issues/248>`_, + `#252 <https://github.com/fmtlib/fmt/issues/252>`_, + `#258 <https://github.com/fmtlib/fmt/pull/258>`_, + `#260 <https://github.com/fmtlib/fmt/issues/260>`_, + `#301 <https://github.com/fmtlib/fmt/issues/301>`_, + `#309 <https://github.com/fmtlib/fmt/pull/309>`_). + Thanks to `@ReadmeCritic <https://github.com/ReadmeCritic>`_ + `@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_ and + `@jwilk (Jakub Wilk) <https://github.com/jwilk>`_. + +* Fixed compiler and sanitizer warnings + (`#244 <https://github.com/fmtlib/fmt/issues/244>`_, + `#256 <https://github.com/fmtlib/fmt/pull/256>`_, + `#259 <https://github.com/fmtlib/fmt/pull/259>`_, + `#263 <https://github.com/fmtlib/fmt/issues/263>`_, + `#274 <https://github.com/fmtlib/fmt/issues/274>`_, + `#277 <https://github.com/fmtlib/fmt/pull/277>`_, + `#286 <https://github.com/fmtlib/fmt/pull/286>`_, + `#291 <https://github.com/fmtlib/fmt/issues/291>`_, + `#296 <https://github.com/fmtlib/fmt/issues/296>`_, + `#308 <https://github.com/fmtlib/fmt/issues/308>`_) + Thanks to `@mwinterb <https://github.com/mwinterb>`_, + `@pweiskircher (Patrik Weiskircher) <https://github.com/pweiskircher>`_, + `@Naios <https://github.com/Naios>`_. + +* Improved compatibility with Windows Store apps + (`#280 <https://github.com/fmtlib/fmt/issues/280>`_, + `#285 <https://github.com/fmtlib/fmt/pull/285>`_) + Thanks to `@mwinterb <https://github.com/mwinterb>`_. + +* Added tests of compatibility with older C++ standards + (`#273 <https://github.com/fmtlib/fmt/pull/273>`_). + Thanks to `@niosHD <https://github.com/niosHD>`_. + +* Fixed Android build (`#271 <https://github.com/fmtlib/fmt/pull/271>`_). + Thanks to `@newnon <https://github.com/newnon>`_. + +* Changed ``ArgMap`` to be backed by a vector instead of a map. + (`#261 <https://github.com/fmtlib/fmt/issues/261>`_, + `#262 <https://github.com/fmtlib/fmt/pull/262>`_). + Thanks to `@mwinterb <https://github.com/mwinterb>`_. + +* Added ``fprintf`` overload that writes to a ``std::ostream`` + (`#251 <https://github.com/fmtlib/fmt/pull/251>`_). + Thanks to `nickhutchinson (Nicholas Hutchinson) <https://github.com/nickhutchinson>`_. + +* Export symbols when building a Windows DLL + (`#245 <https://github.com/fmtlib/fmt/pull/245>`_). + Thanks to `macdems (Maciek Dems) <https://github.com/macdems>`_. + +* Fixed compilation on Cygwin (`#304 <https://github.com/fmtlib/fmt/issues/304>`_). + +* Implemented a workaround for a bug in Apple LLVM version 4.2 of clang + (`#276 <https://github.com/fmtlib/fmt/issues/276>`_). + +* Implemented a workaround for Google Test bug + `#705 <https://github.com/google/googletest/issues/705>`_ on gcc 6 + (`#268 <https://github.com/fmtlib/fmt/issues/268>`_). + Thanks to `octoploid <https://github.com/octoploid>`_. + +* Removed Biicode support because the latter has been discontinued. + +2.1.1 - 2016-04-11 +------------------ + +* The install location for generated CMake files is now configurable via + the ``FMT_CMAKE_DIR`` CMake variable + (`#299 <https://github.com/fmtlib/fmt/pull/299>`_). + Thanks to `@niosHD <https://github.com/niosHD>`_. + +* Documentation fixes (`#252 <https://github.com/fmtlib/fmt/issues/252>`_). + +2.1.0 - 2016-03-21 +------------------ + +* Project layout and build system improvements + (`#267 <https://github.com/fmtlib/fmt/pull/267>`_): + + * The code have been moved to the ``cppformat`` directory. + Including ``format.h`` from the top-level directory is deprecated + but works via a proxy header which will be removed in the next + major version. + + * C++ Format CMake targets now have proper interface definitions. + + * Installed version of the library now supports the header-only + configuration. + + * Targets ``doc``, ``install``, and ``test`` are now disabled if C++ Format + is included as a CMake subproject. They can be enabled by setting + ``FMT_DOC``, ``FMT_INSTALL``, and ``FMT_TEST`` in the parent project. + + Thanks to `@niosHD <https://github.com/niosHD>`_. + +2.0.1 - 2016-03-13 +------------------ + +* Improved CMake find and package support + (`#264 <https://github.com/fmtlib/fmt/issues/264>`_). + Thanks to `@niosHD <https://github.com/niosHD>`_. + +* Fix compile error with Android NDK and mingw32 + (`#241 <https://github.com/fmtlib/fmt/issues/241>`_). + Thanks to `@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_. + +* Documentation fixes + (`#248 <https://github.com/fmtlib/fmt/issues/248>`_, + `#260 <https://github.com/fmtlib/fmt/issues/260>`_). + +2.0.0 - 2015-12-01 +------------------ + +General +~~~~~~~ + +* [Breaking] Named arguments + (`#169 <https://github.com/fmtlib/fmt/pull/169>`_, + `#173 <https://github.com/fmtlib/fmt/pull/173>`_, + `#174 <https://github.com/fmtlib/fmt/pull/174>`_): + + .. code:: c++ + + fmt::print("The answer is {answer}.", fmt::arg("answer", 42)); + + Thanks to `@jamboree <https://github.com/jamboree>`_. + +* [Experimental] User-defined literals for format and named arguments + (`#204 <https://github.com/fmtlib/fmt/pull/204>`_, + `#206 <https://github.com/fmtlib/fmt/pull/206>`_, + `#207 <https://github.com/fmtlib/fmt/pull/207>`_): + + .. code:: c++ + + using namespace fmt::literals; + fmt::print("The answer is {answer}.", "answer"_a=42); + + Thanks to `@dean0x7d (Dean Moldovan) <https://github.com/dean0x7d>`_. + +* [Breaking] Formatting of more than 16 arguments is now supported when using + variadic templates + (`#141 <https://github.com/fmtlib/fmt/issues/141>`_). + Thanks to `@Shauren <https://github.com/Shauren>`_. + +* Runtime width specification + (`#168 <https://github.com/fmtlib/fmt/pull/168>`_): + + .. code:: c++ + + fmt::format("{0:{1}}", 42, 5); // gives " 42" + + Thanks to `@jamboree <https://github.com/jamboree>`_. + +* [Breaking] Enums are now formatted with an overloaded ``std::ostream`` insertion + operator (``operator<<``) if available + (`#232 <https://github.com/fmtlib/fmt/issues/232>`_). + +* [Breaking] Changed default ``bool`` format to textual, "true" or "false" + (`#170 <https://github.com/fmtlib/fmt/issues/170>`_): + + .. code:: c++ + + fmt::print("{}", true); // prints "true" + + To print ``bool`` as a number use numeric format specifier such as ``d``: + + .. code:: c++ + + fmt::print("{:d}", true); // prints "1" + +* ``fmt::printf`` and ``fmt::sprintf`` now support formatting of ``bool`` with the + ``%s`` specifier giving textual output, "true" or "false" + (`#223 <https://github.com/fmtlib/fmt/pull/223>`_): + + .. code:: c++ + + fmt::printf("%s", true); // prints "true" + + Thanks to `@LarsGullik <https://github.com/LarsGullik>`_. + +* [Breaking] ``signed char`` and ``unsigned char`` are now formatted as integers by default + (`#217 <https://github.com/fmtlib/fmt/pull/217>`_). + +* [Breaking] Pointers to C strings can now be formatted with the ``p`` specifier + (`#223 <https://github.com/fmtlib/fmt/pull/223>`_): + + .. code:: c++ + + fmt::print("{:p}", "test"); // prints pointer value + + Thanks to `@LarsGullik <https://github.com/LarsGullik>`_. + +* [Breaking] ``fmt::printf`` and ``fmt::sprintf`` now print null pointers as ``(nil)`` + and null strings as ``(null)`` for consistency with glibc + (`#226 <https://github.com/fmtlib/fmt/pull/226>`_). + Thanks to `@LarsGullik <https://github.com/LarsGullik>`_. + +* [Breaking] ``fmt::(s)printf`` now supports formatting of objects of user-defined types + that provide an overloaded ``std::ostream`` insertion operator (``operator<<``) + (`#201 <https://github.com/fmtlib/fmt/issues/201>`_): + + .. code:: c++ + + fmt::printf("The date is %s", Date(2012, 12, 9)); + +* [Breaking] The ``Buffer`` template is now part of the public API and can be used + to implement custom memory buffers + (`#140 <https://github.com/fmtlib/fmt/issues/140>`_). + Thanks to `@polyvertex (Jean-Charles Lefebvre) <https://github.com/polyvertex>`_. + +* [Breaking] Improved compatibility between ``BasicStringRef`` and + `std::experimental::basic_string_view + <http://en.cppreference.com/w/cpp/experimental/basic_string_view>`_ + (`#100 <https://github.com/fmtlib/fmt/issues/100>`_, + `#159 <https://github.com/fmtlib/fmt/issues/159>`_, + `#183 <https://github.com/fmtlib/fmt/issues/183>`_): + + - Comparison operators now compare string content, not pointers + - ``BasicStringRef::c_str`` replaced by ``BasicStringRef::data`` + - ``BasicStringRef`` is no longer assumed to be null-terminated + + References to null-terminated strings are now represented by a new class, + ``BasicCStringRef``. + +* Dependency on pthreads introduced by Google Test is now optional + (`#185 <https://github.com/fmtlib/fmt/issues/185>`_). + +* New CMake options ``FMT_DOC``, ``FMT_INSTALL`` and ``FMT_TEST`` to control + generation of ``doc``, ``install`` and ``test`` targets respectively, on by default + (`#197 <https://github.com/fmtlib/fmt/issues/197>`_, + `#198 <https://github.com/fmtlib/fmt/issues/198>`_, + `#200 <https://github.com/fmtlib/fmt/issues/200>`_). + Thanks to `@maddinat0r (Alex Martin) <https://github.com/maddinat0r>`_. + +* ``noexcept`` is now used when compiling with MSVC2015 + (`#215 <https://github.com/fmtlib/fmt/pull/215>`_). + Thanks to `@dmkrepo (Dmitriy) <https://github.com/dmkrepo>`_. + +* Added an option to disable use of ``windows.h`` when ``FMT_USE_WINDOWS_H`` + is defined as 0 before including ``format.h`` + (`#171 <https://github.com/fmtlib/fmt/issues/171>`_). + Thanks to `@alfps (Alf P. Steinbach) <https://github.com/alfps>`_. + +* [Breaking] ``windows.h`` is now included with ``NOMINMAX`` unless + ``FMT_WIN_MINMAX`` is defined. This is done to prevent breaking code using + ``std::min`` and ``std::max`` and only affects the header-only configuration + (`#152 <https://github.com/fmtlib/fmt/issues/152>`_, + `#153 <https://github.com/fmtlib/fmt/pull/153>`_, + `#154 <https://github.com/fmtlib/fmt/pull/154>`_). + Thanks to `@DevO2012 <https://github.com/DevO2012>`_. + +* Improved support for custom character types + (`#171 <https://github.com/fmtlib/fmt/issues/171>`_). + Thanks to `@alfps (Alf P. Steinbach) <https://github.com/alfps>`_. + +* Added an option to disable use of IOStreams when ``FMT_USE_IOSTREAMS`` + is defined as 0 before including ``format.h`` + (`#205 <https://github.com/fmtlib/fmt/issues/205>`_, + `#208 <https://github.com/fmtlib/fmt/pull/208>`_). + Thanks to `@JodiTheTigger <https://github.com/JodiTheTigger>`_. + +* Improved detection of ``isnan``, ``isinf`` and ``signbit``. + +Optimization +~~~~~~~~~~~~ + +* Made formatting of user-defined types more efficient with a custom stream buffer + (`#92 <https://github.com/fmtlib/fmt/issues/92>`_, + `#230 <https://github.com/fmtlib/fmt/pull/230>`_). + Thanks to `@NotImplemented <https://github.com/NotImplemented>`_. + +* Further improved performance of ``fmt::Writer`` on integer formatting + and fixed a minor regression. Now it is ~7% faster than ``karma::generate`` + on Karma's benchmark + (`#186 <https://github.com/fmtlib/fmt/issues/186>`_). + +* [Breaking] Reduced `compiled code size + <https://github.com/fmtlib/fmt#compile-time-and-code-bloat>`_ + (`#143 <https://github.com/fmtlib/fmt/issues/143>`_, + `#149 <https://github.com/fmtlib/fmt/pull/149>`_). + +Distribution +~~~~~~~~~~~~ + +* [Breaking] Headers are now installed in + ``${CMAKE_INSTALL_PREFIX}/include/cppformat`` + (`#178 <https://github.com/fmtlib/fmt/issues/178>`_). + Thanks to `@jackyf (Eugene V. Lyubimkin) <https://github.com/jackyf>`_. + +* [Breaking] Changed the library name from ``format`` to ``cppformat`` + for consistency with the project name and to avoid potential conflicts + (`#178 <https://github.com/fmtlib/fmt/issues/178>`_). + Thanks to `@jackyf (Eugene V. Lyubimkin) <https://github.com/jackyf>`_. + +* C++ Format is now available in `Debian <https://www.debian.org/>`_ GNU/Linux + (`stretch <https://packages.debian.org/source/stretch/cppformat>`_, + `sid <https://packages.debian.org/source/sid/cppformat>`_) and + derived distributions such as + `Ubuntu <https://launchpad.net/ubuntu/+source/cppformat>`_ 15.10 and later + (`#155 <https://github.com/fmtlib/fmt/issues/155>`_):: + + $ sudo apt-get install libcppformat1-dev + + Thanks to `@jackyf (Eugene V. Lyubimkin) <https://github.com/jackyf>`_. + +* `Packages for Fedora and RHEL <https://admin.fedoraproject.org/pkgdb/package/cppformat/>`_ + are now available. Thanks to Dave Johansen. + +* C++ Format can now be installed via `Homebrew <http://brew.sh/>`_ on OS X + (`#157 <https://github.com/fmtlib/fmt/issues/157>`_):: + + $ brew install cppformat + + Thanks to `@ortho <https://github.com/ortho>`_, Anatoliy Bulukin. + +Documentation +~~~~~~~~~~~~~ + +* Migrated from ReadTheDocs to GitHub Pages for better responsiveness + and reliability + (`#128 <https://github.com/fmtlib/fmt/issues/128>`_). + New documentation address is http://cppformat.github.io/. + + +* Added `Building the documentation + <https://fmt.dev/2.0.0/usage.html#building-the-documentation>`_ + section to the documentation. + +* Documentation build script is now compatible with Python 3 and newer pip versions. + (`#189 <https://github.com/fmtlib/fmt/pull/189>`_, + `#209 <https://github.com/fmtlib/fmt/issues/209>`_). + Thanks to `@JodiTheTigger <https://github.com/JodiTheTigger>`_ and + `@xentec <https://github.com/xentec>`_. + +* Documentation fixes and improvements + (`#36 <https://github.com/fmtlib/fmt/issues/36>`_, + `#75 <https://github.com/fmtlib/fmt/issues/75>`_, + `#125 <https://github.com/fmtlib/fmt/issues/125>`_, + `#160 <https://github.com/fmtlib/fmt/pull/160>`_, + `#161 <https://github.com/fmtlib/fmt/pull/161>`_, + `#162 <https://github.com/fmtlib/fmt/issues/162>`_, + `#165 <https://github.com/fmtlib/fmt/issues/165>`_, + `#210 <https://github.com/fmtlib/fmt/issues/210>`_). + Thanks to `@syohex (Syohei YOSHIDA) <https://github.com/syohex>`_ and + bug reporters. + +* Fixed out-of-tree documentation build + (`#177 <https://github.com/fmtlib/fmt/issues/177>`_). + Thanks to `@jackyf (Eugene V. Lyubimkin) <https://github.com/jackyf>`_. + +Fixes +~~~~~ + +* Fixed ``initializer_list`` detection + (`#136 <https://github.com/fmtlib/fmt/issues/136>`_). + Thanks to `@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_. + +* [Breaking] Fixed formatting of enums with numeric format specifiers in + ``fmt::(s)printf`` + (`#131 <https://github.com/fmtlib/fmt/issues/131>`_, + `#139 <https://github.com/fmtlib/fmt/issues/139>`_): + + .. code:: c++ + + enum { ANSWER = 42 }; + fmt::printf("%d", ANSWER); + + Thanks to `@Naios <https://github.com/Naios>`_. + +* Improved compatibility with old versions of MinGW + (`#129 <https://github.com/fmtlib/fmt/issues/129>`_, + `#130 <https://github.com/fmtlib/fmt/pull/130>`_, + `#132 <https://github.com/fmtlib/fmt/issues/132>`_). + Thanks to `@cstamford (Christopher Stamford) <https://github.com/cstamford>`_. + +* Fixed a compile error on MSVC with disabled exceptions + (`#144 <https://github.com/fmtlib/fmt/issues/144>`_). + +* Added a workaround for broken implementation of variadic templates in MSVC2012 + (`#148 <https://github.com/fmtlib/fmt/issues/148>`_). + +* Placed the anonymous namespace within ``fmt`` namespace for the header-only + configuration + (`#171 <https://github.com/fmtlib/fmt/issues/171>`_). + Thanks to `@alfps (Alf P. Steinbach) <https://github.com/alfps>`_. + +* Fixed issues reported by Coverity Scan + (`#187 <https://github.com/fmtlib/fmt/issues/187>`_, + `#192 <https://github.com/fmtlib/fmt/issues/192>`_). + +* Implemented a workaround for a name lookup bug in MSVC2010 + (`#188 <https://github.com/fmtlib/fmt/issues/188>`_). + +* Fixed compiler warnings + (`#95 <https://github.com/fmtlib/fmt/issues/95>`_, + `#96 <https://github.com/fmtlib/fmt/issues/96>`_, + `#114 <https://github.com/fmtlib/fmt/pull/114>`_, + `#135 <https://github.com/fmtlib/fmt/issues/135>`_, + `#142 <https://github.com/fmtlib/fmt/issues/142>`_, + `#145 <https://github.com/fmtlib/fmt/issues/145>`_, + `#146 <https://github.com/fmtlib/fmt/issues/146>`_, + `#158 <https://github.com/fmtlib/fmt/issues/158>`_, + `#163 <https://github.com/fmtlib/fmt/issues/163>`_, + `#175 <https://github.com/fmtlib/fmt/issues/175>`_, + `#190 <https://github.com/fmtlib/fmt/issues/190>`_, + `#191 <https://github.com/fmtlib/fmt/pull/191>`_, + `#194 <https://github.com/fmtlib/fmt/issues/194>`_, + `#196 <https://github.com/fmtlib/fmt/pull/196>`_, + `#216 <https://github.com/fmtlib/fmt/issues/216>`_, + `#218 <https://github.com/fmtlib/fmt/pull/218>`_, + `#220 <https://github.com/fmtlib/fmt/pull/220>`_, + `#229 <https://github.com/fmtlib/fmt/pull/229>`_, + `#233 <https://github.com/fmtlib/fmt/issues/233>`_, + `#234 <https://github.com/fmtlib/fmt/issues/234>`_, + `#236 <https://github.com/fmtlib/fmt/pull/236>`_, + `#281 <https://github.com/fmtlib/fmt/issues/281>`_, + `#289 <https://github.com/fmtlib/fmt/issues/289>`_). + Thanks to `@seanmiddleditch (Sean Middleditch) <https://github.com/seanmiddleditch>`_, + `@dixlorenz (Dix Lorenz) <https://github.com/dixlorenz>`_, + `@CarterLi (李通洲) <https://github.com/CarterLi>`_, + `@Naios <https://github.com/Naios>`_, + `@fmatthew5876 (Matthew Fioravante) <https://github.com/fmatthew5876>`_, + `@LevskiWeng (Levski Weng) <https://github.com/LevskiWeng>`_, + `@rpopescu <https://github.com/rpopescu>`_, + `@gabime (Gabi Melman) <https://github.com/gabime>`_, + `@cubicool (Jeremy Moles) <https://github.com/cubicool>`_, + `@jkflying (Julian Kent) <https://github.com/jkflying>`_, + `@LogicalKnight (Sean L) <https://github.com/LogicalKnight>`_, + `@inguin (Ingo van Lil) <https://github.com/inguin>`_ and + `@Jopie64 (Johan) <https://github.com/Jopie64>`_. + +* Fixed portability issues (mostly causing test failures) on ARM, ppc64, ppc64le, + s390x and SunOS 5.11 i386 + (`#138 <https://github.com/fmtlib/fmt/issues/138>`_, + `#179 <https://github.com/fmtlib/fmt/issues/179>`_, + `#180 <https://github.com/fmtlib/fmt/issues/180>`_, + `#202 <https://github.com/fmtlib/fmt/issues/202>`_, + `#225 <https://github.com/fmtlib/fmt/issues/225>`_, + `Red Hat Bugzilla Bug 1260297 <https://bugzilla.redhat.com/show_bug.cgi?id=1260297>`_). + Thanks to `@Naios <https://github.com/Naios>`_, + `@jackyf (Eugene V. Lyubimkin) <https://github.com/jackyf>`_ and Dave Johansen. + +* Fixed a name conflict with macro ``free`` defined in + ``crtdbg.h`` when ``_CRTDBG_MAP_ALLOC`` is set + (`#211 <https://github.com/fmtlib/fmt/issues/211>`_). + +* Fixed shared library build on OS X + (`#212 <https://github.com/fmtlib/fmt/pull/212>`_). + Thanks to `@dean0x7d (Dean Moldovan) <https://github.com/dean0x7d>`_. + +* Fixed an overload conflict on MSVC when ``/Zc:wchar_t-`` option is specified + (`#214 <https://github.com/fmtlib/fmt/pull/214>`_). + Thanks to `@slavanap (Vyacheslav Napadovsky) <https://github.com/slavanap>`_. + +* Improved compatibility with MSVC 2008 + (`#236 <https://github.com/fmtlib/fmt/pull/236>`_). + Thanks to `@Jopie64 (Johan) <https://github.com/Jopie64>`_. + +* Improved compatibility with bcc32 + (`#227 <https://github.com/fmtlib/fmt/issues/227>`_). + +* Fixed ``static_assert`` detection on Clang + (`#228 <https://github.com/fmtlib/fmt/pull/228>`_). + Thanks to `@dean0x7d (Dean Moldovan) <https://github.com/dean0x7d>`_. + +1.1.0 - 2015-03-06 +------------------ + +* Added ``BasicArrayWriter``, a class template that provides operations for + formatting and writing data into a fixed-size array + (`#105 <https://github.com/fmtlib/fmt/issues/105>`_ and + `#122 <https://github.com/fmtlib/fmt/issues/122>`_): + + .. code:: c++ + + char buffer[100]; + fmt::ArrayWriter w(buffer); + w.write("The answer is {}", 42); + +* Added `0 A.D. <http://play0ad.com/>`_ and `PenUltima Online (POL) + <http://www.polserver.com/>`_ to the list of notable projects using C++ Format. + +* C++ Format now uses MSVC intrinsics for better formatting performance + (`#115 <https://github.com/fmtlib/fmt/pull/115>`_, + `#116 <https://github.com/fmtlib/fmt/pull/116>`_, + `#118 <https://github.com/fmtlib/fmt/pull/118>`_ and + `#121 <https://github.com/fmtlib/fmt/pull/121>`_). + Previously these optimizations where only used on GCC and Clang. + Thanks to `@CarterLi <https://github.com/CarterLi>`_ and + `@objectx <https://github.com/objectx>`_. + +* CMake install target (`#119 <https://github.com/fmtlib/fmt/pull/119>`_). + Thanks to `@TrentHouliston <https://github.com/TrentHouliston>`_. + + You can now install C++ Format with ``make install`` command. + +* Improved `Biicode <http://www.biicode.com/>`_ support + (`#98 <https://github.com/fmtlib/fmt/pull/98>`_ and + `#104 <https://github.com/fmtlib/fmt/pull/104>`_). Thanks to + `@MariadeAnton <https://github.com/MariadeAnton>`_ and + `@franramirez688 <https://github.com/franramirez688>`_. + +* Improved support for building with `Android NDK + <https://developer.android.com/tools/sdk/ndk/index.html>`_ + (`#107 <https://github.com/fmtlib/fmt/pull/107>`_). + Thanks to `@newnon <https://github.com/newnon>`_. + + The `android-ndk-example <https://github.com/fmtlib/android-ndk-example>`_ + repository provides and example of using C++ Format with Android NDK: + + .. image:: https://raw.githubusercontent.com/fmtlib/android-ndk-example/ + master/screenshot.png + +* Improved documentation of ``SystemError`` and ``WindowsError`` + (`#54 <https://github.com/fmtlib/fmt/issues/54>`_). + +* Various code improvements + (`#110 <https://github.com/fmtlib/fmt/pull/110>`_, + `#111 <https://github.com/fmtlib/fmt/pull/111>`_ + `#112 <https://github.com/fmtlib/fmt/pull/112>`_). + Thanks to `@CarterLi <https://github.com/CarterLi>`_. + +* Improved compile-time errors when formatting wide into narrow strings + (`#117 <https://github.com/fmtlib/fmt/issues/117>`_). + +* Fixed ``BasicWriter::write`` without formatting arguments when C++11 support + is disabled (`#109 <https://github.com/fmtlib/fmt/issues/109>`_). + +* Fixed header-only build on OS X with GCC 4.9 + (`#124 <https://github.com/fmtlib/fmt/issues/124>`_). + +* Fixed packaging issues (`#94 <https://github.com/fmtlib/fmt/issues/94>`_). + +* Added `changelog <https://github.com/fmtlib/fmt/blob/master/ChangeLog.rst>`_ + (`#103 <https://github.com/fmtlib/fmt/issues/103>`_). + +1.0.0 - 2015-02-05 +------------------ + +* Add support for a header-only configuration when ``FMT_HEADER_ONLY`` is + defined before including ``format.h``: + + .. code:: c++ + + #define FMT_HEADER_ONLY + #include "format.h" + +* Compute string length in the constructor of ``BasicStringRef`` + instead of the ``size`` method + (`#79 <https://github.com/fmtlib/fmt/issues/79>`_). + This eliminates size computation for string literals on reasonable optimizing + compilers. + +* Fix formatting of types with overloaded ``operator <<`` for ``std::wostream`` + (`#86 <https://github.com/fmtlib/fmt/issues/86>`_): + + .. code:: c++ + + fmt::format(L"The date is {0}", Date(2012, 12, 9)); + +* Fix linkage of tests on Arch Linux + (`#89 <https://github.com/fmtlib/fmt/issues/89>`_). + +* Allow precision specifier for non-float arguments + (`#90 <https://github.com/fmtlib/fmt/issues/90>`_): + + .. code:: c++ + + fmt::print("{:.3}\n", "Carpet"); // prints "Car" + +* Fix build on Android NDK + (`#93 <https://github.com/fmtlib/fmt/issues/93>`_) + +* Improvements to documentation build procedure. + +* Remove ``FMT_SHARED`` CMake variable in favor of standard `BUILD_SHARED_LIBS + <http://www.cmake.org/cmake/help/v3.0/variable/BUILD_SHARED_LIBS.html>`_. + +* Fix error handling in ``fmt::fprintf``. + +* Fix a number of warnings. + +0.12.0 - 2014-10-25 +------------------- + +* [Breaking] Improved separation between formatting and buffer management. + ``Writer`` is now a base class that cannot be instantiated directly. + The new ``MemoryWriter`` class implements the default buffer management + with small allocations done on stack. So ``fmt::Writer`` should be replaced + with ``fmt::MemoryWriter`` in variable declarations. + + Old code: + + .. code:: c++ + + fmt::Writer w; + + New code: + + .. code:: c++ + + fmt::MemoryWriter w; + + If you pass ``fmt::Writer`` by reference, you can continue to do so: + + .. code:: c++ + + void f(fmt::Writer &w); + + This doesn't affect the formatting API. + +* Support for custom memory allocators + (`#69 <https://github.com/fmtlib/fmt/issues/69>`_) + +* Formatting functions now accept `signed char` and `unsigned char` strings as + arguments (`#73 <https://github.com/fmtlib/fmt/issues/73>`_): + + .. code:: c++ + + auto s = format("GLSL version: {}", glGetString(GL_VERSION)); + +* Reduced code bloat. According to the new `benchmark results + <https://github.com/fmtlib/fmt#compile-time-and-code-bloat>`_, + cppformat is close to ``printf`` and by the order of magnitude better than + Boost Format in terms of compiled code size. + +* Improved appearance of the documentation on mobile by using the `Sphinx + Bootstrap theme <http://ryan-roemer.github.io/sphinx-bootstrap-theme/>`_: + + .. |old| image:: https://cloud.githubusercontent.com/assets/576385/4792130/ + cd256436-5de3-11e4-9a62-c077d0c2b003.png + + .. |new| image:: https://cloud.githubusercontent.com/assets/576385/4792131/ + cd29896c-5de3-11e4-8f59-cac952942bf0.png + + +-------+-------+ + | Old | New | + +-------+-------+ + | |old| | |new| | + +-------+-------+ + +0.11.0 - 2014-08-21 +------------------- + +* Safe printf implementation with a POSIX extension for positional arguments: + + .. code:: c++ + + fmt::printf("Elapsed time: %.2f seconds", 1.23); + fmt::printf("%1$s, %3$d %2$s", weekday, month, day); + +* Arguments of ``char`` type can now be formatted as integers + (Issue `#55 <https://github.com/fmtlib/fmt/issues/55>`_): + + .. code:: c++ + + fmt::format("0x{0:02X}", 'a'); + +* Deprecated parts of the API removed. + +* The library is now built and tested on MinGW with Appveyor in addition to + existing test platforms Linux/GCC, OS X/Clang, Windows/MSVC. + +0.10.0 - 2014-07-01 +------------------- + +**Improved API** + +* All formatting methods are now implemented as variadic functions instead + of using ``operator<<`` for feeding arbitrary arguments into a temporary + formatter object. This works both with C++11 where variadic templates are + used and with older standards where variadic functions are emulated by + providing lightweight wrapper functions defined with the ``FMT_VARIADIC`` + macro. You can use this macro for defining your own portable variadic + functions: + + .. code:: c++ + + void report_error(const char *format, const fmt::ArgList &args) { + fmt::print("Error: {}"); + fmt::print(format, args); + } + FMT_VARIADIC(void, report_error, const char *) + + report_error("file not found: {}", path); + + Apart from a more natural syntax, this also improves performance as there + is no need to construct temporary formatter objects and control arguments' + lifetimes. Because the wrapper functions are very lightweight, this doesn't + cause code bloat even in pre-C++11 mode. + +* Simplified common case of formatting an ``std::string``. Now it requires a + single function call: + + .. code:: c++ + + std::string s = format("The answer is {}.", 42); + + Previously it required 2 function calls: + + .. code:: c++ + + std::string s = str(Format("The answer is {}.") << 42); + + Instead of unsafe ``c_str`` function, ``fmt::Writer`` should be used directly + to bypass creation of ``std::string``: + + .. code:: c++ + + fmt::Writer w; + w.write("The answer is {}.", 42); + w.c_str(); // returns a C string + + This doesn't do dynamic memory allocation for small strings and is less error + prone as the lifetime of the string is the same as for ``std::string::c_str`` + which is well understood (hopefully). + +* Improved consistency in naming functions that are a part of the public API. + Now all public functions are lowercase following the standard library + conventions. Previously it was a combination of lowercase and + CapitalizedWords. + Issue `#50 <https://github.com/fmtlib/fmt/issues/50>`_. + +* Old functions are marked as deprecated and will be removed in the next + release. + +**Other Changes** + +* Experimental support for printf format specifications (work in progress): + + .. code:: c++ + + fmt::printf("The answer is %d.", 42); + std::string s = fmt::sprintf("Look, a %s!", "string"); + +* Support for hexadecimal floating point format specifiers ``a`` and ``A``: + + .. code:: c++ + + print("{:a}", -42.0); // Prints -0x1.5p+5 + print("{:A}", -42.0); // Prints -0X1.5P+5 + +* CMake option ``FMT_SHARED`` that specifies whether to build format as a + shared library (off by default). + +0.9.0 - 2014-05-13 +------------------ + +* More efficient implementation of variadic formatting functions. + +* ``Writer::Format`` now has a variadic overload: + + .. code:: c++ + + Writer out; + out.Format("Look, I'm {}!", "variadic"); + +* For efficiency and consistency with other overloads, variadic overload of + the ``Format`` function now returns ``Writer`` instead of ``std::string``. + Use the ``str`` function to convert it to ``std::string``: + + .. code:: c++ + + std::string s = str(Format("Look, I'm {}!", "variadic")); + +* Replaced formatter actions with output sinks: ``NoAction`` -> ``NullSink``, + ``Write`` -> ``FileSink``, ``ColorWriter`` -> ``ANSITerminalSink``. + This improves naming consistency and shouldn't affect client code unless + these classes are used directly which should be rarely needed. + +* Added ``ThrowSystemError`` function that formats a message and throws + ``SystemError`` containing the formatted message and system-specific error + description. For example, the following code + + .. code:: c++ + + FILE *f = fopen(filename, "r"); + if (!f) + ThrowSystemError(errno, "Failed to open file '{}'") << filename; + + will throw ``SystemError`` exception with description + "Failed to open file '<filename>': No such file or directory" if file + doesn't exist. + +* Support for AppVeyor continuous integration platform. + +* ``Format`` now throws ``SystemError`` in case of I/O errors. + +* Improve test infrastructure. Print functions are now tested by redirecting + the output to a pipe. + +0.8.0 - 2014-04-14 +------------------ + +* Initial release diff --git a/src/fmt/LICENSE.rst b/src/fmt/LICENSE.rst new file mode 100644 index 000000000..f0ec3db4d --- /dev/null +++ b/src/fmt/LICENSE.rst @@ -0,0 +1,27 @@ +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. diff --git a/src/fmt/README.rst b/src/fmt/README.rst new file mode 100644 index 000000000..cc6d7c41a --- /dev/null +++ b/src/fmt/README.rst @@ -0,0 +1,531 @@ +.. image:: https://user-images.githubusercontent.com/ + 576385/156254208-f5b743a9-88cf-439d-b0c0-923d53e8d551.png + :width: 25% + :alt: {fmt} + +.. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg + :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux + +.. image:: https://github.com/fmtlib/fmt/workflows/macos/badge.svg + :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos + +.. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg + :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows + +.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg + :alt: fmt is continuously fuzzed at oss-fuzz + :target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\ + colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\ + Summary&q=proj%3Dfmt&can=1 + +.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg + :alt: Ask questions at StackOverflow with the tag fmt + :target: https://stackoverflow.com/questions/tagged/fmt + +**{fmt}** is an open-source formatting library providing a fast and safe +alternative to C stdio and C++ iostreams. + +If you like this project, please consider donating to one of the funds that +help victims of the war in Ukraine: https://www.stopputin.net/. + +`Documentation <https://fmt.dev>`__ + +`Cheat Sheets <https://hackingcpp.com/cpp/libs/fmt.html>`__ + +Q&A: ask questions on `StackOverflow with the tag fmt +<https://stackoverflow.com/questions/tagged/fmt>`_. + +Try {fmt} in `Compiler Explorer <https://godbolt.org/z/Eq5763>`_. + +Features +-------- + +* Simple `format API <https://fmt.dev/latest/api.html>`_ with positional arguments + for localization +* Implementation of `C++20 std::format + <https://en.cppreference.com/w/cpp/utility/format>`__ +* `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar to Python's + `format <https://docs.python.org/3/library/stdtypes.html#str.format>`_ +* Fast IEEE 754 floating-point formatter with correct rounding, shortness and + round-trip guarantees +* Safe `printf implementation + <https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX + extension for positional arguments +* Extensibility: `support for user-defined types + <https://fmt.dev/latest/api.html#formatting-user-defined-types>`_ +* High performance: faster than common standard library implementations of + ``(s)printf``, iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_ + and `Converting a hundred million integers to strings per second + <http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_ +* Small code size both in terms of source code with the minimum configuration + consisting of just three files, ``core.h``, ``format.h`` and ``format-inl.h``, + and compiled code; see `Compile time and code bloat`_ +* Reliability: the library has an extensive set of `tests + <https://github.com/fmtlib/fmt/tree/master/test>`_ and is `continuously fuzzed + <https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20 + Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1>`_ +* Safety: the library is fully type safe, errors in format strings can be + reported at compile time, automatic memory management prevents buffer overflow + errors +* Ease of use: small self-contained code base, no external dependencies, + permissive MIT `license + <https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_ +* `Portability <https://fmt.dev/latest/index.html#portability>`_ with + consistent output across platforms and support for older compilers +* Clean warning-free codebase even on high warning levels such as + ``-Wall -Wextra -pedantic`` +* Locale-independence by default +* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro + +See the `documentation <https://fmt.dev>`_ for more details. + +Examples +-------- + +**Print to stdout** (`run <https://godbolt.org/z/Tevcjh>`_) + +.. code:: c++ + + #include <fmt/core.h> + + int main() { + fmt::print("Hello, world!\n"); + } + +**Format a string** (`run <https://godbolt.org/z/oK8h33>`_) + +.. code:: c++ + + std::string s = fmt::format("The answer is {}.", 42); + // s == "The answer is 42." + +**Format a string using positional arguments** (`run <https://godbolt.org/z/Yn7Txe>`_) + +.. code:: c++ + + std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy"); + // s == "I'd rather be happy than right." + +**Print chrono durations** (`run <https://godbolt.org/z/K8s4Mc>`_) + +.. code:: c++ + + #include <fmt/chrono.h> + + int main() { + using namespace std::literals::chrono_literals; + fmt::print("Default format: {} {}\n", 42s, 100ms); + fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s); + } + +Output:: + + Default format: 42s 100ms + strftime-like format: 03:15:30 + +**Print a container** (`run <https://godbolt.org/z/MxM1YqjE7>`_) + +.. code:: c++ + + #include <vector> + #include <fmt/ranges.h> + + int main() { + std::vector<int> v = {1, 2, 3}; + fmt::print("{}\n", v); + } + +Output:: + + [1, 2, 3] + +**Check a format string at compile time** + +.. code:: c++ + + std::string s = fmt::format("{:d}", "I am not a number"); + +This gives a compile-time error in C++20 because ``d`` is an invalid format +specifier for a string. + +**Write a file from a single thread** + +.. code:: c++ + + #include <fmt/os.h> + + int main() { + auto out = fmt::output_file("guide.txt"); + out.print("Don't {}", "Panic"); + } + +This can be `5 to 9 times faster than fprintf +<http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html>`_. + +**Print with colors and text styles** + +.. code:: c++ + + #include <fmt/color.h> + + int main() { + fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, + "Hello, {}!\n", "world"); + fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) | + fmt::emphasis::underline, "Hello, {}!\n", "мир"); + fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, + "Hello, {}!\n", "世界"); + } + +Output on a modern terminal: + +.. image:: https://user-images.githubusercontent.com/ + 576385/88485597-d312f600-cf2b-11ea-9cbe-61f535a86e28.png + +Benchmarks +---------- + +Speed tests +~~~~~~~~~~~ + +================= ============= =========== +Library Method Run Time, s +================= ============= =========== +libc printf 1.04 +libc++ std::ostream 3.05 +{fmt} 6.1.1 fmt::print 0.75 +Boost Format 1.67 boost::format 7.24 +Folly Format folly::format 2.23 +================= ============= =========== + +{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``. + +The above results were generated by building ``tinyformat_test.cpp`` on macOS +10.14.6 with ``clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT``, and taking the +best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` +or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for +further details refer to the `source +<https://github.com/fmtlib/format-benchmark/blob/master/src/tinyformat-test.cc>`_. + +{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on +floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_) +and faster than `double-conversion <https://github.com/google/double-conversion>`_ and +`ryu <https://github.com/ulfjack/ryu>`_: + +.. image:: https://user-images.githubusercontent.com/576385/ + 95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png + :target: https://fmt.dev/unknown_mac64_clang12.0.html + +Compile time and code bloat +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The script `bloat-test.py +<https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py>`_ +from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_ +tests compile time and code bloat for nontrivial projects. +It generates 100 translation units and uses ``printf()`` or its alternative +five times in each to simulate a medium sized project. The resulting +executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42), +macOS Sierra, best of three) is shown in the following tables. + +**Optimized build (-O3)** + +============= =============== ==================== ================== +Method Compile Time, s Executable size, KiB Stripped size, KiB +============= =============== ==================== ================== +printf 2.6 29 26 +printf+string 16.4 29 26 +iostreams 31.1 59 55 +{fmt} 19.0 37 34 +Boost Format 91.9 226 203 +Folly Format 115.7 101 88 +============= =============== ==================== ================== + +As you can see, {fmt} has 60% less overhead in terms of resulting binary code +size compared to iostreams and comes pretty close to ``printf``. Boost Format +and Folly Format have the largest overheads. + +``printf+string`` is the same as ``printf`` but with extra ``<string>`` +include to measure the overhead of the latter. + +**Non-optimized build** + +============= =============== ==================== ================== +Method Compile Time, s Executable size, KiB Stripped size, KiB +============= =============== ==================== ================== +printf 2.2 33 30 +printf+string 16.0 33 30 +iostreams 28.3 56 52 +{fmt} 18.2 59 50 +Boost Format 54.1 365 303 +Folly Format 79.9 445 430 +============= =============== ==================== ================== + +``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared libraries to +compare formatting function overhead only. Boost Format is a +header-only library so it doesn't provide any linkage options. + +Running the tests +~~~~~~~~~~~~~~~~~ + +Please refer to `Building the library`__ for the instructions on how to build +the library and run the unit tests. + +__ https://fmt.dev/latest/usage.html#building-the-library + +Benchmarks reside in a separate repository, +`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_, +so to run the benchmarks you first need to clone this repository and +generate Makefiles with CMake:: + + $ git clone --recursive https://github.com/fmtlib/format-benchmark.git + $ cd format-benchmark + $ cmake . + +Then you can run the speed test:: + + $ make speed-test + +or the bloat test:: + + $ make bloat-test + +Migrating code +-------------- + +`clang-tidy-fmt <https://github.com/mikecrowe/clang-tidy-fmt>`_ provides clang +tidy checks for converting occurrences of ``printf`` and ``fprintf`` to +``fmt::print``. + +Projects using this library +--------------------------- + +* `0 A.D. <https://play0ad.com/>`_: a free, open-source, cross-platform + real-time strategy game + +* `2GIS <https://2gis.ru/>`_: free business listings with a city map + +* `AMPL/MP <https://github.com/ampl/mp>`_: + an open-source library for mathematical programming + +* `Aseprite <https://github.com/aseprite/aseprite>`_: + animated sprite editor & pixel art tool + +* `AvioBook <https://www.aviobook.aero/en>`_: a comprehensive aircraft + operations suite + +* `Blizzard Battle.net <https://battle.net/>`_: an online gaming platform + +* `Celestia <https://celestia.space/>`_: real-time 3D visualization of space + +* `Ceph <https://ceph.com/>`_: a scalable distributed storage system + +* `ccache <https://ccache.dev/>`_: a compiler cache + +* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database + management system + +* `CUAUV <https://cuauv.org/>`_: Cornell University's autonomous underwater + vehicle + +* `Drake <https://drake.mit.edu/>`_: a planning, control, and analysis toolbox + for nonlinear dynamical systems (MIT) + +* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus + (Lyft) + +* `FiveM <https://fivem.net/>`_: a modification framework for GTA V + +* `fmtlog <https://github.com/MengRao/fmtlog>`_: a performant fmtlib-style + logging library with latency in nanoseconds + +* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library + +* `GemRB <https://gemrb.org/>`_: a portable open-source implementation of + Bioware’s Infinity Engine + +* `Grand Mountain Adventure + <https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/>`_: + a beautiful open-world ski & snowboarding game + +* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_: + Player vs Player Gaming Network with tweaks + +* `KBEngine <https://github.com/kbengine/kbengine>`_: an open-source MMOG server + engine + +* `Keypirinha <https://keypirinha.com/>`_: a semantic launcher for Windows + +* `Kodi <https://kodi.tv/>`_ (formerly xbmc): home theater software + +* `Knuth <https://kth.cash/>`_: high-performance Bitcoin full-node + +* `Microsoft Verona <https://github.com/microsoft/verona>`_: + research programming language for concurrent ownership + +* `MongoDB <https://mongodb.com/>`_: distributed document database + +* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: a small tool to + generate randomized datasets + +* `OpenSpace <https://openspaceproject.com/>`_: an open-source + astrovisualization framework + +* `PenUltima Online (POL) <https://www.polserver.com/>`_: + an MMO server, compatible with most Ultima Online clients + +* `PyTorch <https://github.com/pytorch/pytorch>`_: an open-source machine + learning library + +* `quasardb <https://www.quasardb.net/>`_: a distributed, high-performance, + associative database + +* `Quill <https://github.com/odygrd/quill>`_: asynchronous low-latency logging library + +* `QKW <https://github.com/ravijanjam/qkw>`_: generalizing aliasing to simplify + navigation, and executing complex multi-line terminal command sequences + +* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: a Redis cluster + proxy + +* `redpanda <https://vectorized.io/redpanda>`_: a 10x faster Kafka® replacement + for mission critical systems written in C++ + +* `rpclib <http://rpclib.net/>`_: a modern C++ msgpack-RPC server and client + library + +* `Salesforce Analytics Cloud + <https://www.salesforce.com/analytics-cloud/overview/>`_: + business intelligence software + +* `Scylla <https://www.scylladb.com/>`_: a Cassandra-compatible NoSQL data store + that can handle 1 million transactions per second on a single server + +* `Seastar <http://www.seastar-project.org/>`_: an advanced, open-source C++ + framework for high-performance server applications on modern hardware + +* `spdlog <https://github.com/gabime/spdlog>`_: super fast C++ logging library + +* `Stellar <https://www.stellar.org/>`_: financial platform + +* `Touch Surgery <https://www.touchsurgery.com/>`_: surgery simulator + +* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: open-source + MMORPG framework + +* `Windows Terminal <https://github.com/microsoft/terminal>`_: the new Windows + terminal + +`More... <https://github.com/search?q=fmtlib&type=Code>`_ + +If you are aware of other projects using this library, please let me know +by `email <mailto:victor.zverovich@gmail.com>`_ or by submitting an +`issue <https://github.com/fmtlib/fmt/issues>`_. + +Motivation +---------- + +So why yet another formatting library? + +There are plenty of methods for doing this task, from standard ones like +the printf family of function and iostreams to Boost Format and FastFormat +libraries. The reason for creating a new library is that every existing +solution that I found either had serious issues or didn't provide +all the features I needed. + +printf +~~~~~~ + +The good thing about ``printf`` is that it is pretty fast and readily available +being a part of the C standard library. The main drawback is that it +doesn't support user-defined types. ``printf`` also has safety issues although +they are somewhat mitigated with `__attribute__ ((format (printf, ...)) +<https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC. +There is a POSIX extension that adds positional arguments required for +`i18n <https://en.wikipedia.org/wiki/Internationalization_and_localization>`_ +to ``printf`` but it is not a part of C99 and may not be available on some +platforms. + +iostreams +~~~~~~~~~ + +The main issue with iostreams is best illustrated with an example: + +.. code:: c++ + + std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n"; + +which is a lot of typing compared to printf: + +.. code:: c++ + + printf("%.2f\n", 1.23456); + +Matthew Wilson, the author of FastFormat, called this "chevron hell". iostreams +don't support positional arguments by design. + +The good part is that iostreams support user-defined types and are safe although +error handling is awkward. + +Boost Format +~~~~~~~~~~~~ + +This is a very powerful library which supports both ``printf``-like format +strings and positional arguments. Its main drawback is performance. According to +various benchmarks, it is much slower than other methods considered here. Boost +Format also has excessive build times and severe code bloat issues (see +`Benchmarks`_). + +FastFormat +~~~~~~~~~~ + +This is an interesting library which is fast, safe and has positional arguments. +However, it has significant limitations, citing its author: + + Three features that have no hope of being accommodated within the + current design are: + + * Leading zeros (or any other non-space padding) + * Octal/hexadecimal encoding + * Runtime width/alignment specification + +It is also quite big and has a heavy dependency, STLSoft, which might be too +restrictive for using it in some projects. + +Boost Spirit.Karma +~~~~~~~~~~~~~~~~~~ + +This is not really a formatting library but I decided to include it here for +completeness. As iostreams, it suffers from the problem of mixing verbatim text +with arguments. The library is pretty fast, but slower on integer formatting +than ``fmt::format_to`` with format string compilation on Karma's own benchmark, +see `Converting a hundred million integers to strings per second +<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_. + +License +------- + +{fmt} is distributed under the MIT `license +<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_. + +Documentation License +--------------------- + +The `Format String Syntax <https://fmt.dev/latest/syntax.html>`_ +section in the documentation is based on the one from Python `string module +documentation <https://docs.python.org/3/library/string.html#module-string>`_. +For this reason the documentation is distributed under the Python Software +Foundation license available in `doc/python-license.txt +<https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt>`_. +It only applies if you distribute the documentation of {fmt}. + +Maintainers +----------- + +The {fmt} library is maintained by Victor Zverovich (`vitaut +<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan +<https://github.com/foonathan>`_) with contributions from many other people. +See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and +`Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names. +Let us know if your contribution is not listed or mentioned incorrectly and +we'll make it right. diff --git a/src/fmt/doc/CMakeLists.txt b/src/fmt/doc/CMakeLists.txt new file mode 100644 index 000000000..fd9f4d1e4 --- /dev/null +++ b/src/fmt/doc/CMakeLists.txt @@ -0,0 +1,26 @@ +find_program(DOXYGEN doxygen + PATHS "$ENV{ProgramFiles}/doxygen/bin" + "$ENV{ProgramFiles\(x86\)}/doxygen/bin") +if (NOT DOXYGEN) + message(STATUS "Target 'doc' disabled (requires doxygen)") + return () +endif () + +# Find the Python interpreter and set the PYTHON_EXECUTABLE variable. +if (CMAKE_VERSION VERSION_LESS 3.12) + # This logic is deprecated in CMake after 3.12. + find_package(PythonInterp QUIET REQUIRED) +else () + find_package(Python QUIET REQUIRED) + set(PYTHON_EXECUTABLE ${Python_EXECUTABLE}) +endif () + +add_custom_target(doc + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/build.py + ${FMT_VERSION} + SOURCES api.rst syntax.rst usage.rst build.py conf.py _templates/layout.html) + +include(GNUInstallDirs) +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/ + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt OPTIONAL + PATTERN ".doctrees" EXCLUDE) diff --git a/src/fmt/doc/_static/bootstrap.min.js b/src/fmt/doc/_static/bootstrap.min.js new file mode 100644 index 000000000..c8f82e592 --- /dev/null +++ b/src/fmt/doc/_static/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.3.4 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.4",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.4",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.4",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.4",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.4",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('<div class="dropdown-backdrop"/>').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(b){if(/(38|40|27|32)/.test(b.which)&&!/input|textarea/i.test(b.target.tagName)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var e=c(d),g=e.hasClass("open");if(!g&&27!=b.which||g&&27==b.which)return 27==b.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find('[role="menu"]'+h+', [role="listbox"]'+h);if(i.length){var j=i.index(b.target);38==b.which&&j>0&&j--,40==b.which&&j<i.length-1&&j++,~j||(j=0),i.eq(j).trigger("focus")}}}};var h=a.fn.dropdown;a.fn.dropdown=d,a.fn.dropdown.Constructor=g,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=h,this},a(document).on("click.bs.dropdown.data-api",b).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",f,g.prototype.toggle).on("keydown.bs.dropdown.data-api",f,g.prototype.keydown).on("keydown.bs.dropdown.data-api",'[role="menu"]',g.prototype.keydown).on("keydown.bs.dropdown.data-api",'[role="listbox"]',g.prototype.keydown)}(jQuery),+function(a){"use strict";function b(b,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},c.DEFAULTS,e.data(),"object"==typeof b&&b);f||e.data("bs.modal",f=new c(this,g)),"string"==typeof b?f[b](d):g.show&&f.show(d)})}var c=function(b,c){this.options=c,this.$body=a(document.body),this.$element=a(b),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};c.VERSION="3.3.4",c.TRANSITION_DURATION=300,c.BACKDROP_TRANSITION_DURATION=150,c.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},c.prototype.toggle=function(a){return this.isShown?this.hide():this.show(a)},c.prototype.show=function(b){var d=this,e=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){d.$element.one("mouseup.dismiss.bs.modal",function(b){a(b.target).is(d.$element)&&(d.ignoreBackdropClick=!0)})}),this.backdrop(function(){var e=a.support.transition&&d.$element.hasClass("fade");d.$element.parent().length||d.$element.appendTo(d.$body),d.$element.show().scrollTop(0),d.adjustDialog(),e&&d.$element[0].offsetWidth,d.$element.addClass("in").attr("aria-hidden",!1),d.enforceFocus();var f=a.Event("shown.bs.modal",{relatedTarget:b});e?d.$dialog.one("bsTransitionEnd",function(){d.$element.trigger("focus").trigger(f)}).emulateTransitionEnd(c.TRANSITION_DURATION):d.$element.trigger("focus").trigger(f)}))},c.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").attr("aria-hidden",!0).off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",a.proxy(this.hideModal,this)).emulateTransitionEnd(c.TRANSITION_DURATION):this.hideModal())},c.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.trigger("focus")},this))},c.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},c.prototype.resize=function(){this.isShown?a(window).on("resize.bs.modal",a.proxy(this.handleUpdate,this)):a(window).off("resize.bs.modal")},c.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.$body.removeClass("modal-open"),a.resetAdjustments(),a.resetScrollbar(),a.$element.trigger("hidden.bs.modal")})},c.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},c.prototype.backdrop=function(b){var d=this,e=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var f=a.support.transition&&e;if(this.$backdrop=a('<div class="modal-backdrop '+e+'" />').appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.adjustDialog()},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth<a,this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"",this.bodyIsOverflowing&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad)},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.init("tooltip",a,b)};c.VERSION="3.3.4",c.TRANSITION_DURATION=150,c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(this.options.viewport.selector||this.options.viewport),this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c&&c.$tip&&c.$tip.is(":visible")?void(c.hoverState="in"):(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.options.container?a(this.options.container):this.$element.parent(),p=this.getPosition(o);h="bottom"==h&&k.bottom+m>p.bottom?"top":"top"==h&&k.top-m<p.top?"bottom":"right"==h&&k.right+l>p.width?"left":"left"==h&&k.left-l<p.left?"right":h,f.removeClass(n).addClass(h)}var q=this.getCalculatedOffset(h,k,l,m);this.applyPlacement(q,h);var r=function(){var a=e.hoverState;e.$element.trigger("shown.bs."+e.type),e.hoverState=null,"out"==a&&e.leave(e)};a.support.transition&&this.$tip.hasClass("fade")?f.one("bsTransitionEnd",r).emulateTransitionEnd(c.TRANSITION_DURATION):r()}},c.prototype.applyPlacement=function(b,c){var d=this.tip(),e=d[0].offsetWidth,f=d[0].offsetHeight,g=parseInt(d.css("margin-top"),10),h=parseInt(d.css("margin-left"),10);isNaN(g)&&(g=0),isNaN(h)&&(h=0),b.top=b.top+g,b.left=b.left+h,a.offset.setOffset(d[0],a.extend({using:function(a){d.css({top:Math.round(a.top),left:Math.round(a.left)})}},b),0),d.addClass("in");var i=d[0].offsetWidth,j=d[0].offsetHeight;"top"==c&&j!=f&&(b.top=b.top+f-j);var k=this.getViewportAdjustedDelta(c,b,i,j);k.left?b.left+=k.left:b.top+=k.top;var l=/top|bottom/.test(c),m=l?2*k.left-e+i:2*k.top-f+j,n=l?"offsetWidth":"offsetHeight";d.offset(b),this.replaceArrow(m,d[0][n],l)},c.prototype.replaceArrow=function(a,b,c){this.arrow().css(c?"left":"top",50*(1-a/b)+"%").css(c?"top":"left","")},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},c.prototype.hide=function(b){function d(){"in"!=e.hoverState&&f.detach(),e.$element.removeAttr("aria-describedby").trigger("hidden.bs."+e.type),b&&b()}var e=this,f=a(this.$tip),g=a.Event("hide.bs."+this.type);return this.$element.trigger(g),g.isDefaultPrevented()?void 0:(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",d).emulateTransitionEnd(c.TRANSITION_DURATION):d(),this.hoverState=null,this)},c.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},c.prototype.hasContent=function(){return this.getTitle()},c.prototype.getPosition=function(b){b=b||this.$element;var c=b[0],d="BODY"==c.tagName,e=c.getBoundingClientRect();null==e.width&&(e=a.extend({},e,{width:e.right-e.left,height:e.bottom-e.top}));var f=d?{top:0,left:0}:b.offset(),g={scroll:d?document.documentElement.scrollTop||document.body.scrollTop:b.scrollTop()},h=d?{width:a(window).width(),height:a(window).height()}:null;return a.extend({},e,g,h,f)},c.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},c.prototype.getViewportAdjustedDelta=function(a,b,c,d){var e={top:0,left:0};if(!this.$viewport)return e;var f=this.options.viewport&&this.options.viewport.padding||0,g=this.getPosition(this.$viewport);if(/right|left/.test(a)){var h=b.top-f-g.scroll,i=b.top+f-g.scroll+d;h<g.top?e.top=g.top-h:i>g.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;j<g.left?e.left=g.left-j:k>g.width&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type)})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.4",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.4",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b<e[0])return this.activeTarget=null,this.clear();for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(void 0===e[a+1]||b<e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.4",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){ +var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.4",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=a(document.body).height();"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
\ No newline at end of file diff --git a/src/fmt/doc/_static/breathe.css b/src/fmt/doc/_static/breathe.css new file mode 100644 index 000000000..2a1534b0a --- /dev/null +++ b/src/fmt/doc/_static/breathe.css @@ -0,0 +1,28 @@ + +/* -- breathe specific styles ----------------------------------------------- */ + +/* So enum value descriptions are displayed inline to the item */ +.breatheenumvalues li tt + p { + display: inline; +} + +/* So parameter descriptions are displayed inline to the item */ +.breatheparameterlist li tt + p { + display: inline; +} + +.container .breathe-sectiondef { + width: inherit; +} + +.github-btn { + border: 0; + overflow: hidden; +} + +.jumbotron { + background-size: 100% 4px; + background-repeat: repeat-y; + color: white; + text-align: center; +} diff --git a/src/fmt/doc/_static/fonts/glyphicons-halflings-regular.eot b/src/fmt/doc/_static/fonts/glyphicons-halflings-regular.eot Binary files differnew file mode 100644 index 000000000..4a4ca865d --- /dev/null +++ b/src/fmt/doc/_static/fonts/glyphicons-halflings-regular.eot diff --git a/src/fmt/doc/_static/fonts/glyphicons-halflings-regular.svg b/src/fmt/doc/_static/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 000000000..e3e2dc739 --- /dev/null +++ b/src/fmt/doc/_static/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,229 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata></metadata> +<defs> +<font id="glyphicons_halflingsregular" horiz-adv-x="1200" > +<font-face units-per-em="1200" ascent="960" descent="-240" /> +<missing-glyph horiz-adv-x="500" /> +<glyph /> +<glyph /> +<glyph unicode="
" /> +<glyph unicode=" " /> +<glyph unicode="*" d="M100 500v200h259l-183 183l141 141l183 -183v259h200v-259l183 183l141 -141l-183 -183h259v-200h-259l183 -183l-141 -141l-183 183v-259h-200v259l-183 -183l-141 141l183 183h-259z" /> +<glyph unicode="+" d="M0 400v300h400v400h300v-400h400v-300h-400v-400h-300v400h-400z" /> +<glyph unicode=" " /> +<glyph unicode=" " horiz-adv-x="652" /> +<glyph unicode=" " horiz-adv-x="1304" /> +<glyph unicode=" " horiz-adv-x="652" /> +<glyph unicode=" " horiz-adv-x="1304" /> +<glyph unicode=" " horiz-adv-x="434" /> +<glyph unicode=" " horiz-adv-x="326" /> +<glyph unicode=" " horiz-adv-x="217" /> +<glyph unicode=" " horiz-adv-x="217" /> +<glyph unicode=" " horiz-adv-x="163" /> +<glyph unicode=" " horiz-adv-x="260" /> +<glyph unicode=" " horiz-adv-x="72" /> +<glyph unicode=" " horiz-adv-x="260" /> +<glyph unicode=" " horiz-adv-x="326" /> +<glyph unicode="€" d="M100 500l100 100h113q0 47 5 100h-218l100 100h135q37 167 112 257q117 141 297 141q242 0 354 -189q60 -103 66 -209h-181q0 55 -25.5 99t-63.5 68t-75 36.5t-67 12.5q-24 0 -52.5 -10t-62.5 -32t-65.5 -67t-50.5 -107h379l-100 -100h-300q-6 -46 -6 -100h406l-100 -100 h-300q9 -74 33 -132t52.5 -91t62 -54.5t59 -29t46.5 -7.5q29 0 66 13t75 37t63.5 67.5t25.5 96.5h174q-31 -172 -128 -278q-107 -117 -274 -117q-205 0 -324 158q-36 46 -69 131.5t-45 205.5h-217z" /> +<glyph unicode="−" d="M200 400h900v300h-900v-300z" /> +<glyph unicode="◼" horiz-adv-x="500" d="M0 0z" /> +<glyph unicode="☁" d="M-14 494q0 -80 56.5 -137t135.5 -57h750q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5z" /> +<glyph unicode="✉" d="M0 100l400 400l200 -200l200 200l400 -400h-1200zM0 300v600l300 -300zM0 1100l600 -603l600 603h-1200zM900 600l300 300v-600z" /> +<glyph unicode="✏" d="M-13 -13l333 112l-223 223zM187 403l214 -214l614 614l-214 214zM887 1103l214 -214l99 92q13 13 13 32.5t-13 33.5l-153 153q-15 13 -33 13t-33 -13z" /> +<glyph unicode="" d="M0 1200h1200l-500 -550v-550h300v-100h-800v100h300v550z" /> +<glyph unicode="" d="M14 84q18 -55 86 -75.5t147 5.5q65 21 109 69t44 90v606l600 155v-521q-64 16 -138 -7q-79 -26 -122.5 -83t-25.5 -111q18 -55 86 -75.5t147 4.5q70 23 111.5 63.5t41.5 95.5v881q0 10 -7 15.5t-17 2.5l-752 -193q-10 -3 -17 -12.5t-7 -19.5v-689q-64 17 -138 -7 q-79 -25 -122.5 -82t-25.5 -112z" /> +<glyph unicode="" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233z" /> +<glyph unicode="" d="M100 784q0 64 28 123t73 100.5t104.5 64t119 20.5t120 -38.5t104.5 -104.5q48 69 109.5 105t121.5 38t118.5 -20.5t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-149.5 152.5t-126.5 127.5 t-94 124.5t-33.5 117.5z" /> +<glyph unicode="" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1z" /> +<glyph unicode="" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1zM237 700l196 -142l-73 -226l192 140l195 -141l-74 229l193 140h-235l-77 211l-78 -211h-239z" /> +<glyph unicode="" d="M0 0v143l400 257v100q-37 0 -68.5 74.5t-31.5 125.5v200q0 124 88 212t212 88t212 -88t88 -212v-200q0 -51 -31.5 -125.5t-68.5 -74.5v-100l400 -257v-143h-1200z" /> +<glyph unicode="" d="M0 0v1100h1200v-1100h-1200zM100 100h100v100h-100v-100zM100 300h100v100h-100v-100zM100 500h100v100h-100v-100zM100 700h100v100h-100v-100zM100 900h100v100h-100v-100zM300 100h600v400h-600v-400zM300 600h600v400h-600v-400zM1000 100h100v100h-100v-100z M1000 300h100v100h-100v-100zM1000 500h100v100h-100v-100zM1000 700h100v100h-100v-100zM1000 900h100v100h-100v-100z" /> +<glyph unicode="" d="M0 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM0 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5zM600 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM600 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5z" /> +<glyph unicode="" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 450v200q0 21 14.5 35.5t35.5 14.5h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5z" /> +<glyph unicode="" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v200q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5 t-14.5 -35.5v-200zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5z" /> +<glyph unicode="" d="M29 454l419 -420l818 820l-212 212l-607 -607l-206 207z" /> +<glyph unicode="" d="M106 318l282 282l-282 282l212 212l282 -282l282 282l212 -212l-282 -282l282 -282l-212 -212l-282 282l-282 -282z" /> +<glyph unicode="" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233zM300 600v200h100v100h200v-100h100v-200h-100v-100h-200v100h-100z" /> +<glyph unicode="" d="M23 694q0 200 142 342t342 142t342 -142t142 -342q0 -141 -78 -262l300 -299q7 -7 7 -18t-7 -18l-109 -109q-8 -8 -18 -8t-18 8l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 694q0 -136 97 -233t234 -97t233.5 97t96.5 233t-96.5 233t-233.5 97t-234 -97 t-97 -233zM300 601h400v200h-400v-200z" /> +<glyph unicode="" d="M23 600q0 183 105 331t272 210v-166q-103 -55 -165 -155t-62 -220q0 -177 125 -302t302 -125t302 125t125 302q0 120 -62 220t-165 155v166q167 -62 272 -210t105 -331q0 -118 -45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5 zM500 750q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v400q0 21 -14.5 35.5t-35.5 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-400z" /> +<glyph unicode="" d="M100 1h200v300h-200v-300zM400 1v500h200v-500h-200zM700 1v800h200v-800h-200zM1000 1v1200h200v-1200h-200z" /> +<glyph unicode="" d="M26 601q0 -33 6 -74l151 -38l2 -6q14 -49 38 -93l3 -5l-80 -134q45 -59 105 -105l133 81l5 -3q45 -26 94 -39l5 -2l38 -151q40 -5 74 -5q27 0 74 5l38 151l6 2q46 13 93 39l5 3l134 -81q56 44 104 105l-80 134l3 5q24 44 39 93l1 6l152 38q5 40 5 74q0 28 -5 73l-152 38 l-1 6q-16 51 -39 93l-3 5l80 134q-44 58 -104 105l-134 -81l-5 3q-45 25 -93 39l-6 1l-38 152q-40 5 -74 5q-27 0 -74 -5l-38 -152l-5 -1q-50 -14 -94 -39l-5 -3l-133 81q-59 -47 -105 -105l80 -134l-3 -5q-25 -47 -38 -93l-2 -6l-151 -38q-6 -48 -6 -73zM385 601 q0 88 63 151t152 63t152 -63t63 -151q0 -89 -63 -152t-152 -63t-152 63t-63 152z" /> +<glyph unicode="" d="M100 1025v50q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-50q0 -11 -7 -18t-18 -7h-1050q-11 0 -18 7t-7 18zM200 100v800h900v-800q0 -41 -29.5 -71t-70.5 -30h-700q-41 0 -70.5 30 t-29.5 71zM300 100h100v700h-100v-700zM500 100h100v700h-100v-700zM500 1100h300v100h-300v-100zM700 100h100v700h-100v-700zM900 100h100v700h-100v-700z" /> +<glyph unicode="" d="M1 601l656 644l644 -644h-200v-600h-300v400h-300v-400h-300v600h-200z" /> +<glyph unicode="" d="M100 25v1150q0 11 7 18t18 7h475v-500h400v-675q0 -11 -7 -18t-18 -7h-850q-11 0 -18 7t-7 18zM700 800v300l300 -300h-300z" /> +<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 500v400h100 v-300h200v-100h-300z" /> +<glyph unicode="" d="M-100 0l431 1200h209l-21 -300h162l-20 300h208l431 -1200h-538l-41 400h-242l-40 -400h-539zM488 500h224l-27 300h-170z" /> +<glyph unicode="" d="M0 0v400h490l-290 300h200v500h300v-500h200l-290 -300h490v-400h-1100zM813 200h175v100h-175v-100z" /> +<glyph unicode="" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM188 600q0 -170 121 -291t291 -121t291 121t121 291t-121 291t-291 121 t-291 -121t-121 -291zM350 600h150v300h200v-300h150l-250 -300z" /> +<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM350 600l250 300 l250 -300h-150v-300h-200v300h-150z" /> +<glyph unicode="" d="M0 25v475l200 700h800l199 -700l1 -475q0 -11 -7 -18t-18 -7h-1150q-11 0 -18 7t-7 18zM200 500h200l50 -200h300l50 200h200l-97 500h-606z" /> +<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 397v401 l297 -200z" /> +<glyph unicode="" d="M23 600q0 -118 45.5 -224.5t123 -184t184 -123t224.5 -45.5t224.5 45.5t184 123t123 184t45.5 224.5h-150q0 -177 -125 -302t-302 -125t-302 125t-125 302t125 302t302 125q136 0 246 -81l-146 -146h400v400l-145 -145q-157 122 -355 122q-118 0 -224.5 -45.5t-184 -123 t-123 -184t-45.5 -224.5z" /> +<glyph unicode="" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5q198 0 355 -122l145 145v-400h-400l147 147q-112 80 -247 80q-177 0 -302 -125t-125 -302h-150zM100 0v400h400l-147 -147q112 -80 247 -80q177 0 302 125t125 302h150q0 -118 -45.5 -224.5t-123 -184t-184 -123 t-224.5 -45.5q-198 0 -355 122z" /> +<glyph unicode="" d="M100 0h1100v1200h-1100v-1200zM200 100v900h900v-900h-900zM300 200v100h100v-100h-100zM300 400v100h100v-100h-100zM300 600v100h100v-100h-100zM300 800v100h100v-100h-100zM500 200h500v100h-500v-100zM500 400v100h500v-100h-500zM500 600v100h500v-100h-500z M500 800v100h500v-100h-500z" /> +<glyph unicode="" d="M0 100v600q0 41 29.5 70.5t70.5 29.5h100v200q0 82 59 141t141 59h300q82 0 141 -59t59 -141v-200h100q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-900q-41 0 -70.5 29.5t-29.5 70.5zM400 800h300v150q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-150z" /> +<glyph unicode="" d="M100 0v1100h100v-1100h-100zM300 400q60 60 127.5 84t127.5 17.5t122 -23t119 -30t110 -11t103 42t91 120.5v500q-40 -81 -101.5 -115.5t-127.5 -29.5t-138 25t-139.5 40t-125.5 25t-103 -29.5t-65 -115.5v-500z" /> +<glyph unicode="" d="M0 275q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 127 70.5 231.5t184.5 161.5t245 57t245 -57t184.5 -161.5t70.5 -231.5v-300q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 116 -49.5 227t-131 192.5t-192.5 131t-227 49.5t-227 -49.5t-192.5 -131t-131 -192.5 t-49.5 -227v-300zM200 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14zM800 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14z" /> +<glyph unicode="" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM688 459l141 141l-141 141l71 71l141 -141l141 141l71 -71l-141 -141l141 -141l-71 -71l-141 141l-141 -141z" /> +<glyph unicode="" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM700 857l69 53q111 -135 111 -310q0 -169 -106 -302l-67 54q86 110 86 248q0 146 -93 257z" /> +<glyph unicode="" d="M0 401v400h300l300 200v-800l-300 200h-300zM702 858l69 53q111 -135 111 -310q0 -170 -106 -303l-67 55q86 110 86 248q0 145 -93 257zM889 951l7 -8q123 -151 123 -344q0 -189 -119 -339l-7 -8l81 -66l6 8q142 178 142 405q0 230 -144 408l-6 8z" /> +<glyph unicode="" d="M0 0h500v500h-200v100h-100v-100h-200v-500zM0 600h100v100h400v100h100v100h-100v300h-500v-600zM100 100v300h300v-300h-300zM100 800v300h300v-300h-300zM200 200v100h100v-100h-100zM200 900h100v100h-100v-100zM500 500v100h300v-300h200v-100h-100v-100h-200v100 h-100v100h100v200h-200zM600 0v100h100v-100h-100zM600 1000h100v-300h200v-300h300v200h-200v100h200v500h-600v-200zM800 800v300h300v-300h-300zM900 0v100h300v-100h-300zM900 900v100h100v-100h-100zM1100 200v100h100v-100h-100z" /> +<glyph unicode="" d="M0 200h100v1000h-100v-1000zM100 0v100h300v-100h-300zM200 200v1000h100v-1000h-100zM500 0v91h100v-91h-100zM500 200v1000h200v-1000h-200zM700 0v91h100v-91h-100zM800 200v1000h100v-1000h-100zM900 0v91h200v-91h-200zM1000 200v1000h200v-1000h-200z" /> +<glyph unicode="" d="M0 700l1 475q0 10 7.5 17.5t17.5 7.5h474l700 -700l-500 -500zM148 953q0 -42 29 -71q30 -30 71.5 -30t71.5 30q29 29 29 71t-29 71q-30 30 -71.5 30t-71.5 -30q-29 -29 -29 -71z" /> +<glyph unicode="" d="M1 700l1 475q0 11 7 18t18 7h474l700 -700l-500 -500zM148 953q0 -42 30 -71q29 -30 71 -30t71 30q30 29 30 71t-30 71q-29 30 -71 30t-71 -30q-30 -29 -30 -71zM701 1200h100l700 -700l-500 -500l-50 50l450 450z" /> +<glyph unicode="" d="M100 0v1025l175 175h925v-1000l-100 -100v1000h-750l-100 -100h750v-1000h-900z" /> +<glyph unicode="" d="M200 0l450 444l450 -443v1150q0 20 -14.5 35t-35.5 15h-800q-21 0 -35.5 -15t-14.5 -35v-1151z" /> +<glyph unicode="" d="M0 100v700h200l100 -200h600l100 200h200v-700h-200v200h-800v-200h-200zM253 829l40 -124h592l62 124l-94 346q-2 11 -10 18t-18 7h-450q-10 0 -18 -7t-10 -18zM281 24l38 152q2 10 11.5 17t19.5 7h500q10 0 19.5 -7t11.5 -17l38 -152q2 -10 -3.5 -17t-15.5 -7h-600 q-10 0 -15.5 7t-3.5 17z" /> +<glyph unicode="" d="M0 200q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-150q-4 8 -11.5 21.5t-33 48t-53 61t-69 48t-83.5 21.5h-200q-41 0 -82 -20.5t-70 -50t-52 -59t-34 -50.5l-12 -20h-150q-41 0 -70.5 -29.5t-29.5 -70.5v-600z M356 500q0 100 72 172t172 72t172 -72t72 -172t-72 -172t-172 -72t-172 72t-72 172zM494 500q0 -44 31 -75t75 -31t75 31t31 75t-31 75t-75 31t-75 -31t-31 -75zM900 700v100h100v-100h-100z" /> +<glyph unicode="" d="M53 0h365v66q-41 0 -72 11t-49 38t1 71l92 234h391l82 -222q16 -45 -5.5 -88.5t-74.5 -43.5v-66h417v66q-34 1 -74 43q-18 19 -33 42t-21 37l-6 13l-385 998h-93l-399 -1006q-24 -48 -52 -75q-12 -12 -33 -25t-36 -20l-15 -7v-66zM416 521l178 457l46 -140l116 -317h-340 z" /> +<glyph unicode="" d="M100 0v89q41 7 70.5 32.5t29.5 65.5v827q0 28 -1 39.5t-5.5 26t-15.5 21t-29 14t-49 14.5v71l471 -1q120 0 213 -88t93 -228q0 -55 -11.5 -101.5t-28 -74t-33.5 -47.5t-28 -28l-12 -7q8 -3 21.5 -9t48 -31.5t60.5 -58t47.5 -91.5t21.5 -129q0 -84 -59 -156.5t-142 -111 t-162 -38.5h-500zM400 200h161q89 0 153 48.5t64 132.5q0 90 -62.5 154.5t-156.5 64.5h-159v-400zM400 700h139q76 0 130 61.5t54 138.5q0 82 -84 130.5t-239 48.5v-379z" /> +<glyph unicode="" d="M200 0v57q77 7 134.5 40.5t65.5 80.5l173 849q10 56 -10 74t-91 37q-6 1 -10.5 2.5t-9.5 2.5v57h425l2 -57q-33 -8 -62 -25.5t-46 -37t-29.5 -38t-17.5 -30.5l-5 -12l-128 -825q-10 -52 14 -82t95 -36v-57h-500z" /> +<glyph unicode="" d="M-75 200h75v800h-75l125 167l125 -167h-75v-800h75l-125 -167zM300 900v300h150h700h150v-300h-50q0 29 -8 48.5t-18.5 30t-33.5 15t-39.5 5.5t-50.5 1h-200v-850l100 -50v-100h-400v100l100 50v850h-200q-34 0 -50.5 -1t-40 -5.5t-33.5 -15t-18.5 -30t-8.5 -48.5h-49z " /> +<glyph unicode="" d="M33 51l167 125v-75h800v75l167 -125l-167 -125v75h-800v-75zM100 901v300h150h700h150v-300h-50q0 29 -8 48.5t-18 30t-33.5 15t-40 5.5t-50.5 1h-200v-650l100 -50v-100h-400v100l100 50v650h-200q-34 0 -50.5 -1t-39.5 -5.5t-33.5 -15t-18.5 -30t-8 -48.5h-50z" /> +<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 350q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM0 650q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1000q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 950q0 -20 14.5 -35t35.5 -15h600q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-600q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" /> +<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 650q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM200 350q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM200 950q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" /> +<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1000q-21 0 -35.5 15 t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-600 q-21 0 -35.5 15t-14.5 35z" /> +<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100 q-21 0 -35.5 15t-14.5 35z" /> +<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM300 50v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800 q-21 0 -35.5 15t-14.5 35zM300 650v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 950v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15 h-800q-21 0 -35.5 15t-14.5 35z" /> +<glyph unicode="" d="M-101 500v100h201v75l166 -125l-166 -125v75h-201zM300 0h100v1100h-100v-1100zM500 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35 v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 650q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100 q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100z" /> +<glyph unicode="" d="M1 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 650 q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM801 0v1100h100v-1100 h-100zM934 550l167 -125v75h200v100h-200v75z" /> +<glyph unicode="" d="M0 275v650q0 31 22 53t53 22h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53zM900 600l300 300v-600z" /> +<glyph unicode="" d="M0 44v1012q0 18 13 31t31 13h1112q19 0 31.5 -13t12.5 -31v-1012q0 -18 -12.5 -31t-31.5 -13h-1112q-18 0 -31 13t-13 31zM100 263l247 182l298 -131l-74 156l293 318l236 -288v500h-1000v-737zM208 750q0 56 39 95t95 39t95 -39t39 -95t-39 -95t-95 -39t-95 39t-39 95z " /> +<glyph unicode="" d="M148 745q0 124 60.5 231.5t165 172t226.5 64.5q123 0 227 -63t164.5 -169.5t60.5 -229.5t-73 -272q-73 -114 -166.5 -237t-150.5 -189l-57 -66q-10 9 -27 26t-66.5 70.5t-96 109t-104 135.5t-100.5 155q-63 139 -63 262zM342 772q0 -107 75.5 -182.5t181.5 -75.5 q107 0 182.5 75.5t75.5 182.5t-75.5 182t-182.5 75t-182 -75.5t-75 -181.5z" /> +<glyph unicode="" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM173 600q0 -177 125.5 -302t301.5 -125v854q-176 0 -301.5 -125 t-125.5 -302z" /> +<glyph unicode="" d="M117 406q0 94 34 186t88.5 172.5t112 159t115 177t87.5 194.5q21 -71 57.5 -142.5t76 -130.5t83 -118.5t82 -117t70 -116t50 -125.5t18.5 -136q0 -89 -39 -165.5t-102 -126.5t-140 -79.5t-156 -33.5q-114 6 -211.5 53t-161.5 139t-64 210zM243 414q14 -82 59.5 -136 t136.5 -80l16 98q-7 6 -18 17t-34 48t-33 77q-15 73 -14 143.5t10 122.5l9 51q-92 -110 -119.5 -185t-12.5 -156z" /> +<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5q366 -6 397 -14l-186 -186h-311q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v125l200 200v-225q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM436 341l161 50l412 412l-114 113l-405 -405zM995 1015l113 -113l113 113l-21 85l-92 28z" /> +<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h261l2 -80q-133 -32 -218 -120h-145q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-53q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5 zM423 524q30 38 81.5 64t103 35.5t99 14t77.5 3.5l29 -1v-209l360 324l-359 318v-216q-7 0 -19 -1t-48 -8t-69.5 -18.5t-76.5 -37t-76.5 -59t-62 -88t-39.5 -121.5z" /> +<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q61 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-169q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM342 632l283 -284l567 567l-137 137l-430 -431l-146 147z" /> +<glyph unicode="" d="M0 603l300 296v-198h200v200h-200l300 300l295 -300h-195v-200h200v198l300 -296l-300 -300v198h-200v-200h195l-295 -300l-300 300h200v200h-200v-198z" /> +<glyph unicode="" d="M200 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-1100l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" /> +<glyph unicode="" d="M0 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-487l500 487v-1100l-500 488v-488l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" /> +<glyph unicode="" d="M136 550l564 550v-487l500 487v-1100l-500 488v-488z" /> +<glyph unicode="" d="M200 0l900 550l-900 550v-1100z" /> +<glyph unicode="" d="M200 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-800zM600 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" /> +<glyph unicode="" d="M200 150q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v800q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" /> +<glyph unicode="" d="M0 0v1100l500 -487v487l564 -550l-564 -550v488z" /> +<glyph unicode="" d="M0 0v1100l500 -487v487l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-500 -488v488z" /> +<glyph unicode="" d="M300 0v1100l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438z" /> +<glyph unicode="" d="M100 250v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5zM100 500h1100l-550 564z" /> +<glyph unicode="" d="M185 599l592 -592l240 240l-353 353l353 353l-240 240z" /> +<glyph unicode="" d="M272 194l353 353l-353 353l241 240l572 -571l21 -22l-1 -1v-1l-592 -591z" /> +<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h200v-200h200v200h200v200h-200v200h-200v-200h-200v-200z" /> +<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h600v200h-600v-200z" /> +<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM246 459l213 -213l141 142l141 -142l213 213l-142 141l142 141l-213 212l-141 -141l-141 142l-212 -213l141 -141 z" /> +<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM270 551l276 -277l411 411l-175 174l-236 -236l-102 102z" /> +<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM364 700h143q4 0 11.5 -1t11 -1t6.5 3t3 9t1 11t3.5 8.5t3.5 6t5.5 4t6.5 2.5t9 1.5t9 0.5h11.5h12.5 q19 0 30 -10t11 -26q0 -22 -4 -28t-27 -22q-5 -1 -12.5 -3t-27 -13.5t-34 -27t-26.5 -46t-11 -68.5h200q5 3 14 8t31.5 25.5t39.5 45.5t31 69t14 94q0 51 -17.5 89t-42 58t-58.5 32t-58.5 15t-51.5 3q-50 0 -90.5 -12t-75 -38.5t-53.5 -74.5t-19 -114zM500 300h200v100h-200 v-100z" /> +<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM400 300h400v100h-100v300h-300v-100h100v-200h-100v-100zM500 800h200v100h-200v-100z" /> +<glyph unicode="" d="M0 500v200h195q31 125 98.5 199.5t206.5 100.5v200h200v-200q54 -20 113 -60t112.5 -105.5t71.5 -134.5h203v-200h-203q-25 -102 -116.5 -186t-180.5 -117v-197h-200v197q-140 27 -208 102.5t-98 200.5h-194zM290 500q24 -73 79.5 -127.5t130.5 -78.5v206h200v-206 q149 48 201 206h-201v200h200q-25 74 -75.5 127t-124.5 77v-204h-200v203q-75 -23 -130 -77t-79 -126h209v-200h-210z" /> +<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM356 465l135 135 l-135 135l109 109l135 -135l135 135l109 -109l-135 -135l135 -135l-109 -109l-135 135l-135 -135z" /> +<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM322 537l141 141 l87 -87l204 205l142 -142l-346 -345z" /> +<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -115 62 -215l568 567q-100 62 -216 62q-171 0 -292.5 -121.5t-121.5 -292.5zM391 245q97 -59 209 -59q171 0 292.5 121.5t121.5 292.5 q0 112 -59 209z" /> +<glyph unicode="" d="M0 547l600 453v-300h600v-300h-600v-301z" /> +<glyph unicode="" d="M0 400v300h600v300l600 -453l-600 -448v301h-600z" /> +<glyph unicode="" d="M204 600l450 600l444 -600h-298v-600h-300v600h-296z" /> +<glyph unicode="" d="M104 600h296v600h300v-600h298l-449 -600z" /> +<glyph unicode="" d="M0 200q6 132 41 238.5t103.5 193t184 138t271.5 59.5v271l600 -453l-600 -448v301q-95 -2 -183 -20t-170 -52t-147 -92.5t-100 -135.5z" /> +<glyph unicode="" d="M0 0v400l129 -129l294 294l142 -142l-294 -294l129 -129h-400zM635 777l142 -142l294 294l129 -129v400h-400l129 -129z" /> +<glyph unicode="" d="M34 176l295 295l-129 129h400v-400l-129 130l-295 -295zM600 600v400l129 -129l295 295l142 -141l-295 -295l129 -130h-400z" /> +<glyph unicode="" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5t224.5 -45.5t184 -123t123 -184t45.5 -224.5t-45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5zM456 851l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5 t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5h-207q-21 0 -33 -14.5t-8 -34.5zM500 300h200v100h-200v-100z" /> +<glyph unicode="" d="M0 800h100v-200h400v300h200v-300h400v200h100v100h-111q1 1 1 6.5t-1.5 15t-3.5 17.5l-34 172q-11 39 -41.5 63t-69.5 24q-32 0 -61 -17l-239 -144q-22 -13 -40 -35q-19 24 -40 36l-238 144q-33 18 -62 18q-39 0 -69.5 -23t-40.5 -61l-35 -177q-2 -8 -3 -18t-1 -15v-6 h-111v-100zM100 0h400v400h-400v-400zM200 900q-3 0 14 48t36 96l18 47l213 -191h-281zM700 0v400h400v-400h-400zM731 900l202 197q5 -12 12 -32.5t23 -64t25 -72t7 -28.5h-269z" /> +<glyph unicode="" d="M0 -22v143l216 193q-9 53 -13 83t-5.5 94t9 113t38.5 114t74 124q47 60 99.5 102.5t103 68t127.5 48t145.5 37.5t184.5 43.5t220 58.5q0 -189 -22 -343t-59 -258t-89 -181.5t-108.5 -120t-122 -68t-125.5 -30t-121.5 -1.5t-107.5 12.5t-87.5 17t-56.5 7.5l-99 -55z M238.5 300.5q19.5 -6.5 86.5 76.5q55 66 367 234q70 38 118.5 69.5t102 79t99 111.5t86.5 148q22 50 24 60t-6 19q-7 5 -17 5t-26.5 -14.5t-33.5 -39.5q-35 -51 -113.5 -108.5t-139.5 -89.5l-61 -32q-369 -197 -458 -401q-48 -111 -28.5 -117.5z" /> +<glyph unicode="" d="M111 408q0 -33 5 -63q9 -56 44 -119.5t105 -108.5q31 -21 64 -16t62 23.5t57 49.5t48 61.5t35 60.5q32 66 39 184.5t-13 157.5q79 -80 122 -164t26 -184q-5 -33 -20.5 -69.5t-37.5 -80.5q-10 -19 -14.5 -29t-12 -26t-9 -23.5t-3 -19t2.5 -15.5t11 -9.5t19.5 -5t30.5 2.5 t42 8q57 20 91 34t87.5 44.5t87 64t65.5 88.5t47 122q38 172 -44.5 341.5t-246.5 278.5q22 -44 43 -129q39 -159 -32 -154q-15 2 -33 9q-79 33 -120.5 100t-44 175.5t48.5 257.5q-13 -8 -34 -23.5t-72.5 -66.5t-88.5 -105.5t-60 -138t-8 -166.5q2 -12 8 -41.5t8 -43t6 -39.5 t3.5 -39.5t-1 -33.5t-6 -31.5t-13.5 -24t-21 -20.5t-31 -12q-38 -10 -67 13t-40.5 61.5t-15 81.5t10.5 75q-52 -46 -83.5 -101t-39 -107t-7.5 -85z" /> +<glyph unicode="" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5t145.5 -23.5t132.5 -59t116.5 -83.5t97 -90t74.5 -85.5t49 -63.5t20 -30l26 -40l-26 -40q-6 -10 -20 -30t-49 -63.5t-74.5 -85.5t-97 -90t-116.5 -83.5t-132.5 -59t-145.5 -23.5 t-145.5 23.5t-132.5 59t-116.5 83.5t-97 90t-74.5 85.5t-49 63.5t-20 30zM120 600q7 -10 40.5 -58t56 -78.5t68 -77.5t87.5 -75t103 -49.5t125 -21.5t123.5 20t100.5 45.5t85.5 71.5t66.5 75.5t58 81.5t47 66q-1 1 -28.5 37.5t-42 55t-43.5 53t-57.5 63.5t-58.5 54 q49 -74 49 -163q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l105 105q-37 24 -75 72t-57 84l-20 36z" /> +<glyph unicode="" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5q61 0 121 -17l37 142h148l-314 -1200h-148l37 143q-82 21 -165 71.5t-140 102t-109.5 112t-72 88.5t-29.5 43zM120 600q210 -282 393 -336l37 141q-107 18 -178.5 101.5t-71.5 193.5 q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l47 47l23 87q-30 28 -59 69t-44 68l-14 26zM780 161l38 145q22 15 44.5 34t46 44t40.5 44t41 50.5t33.5 43.5t33 44t24.5 34q-97 127 -140 175l39 146q67 -54 131.5 -125.5t87.5 -103.5t36 -52l26 -40l-26 -40 q-7 -12 -25.5 -38t-63.5 -79.5t-95.5 -102.5t-124 -100t-146.5 -79z" /> +<glyph unicode="" d="M-97.5 34q13.5 -34 50.5 -34h1294q37 0 50.5 35.5t-7.5 67.5l-642 1056q-20 34 -48 36.5t-48 -29.5l-642 -1066q-21 -32 -7.5 -66zM155 200l445 723l445 -723h-345v100h-200v-100h-345zM500 600l100 -300l100 300v100h-200v-100z" /> +<glyph unicode="" d="M100 262v41q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44t106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -91 100 -113v-64q0 -20 -13 -28.5t-32 0.5l-94 78h-222l-94 -78q-19 -9 -32 -0.5t-13 28.5 v64q0 22 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5z" /> +<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v750h-1100v-750zM0 900h1100v150q0 21 -14.5 35.5t-35.5 14.5h-150v100h-100v-100h-500v100h-100v-100h-150q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 100v100h100v-100h-100zM100 300v100h100v-100h-100z M100 500v100h100v-100h-100zM300 100v100h100v-100h-100zM300 300v100h100v-100h-100zM300 500v100h100v-100h-100zM500 100v100h100v-100h-100zM500 300v100h100v-100h-100zM500 500v100h100v-100h-100zM700 100v100h100v-100h-100zM700 300v100h100v-100h-100zM700 500 v100h100v-100h-100zM900 100v100h100v-100h-100zM900 300v100h100v-100h-100zM900 500v100h100v-100h-100z" /> +<glyph unicode="" d="M0 200v200h259l600 600h241v198l300 -295l-300 -300v197h-159l-600 -600h-341zM0 800h259l122 -122l141 142l-181 180h-341v-200zM678 381l141 142l122 -123h159v198l300 -295l-300 -300v197h-241z" /> +<glyph unicode="" d="M0 400v600q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5z" /> +<glyph unicode="" d="M100 600v200h300v-250q0 -113 6 -145q17 -92 102 -117q39 -11 92 -11q37 0 66.5 5.5t50 15.5t36 24t24 31.5t14 37.5t7 42t2.5 45t0 47v25v250h300v-200q0 -42 -3 -83t-15 -104t-31.5 -116t-58 -109.5t-89 -96.5t-129 -65.5t-174.5 -25.5t-174.5 25.5t-129 65.5t-89 96.5 t-58 109.5t-31.5 116t-15 104t-3 83zM100 900v300h300v-300h-300zM800 900v300h300v-300h-300z" /> +<glyph unicode="" d="M-30 411l227 -227l352 353l353 -353l226 227l-578 579z" /> +<glyph unicode="" d="M70 797l580 -579l578 579l-226 227l-353 -353l-352 353z" /> +<glyph unicode="" d="M-198 700l299 283l300 -283h-203v-400h385l215 -200h-800v600h-196zM402 1000l215 -200h381v-400h-198l299 -283l299 283h-200v600h-796z" /> +<glyph unicode="" d="M18 939q-5 24 10 42q14 19 39 19h896l38 162q5 17 18.5 27.5t30.5 10.5h94q20 0 35 -14.5t15 -35.5t-15 -35.5t-35 -14.5h-54l-201 -961q-2 -4 -6 -10.5t-19 -17.5t-33 -11h-31v-50q0 -20 -14.5 -35t-35.5 -15t-35.5 15t-14.5 35v50h-300v-50q0 -20 -14.5 -35t-35.5 -15 t-35.5 15t-14.5 35v50h-50q-21 0 -35.5 15t-14.5 35q0 21 14.5 35.5t35.5 14.5h535l48 200h-633q-32 0 -54.5 21t-27.5 43z" /> +<glyph unicode="" d="M0 0v800h1200v-800h-1200zM0 900v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-100h-1200z" /> +<glyph unicode="" d="M1 0l300 700h1200l-300 -700h-1200zM1 400v600h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-200h-1000z" /> +<glyph unicode="" d="M302 300h198v600h-198l298 300l298 -300h-198v-600h198l-298 -300z" /> +<glyph unicode="" d="M0 600l300 298v-198h600v198l300 -298l-300 -297v197h-600v-197z" /> +<glyph unicode="" d="M0 100v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM31 400l172 739q5 22 23 41.5t38 19.5h672q19 0 37.5 -22.5t23.5 -45.5l172 -732h-1138zM800 100h100v100h-100v-100z M1000 100h100v100h-100v-100z" /> +<glyph unicode="" d="M-101 600v50q0 24 25 49t50 38l25 13v-250l-11 5.5t-24 14t-30 21.5t-24 27.5t-11 31.5zM100 500v250v8v8v7t0.5 7t1.5 5.5t2 5t3 4t4.5 3.5t6 1.5t7.5 0.5h200l675 250v-850l-675 200h-38l47 -276q2 -12 -3 -17.5t-11 -6t-21 -0.5h-8h-83q-20 0 -34.5 14t-18.5 35 q-55 337 -55 351zM1100 200v850q0 21 14.5 35.5t35.5 14.5q20 0 35 -14.5t15 -35.5v-850q0 -20 -15 -35t-35 -15q-21 0 -35.5 15t-14.5 35z" /> +<glyph unicode="" d="M74 350q0 21 13.5 35.5t33.5 14.5h18l117 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3 32t29 13h94q20 0 29 -10.5t3 -29.5q-18 -36 -18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q20 0 33.5 -14.5t13.5 -35.5q0 -20 -13 -40t-31 -27q-8 -3 -23 -8.5 t-65 -20t-103 -25t-132.5 -19.5t-158.5 -9q-125 0 -245.5 20.5t-178.5 40.5l-58 20q-18 7 -31 27.5t-13 40.5zM497 110q12 -49 40 -79.5t63 -30.5t63 30.5t39 79.5q-48 -6 -102 -6t-103 6z" /> +<glyph unicode="" d="M21 445l233 -45l-78 -224l224 78l45 -233l155 179l155 -179l45 233l224 -78l-78 224l234 45l-180 155l180 156l-234 44l78 225l-224 -78l-45 233l-155 -180l-155 180l-45 -233l-224 78l78 -225l-233 -44l179 -156z" /> +<glyph unicode="" d="M0 200h200v600h-200v-600zM300 275q0 -75 100 -75h61q124 -100 139 -100h250q46 0 83 57l238 344q29 31 29 74v100q0 44 -30.5 84.5t-69.5 40.5h-328q28 118 28 125v150q0 44 -30.5 84.5t-69.5 40.5h-50q-27 0 -51 -20t-38 -48l-96 -198l-145 -196q-20 -26 -20 -63v-400z M400 300v375l150 213l100 212h50v-175l-50 -225h450v-125l-250 -375h-214l-136 100h-100z" /> +<glyph unicode="" d="M0 400v600h200v-600h-200zM300 525v400q0 75 100 75h61q124 100 139 100h250q46 0 83 -57l238 -344q29 -31 29 -74v-100q0 -44 -30.5 -84.5t-69.5 -40.5h-328q28 -118 28 -125v-150q0 -44 -30.5 -84.5t-69.5 -40.5h-50q-27 0 -51 20t-38 48l-96 198l-145 196 q-20 26 -20 63zM400 525l150 -212l100 -213h50v175l-50 225h450v125l-250 375h-214l-136 -100h-100v-375z" /> +<glyph unicode="" d="M8 200v600h200v-600h-200zM308 275v525q0 17 14 35.5t28 28.5l14 9l362 230q14 6 25 6q17 0 29 -12l109 -112q14 -14 14 -34q0 -18 -11 -32l-85 -121h302q85 0 138.5 -38t53.5 -110t-54.5 -111t-138.5 -39h-107l-130 -339q-7 -22 -20.5 -41.5t-28.5 -19.5h-341 q-7 0 -90 81t-83 94zM408 289l100 -89h293l131 339q6 21 19.5 41t28.5 20h203q16 0 25 15t9 36q0 20 -9 34.5t-25 14.5h-457h-6.5h-7.5t-6.5 0.5t-6 1t-5 1.5t-5.5 2.5t-4 4t-4 5.5q-5 12 -5 20q0 14 10 27l147 183l-86 83l-339 -236v-503z" /> +<glyph unicode="" d="M-101 651q0 72 54 110t139 38l302 -1l-85 121q-11 16 -11 32q0 21 14 34l109 113q13 12 29 12q11 0 25 -6l365 -230q7 -4 17 -10.5t26.5 -26t16.5 -36.5v-526q0 -13 -86 -93.5t-94 -80.5h-341q-16 0 -29.5 20t-19.5 41l-130 339h-107q-84 0 -139 39t-55 111zM-1 601h222 q15 0 28.5 -20.5t19.5 -40.5l131 -339h293l107 89v502l-343 237l-87 -83l145 -184q10 -11 10 -26q0 -11 -5 -20q-1 -3 -3.5 -5.5l-4 -4t-5 -2.5t-5.5 -1.5t-6.5 -1t-6.5 -0.5h-7.5h-6.5h-476v-100zM1000 201v600h200v-600h-200z" /> +<glyph unicode="" d="M97 719l230 -363q4 -6 10.5 -15.5t26 -25t36.5 -15.5h525q13 0 94 83t81 90v342q0 15 -20 28.5t-41 19.5l-339 131v106q0 84 -39 139t-111 55t-110 -53.5t-38 -138.5v-302l-121 84q-15 12 -33.5 11.5t-32.5 -13.5l-112 -110q-22 -22 -6 -53zM172 739l83 86l183 -146 q22 -18 47 -5q3 1 5.5 3.5l4 4t2.5 5t1.5 5.5t1 6.5t0.5 6.5v7.5v6.5v456q0 22 25 31t50 -0.5t25 -30.5v-202q0 -16 20 -29.5t41 -19.5l339 -130v-294l-89 -100h-503zM400 0v200h600v-200h-600z" /> +<glyph unicode="" d="M2 585q-16 -31 6 -53l112 -110q13 -13 32 -13.5t34 10.5l121 85q0 -51 -0.5 -153.5t-0.5 -148.5q0 -84 38.5 -138t110.5 -54t111 55t39 139v106l339 131q20 6 40.5 19.5t20.5 28.5v342q0 7 -81 90t-94 83h-525q-17 0 -35.5 -14t-28.5 -28l-10 -15zM77 565l236 339h503 l89 -100v-294l-340 -130q-20 -6 -40 -20t-20 -29v-202q0 -22 -25 -31t-50 0t-25 31v456v14.5t-1.5 11.5t-5 12t-9.5 7q-24 13 -46 -5l-184 -146zM305 1104v200h600v-200h-600z" /> +<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM298 701l2 -201h300l-2 -194l402 294l-402 298v-197h-300z" /> +<glyph unicode="" d="M0 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t231.5 47.5q122 0 232.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-218 -217.5t-300 -80t-299.5 80t-217.5 217.5t-80 299.5zM200 600l402 -294l-2 194h300l2 201h-300v197z" /> +<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600h200v-300h200v300h200l-300 400z" /> +<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600l300 -400l300 400h-200v300h-200v-300h-200z" /> +<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM254 780q-8 -33 5.5 -92.5t7.5 -87.5q0 -9 17 -44t16 -60 q12 0 23 -5.5t23 -15t20 -13.5q24 -12 108 -42q22 -8 53 -31.5t59.5 -38.5t57.5 -11q8 -18 -15 -55t-20 -57q42 -71 87 -80q0 -6 -3 -15.5t-3.5 -14.5t4.5 -17q104 -3 221 112q30 29 47 47t34.5 49t20.5 62q-14 9 -37 9.5t-36 7.5q-14 7 -49 15t-52 19q-9 0 -39.5 -0.5 t-46.5 -1.5t-39 -6.5t-39 -16.5q-50 -35 -66 -12q-4 2 -3.5 25.5t0.5 25.5q-6 13 -26.5 17t-24.5 7q2 22 -2 41t-16.5 28t-38.5 -20q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q12 -19 32 -37.5t34 -27.5l14 -8q0 3 9.5 39.5t5.5 57.5 q-4 23 14.5 44.5t22.5 31.5q5 14 10 35t8.5 31t15.5 22.5t34 21.5q-6 18 10 37q8 0 23.5 -1.5t24.5 -1.5t20.5 4.5t20.5 15.5q-10 23 -30.5 42.5t-38 30t-49 26.5t-43.5 23q11 39 2 44q31 -13 58 -14.5t39 3.5l11 4q7 36 -16.5 53.5t-64.5 28.5t-56 23q-19 -3 -37 0 q-15 -12 -36.5 -21t-34.5 -12t-44 -8t-39 -6q-15 -3 -45.5 0.5t-45.5 -2.5q-21 -7 -52 -26.5t-34 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -90.5t-29.5 -79.5zM518 916q3 12 16 30t16 25q10 -10 18.5 -10t14 6t14.5 14.5t16 12.5q0 -24 17 -66.5t17 -43.5 q-9 2 -31 5t-36 5t-32 8t-30 14zM692 1003h1h-1z" /> +<glyph unicode="" d="M0 164.5q0 21.5 15 37.5l600 599q-33 101 6 201.5t135 154.5q164 92 306 -9l-259 -138l145 -232l251 126q13 -175 -151 -267q-123 -70 -253 -23l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5z" /> +<glyph unicode="" horiz-adv-x="1220" d="M0 196v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 596v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5zM0 996v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM600 596h500v100h-500v-100zM800 196h300v100h-300v-100zM900 996h200v100h-200v-100z" /> +<glyph unicode="" d="M100 1100v100h1000v-100h-1000zM150 1000h900l-350 -500v-300l-200 -200v500z" /> +<glyph unicode="" d="M0 200v200h1200v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500z M500 1000h200v100h-200v-100z" /> +<glyph unicode="" d="M0 0v400l129 -129l200 200l142 -142l-200 -200l129 -129h-400zM0 800l129 129l200 -200l142 142l-200 200l129 129h-400v-400zM729 329l142 142l200 -200l129 129v-400h-400l129 129zM729 871l200 200l-129 129h400v-400l-129 129l-200 -200z" /> +<glyph unicode="" d="M0 596q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 596q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM291 655 q0 23 15.5 38.5t38.5 15.5t39 -16t16 -38q0 -23 -16 -39t-39 -16q-22 0 -38 16t-16 39zM400 850q0 22 16 38.5t39 16.5q22 0 38 -16t16 -39t-16 -39t-38 -16q-23 0 -39 16.5t-16 38.5zM514 609q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 22 16 38.5t39 16.5 q22 0 38 -16t16 -39t-16 -39t-38 -16q-14 0 -29 10l-55 -145q17 -22 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5zM800 655q0 22 16 38t39 16t38.5 -15.5t15.5 -38.5t-16 -39t-38 -16q-23 0 -39 16t-16 39z" /> +<glyph unicode="" d="M-40 375q-13 -95 35 -173q35 -57 94 -89t129 -32q63 0 119 28q33 16 65 40.5t52.5 45.5t59.5 64q40 44 57 61l394 394q35 35 47 84t-3 96q-27 87 -117 104q-20 2 -29 2q-46 0 -78.5 -16.5t-67.5 -51.5l-389 -396l-7 -7l69 -67l377 373q20 22 39 38q23 23 50 23 q38 0 53 -36q16 -39 -20 -75l-547 -547q-52 -52 -125 -52q-55 0 -100 33t-54 96q-5 35 2.5 66t31.5 63t42 50t56 54q24 21 44 41l348 348q52 52 82.5 79.5t84 54t107.5 26.5q25 0 48 -4q95 -17 154 -94.5t51 -175.5q-7 -101 -98 -192l-252 -249l-253 -256l7 -7l69 -60 l517 511q67 67 95 157t11 183q-16 87 -67 154t-130 103q-69 33 -152 33q-107 0 -197 -55q-40 -24 -111 -95l-512 -512q-68 -68 -81 -163z" /> +<glyph unicode="" d="M80 784q0 131 98.5 229.5t230.5 98.5q143 0 241 -129q103 129 246 129q129 0 226 -98.5t97 -229.5q0 -46 -17.5 -91t-61 -99t-77 -89.5t-104.5 -105.5q-197 -191 -293 -322l-17 -23l-16 23q-43 58 -100 122.5t-92 99.5t-101 100q-71 70 -104.5 105.5t-77 89.5t-61 99 t-17.5 91zM250 784q0 -27 30.5 -70t61.5 -75.5t95 -94.5l22 -22q93 -90 190 -201q82 92 195 203l12 12q64 62 97.5 97t64.5 79t31 72q0 71 -48 119.5t-105 48.5q-74 0 -132 -83l-118 -171l-114 174q-51 80 -123 80q-60 0 -109.5 -49.5t-49.5 -118.5z" /> +<glyph unicode="" d="M57 353q0 -95 66 -159l141 -142q68 -66 159 -66q93 0 159 66l283 283q66 66 66 159t-66 159l-141 141q-8 9 -19 17l-105 -105l212 -212l-389 -389l-247 248l95 95l-18 18q-46 45 -75 101l-55 -55q-66 -66 -66 -159zM269 706q0 -93 66 -159l141 -141q7 -7 19 -17l105 105 l-212 212l389 389l247 -247l-95 -96l18 -17q47 -49 77 -100l29 29q35 35 62.5 88t27.5 96q0 93 -66 159l-141 141q-66 66 -159 66q-95 0 -159 -66l-283 -283q-66 -64 -66 -159z" /> +<glyph unicode="" d="M200 100v953q0 21 30 46t81 48t129 38t163 15t162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5zM300 300h600v700h-600v-700zM496 150q0 -43 30.5 -73.5t73.5 -30.5t73.5 30.5t30.5 73.5t-30.5 73.5t-73.5 30.5 t-73.5 -30.5t-30.5 -73.5z" /> +<glyph unicode="" d="M0 0l303 380l207 208l-210 212h300l267 279l-35 36q-15 14 -15 35t15 35q14 15 35 15t35 -15l283 -282q15 -15 15 -36t-15 -35q-14 -15 -35 -15t-35 15l-36 35l-279 -267v-300l-212 210l-208 -207z" /> +<glyph unicode="" d="M295 433h139q5 -77 48.5 -126.5t117.5 -64.5v335q-6 1 -15.5 4t-11.5 3q-46 14 -79 26.5t-72 36t-62.5 52t-40 72.5t-16.5 99q0 92 44 159.5t109 101t144 40.5v78h100v-79q38 -4 72.5 -13.5t75.5 -31.5t71 -53.5t51.5 -84t24.5 -118.5h-159q-8 72 -35 109.5t-101 50.5 v-307l64 -14q34 -7 64 -16.5t70 -31.5t67.5 -52t47.5 -80.5t20 -112.5q0 -139 -89 -224t-244 -96v-77h-100v78q-152 17 -237 104q-40 40 -52.5 93.5t-15.5 139.5zM466 889q0 -29 8 -51t16.5 -34t29.5 -22.5t31 -13.5t38 -10q7 -2 11 -3v274q-61 -8 -97.5 -37.5t-36.5 -102.5 zM700 237q170 18 170 151q0 64 -44 99.5t-126 60.5v-311z" /> +<glyph unicode="" d="M100 600v100h166q-24 49 -44 104q-10 26 -14.5 55.5t-3 72.5t25 90t68.5 87q97 88 263 88q129 0 230 -89t101 -208h-153q0 52 -34 89.5t-74 51.5t-76 14q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -28 16.5 -69.5t28 -62.5t41.5 -72h241v-100h-197q8 -50 -2.5 -115 t-31.5 -94q-41 -59 -99 -113q35 11 84 18t70 7q33 1 103 -16t103 -17q76 0 136 30l50 -147q-41 -25 -80.5 -36.5t-59 -13t-61.5 -1.5q-23 0 -128 33t-155 29q-39 -4 -82 -17t-66 -25l-24 -11l-55 145l16.5 11t15.5 10t13.5 9.5t14.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221z" /> +<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM602 900l298 300l298 -300h-198v-900h-200v900h-198z" /> +<glyph unicode="" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v200h100v-100h200v-100h-300zM700 400v100h300v-200h-99v-100h-100v100h99v100h-200zM700 700v500h300v-500h-100v100h-100v-100h-100zM801 900h100v200h-100v-200z" /> +<glyph unicode="" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v500h300v-500h-100v100h-100v-100h-100zM700 700v200h100v-100h200v-100h-300zM700 1100v100h300v-200h-99v-100h-100v100h99v100h-200zM801 200h100v200h-100v-200z" /> +<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 100v400h300v-500h-100v100h-200zM800 1100v100h200v-500h-100v400h-100zM901 200h100v200h-100v-200z" /> +<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 400v100h200v-500h-100v400h-100zM800 800v400h300v-500h-100v100h-200zM901 900h100v200h-100v-200z" /> +<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h500v-200h-500zM700 400v200h400v-200h-400zM700 700v200h300v-200h-300zM700 1000v200h200v-200h-200z" /> +<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h200v-200h-200zM700 400v200h300v-200h-300zM700 700v200h400v-200h-400zM700 1000v200h500v-200h-500z" /> +<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q162 0 281 -118.5t119 -281.5v-300q0 -165 -118.5 -282.5t-281.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500z" /> +<glyph unicode="" d="M0 400v300q0 163 119 281.5t281 118.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-163 0 -281.5 117.5t-118.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM400 300l333 250l-333 250v-500z" /> +<glyph unicode="" d="M0 400v300q0 163 117.5 281.5t282.5 118.5h300q163 0 281.5 -119t118.5 -281v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 700l250 -333l250 333h-500z" /> +<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -162 -118.5 -281t-281.5 -119h-300q-165 0 -282.5 118.5t-117.5 281.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 400h500l-250 333z" /> +<glyph unicode="" d="M0 400v300h300v200l400 -350l-400 -350v200h-300zM500 0v200h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-500v200h400q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-400z" /> +<glyph unicode="" d="M217 519q8 -19 31 -19h302q-155 -438 -160 -458q-5 -21 4 -32l9 -8h9q14 0 26 15q11 13 274.5 321.5t264.5 308.5q14 19 5 36q-8 17 -31 17l-301 -1q1 4 78 219.5t79 227.5q2 15 -5 27l-9 9h-9q-15 0 -25 -16q-4 -6 -98 -111.5t-228.5 -257t-209.5 -237.5q-16 -19 -6 -41 z" /> +<glyph unicode="" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q47 0 100 15v185h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h500v185q-14 4 -114 7.5t-193 5.5l-93 2q-165 0 -282.5 -117.5t-117.5 -282.5v-300zM600 400v300h300v200l400 -350l-400 -350v200h-300z " /> +<glyph unicode="" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q163 0 281.5 117.5t118.5 282.5v98l-78 73l-122 -123v-148q0 -41 -29.5 -70.5t-70.5 -29.5h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h156l118 122l-74 78h-100q-165 0 -282.5 -117.5t-117.5 -282.5 v-300zM496 709l353 342l-149 149h500v-500l-149 149l-342 -353z" /> +<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM406 600 q0 80 57 137t137 57t137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137z" /> +<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 800l445 -500l450 500h-295v400h-300v-400h-300zM900 150h100v50h-100v-50z" /> +<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 700h300v-300h300v300h295l-445 500zM900 150h100v50h-100v-50z" /> +<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 705l305 -305l596 596l-154 155l-442 -442l-150 151zM900 150h100v50h-100v-50z" /> +<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 988l97 -98l212 213l-97 97zM200 400l697 1l3 699l-250 -239l-149 149l-212 -212l149 -149zM900 150h100v50h-100v-50z" /> +<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM200 612l212 -212l98 97l-213 212zM300 1200l239 -250l-149 -149l212 -212l149 148l249 -237l-1 697zM900 150h100v50h-100v-50z" /> +<glyph unicode="" d="M23 415l1177 784v-1079l-475 272l-310 -393v416h-392zM494 210l672 938l-672 -712v-226z" /> +<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-850q0 -21 -15 -35.5t-35 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200z" /> +<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-218l-276 -275l-120 120l-126 -127h-378v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM581 306l123 123l120 -120l353 352l123 -123l-475 -476zM600 1000h100v200h-100v-200z" /> +<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-269l-103 -103l-170 170l-298 -298h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200zM700 133l170 170l-170 170l127 127l170 -170l170 170l127 -128l-170 -169l170 -170 l-127 -127l-170 170l-170 -170z" /> +<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-300h-400v-200h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300l300 -300l300 300h-200v300h-200v-300h-200zM600 1000v200h100v-200h-100z" /> +<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-402l-200 200l-298 -298h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300h200v-300h200v300h200l-300 300zM600 1000v200h100v-200h-100z" /> +<glyph unicode="" d="M0 250q0 -21 14.5 -35.5t35.5 -14.5h1100q21 0 35.5 14.5t14.5 35.5v550h-1200v-550zM0 900h1200v150q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 300v200h400v-200h-400z" /> +<glyph unicode="" d="M0 400l300 298v-198h400v-200h-400v-198zM100 800v200h100v-200h-100zM300 800v200h100v-200h-100zM500 800v200h400v198l300 -298l-300 -298v198h-400zM800 300v200h100v-200h-100zM1000 300h100v200h-100v-200z" /> +<glyph unicode="" d="M100 700v400l50 100l50 -100v-300h100v300l50 100l50 -100v-300h100v300l50 100l50 -100v-400l-100 -203v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447zM800 597q0 -29 10.5 -55.5t25 -43t29 -28.5t25.5 -18l10 -5v-397q0 -21 14.5 -35.5 t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v1106q0 31 -18 40.5t-44 -7.5l-276 -116q-25 -17 -43.5 -51.5t-18.5 -65.5v-359z" /> +<glyph unicode="" d="M100 0h400v56q-75 0 -87.5 6t-12.5 44v394h500v-394q0 -38 -12.5 -44t-87.5 -6v-56h400v56q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v888q0 22 25 34.5t50 13.5l25 2v56h-400v-56q75 0 87.5 -6t12.5 -44v-394h-500v394q0 38 12.5 44t87.5 6v56h-400v-56q4 0 11 -0.5 t24 -3t30 -7t24 -15t11 -24.5v-888q0 -22 -25 -34.5t-50 -13.5l-25 -2v-56z" /> +<glyph unicode="" d="M0 300q0 -41 29.5 -70.5t70.5 -29.5h300q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-300q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM100 100h400l200 200h105l295 98v-298h-425l-100 -100h-375zM100 300v200h300v-200h-300zM100 600v200h300v-200h-300z M100 1000h400l200 -200v-98l295 98h105v200h-425l-100 100h-375zM700 402v163l400 133v-163z" /> +<glyph unicode="" d="M16.5 974.5q0.5 -21.5 16 -90t46.5 -140t104 -177.5t175 -208q103 -103 207.5 -176t180 -103.5t137 -47t92.5 -16.5l31 1l163 162q17 18 13.5 41t-22.5 37l-192 136q-19 14 -45 12t-42 -19l-118 -118q-142 101 -268 227t-227 268l118 118q17 17 20 41.5t-11 44.5 l-139 194q-14 19 -36.5 22t-40.5 -14l-162 -162q-1 -11 -0.5 -32.5z" /> +<glyph unicode="" d="M0 50v212q0 20 10.5 45.5t24.5 39.5l365 303v50q0 4 1 10.5t12 22.5t30 28.5t60 23t97 10.5t97 -10t60 -23.5t30 -27.5t12 -24l1 -10v-50l365 -303q14 -14 24.5 -39.5t10.5 -45.5v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-20 0 -35 14.5t-15 35.5zM0 712 q0 -21 14.5 -33.5t34.5 -8.5l202 33q20 4 34.5 21t14.5 38v146q141 24 300 24t300 -24v-146q0 -21 14.5 -38t34.5 -21l202 -33q20 -4 34.5 8.5t14.5 33.5v200q-6 8 -19 20.5t-63 45t-112 57t-171 45t-235 20.5q-92 0 -175 -10.5t-141.5 -27t-108.5 -36.5t-81.5 -40 t-53.5 -36.5t-31 -27.5l-9 -10v-200z" /> +<glyph unicode="" d="M100 0v100h1100v-100h-1100zM175 200h950l-125 150v250l100 100v400h-100v-200h-100v200h-200v-200h-100v200h-200v-200h-100v200h-100v-400l100 -100v-250z" /> +<glyph unicode="" d="M100 0h300v400q0 41 -29.5 70.5t-70.5 29.5h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-400zM500 0v1000q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-1000h-300zM900 0v700q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-700h-300z" /> +<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" /> +<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h100v200h100v-200h100v500h-100v-200h-100v200h-100v-500zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" /> +<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v100h-200v300h200v100h-300v-500zM600 300h300v100h-200v300h200v100h-300v-500z" /> +<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 550l300 -150v300zM600 400l300 150l-300 150v-300z" /> +<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300v500h700v-500h-700zM300 400h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130v-300zM575 549 q0 -65 27 -107t68 -42h130v300h-130q-38 0 -66.5 -43t-28.5 -108z" /> +<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" /> +<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v400h-200v100h-100v-500zM301 400v200h100v-200h-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" /> +<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 700v100h300v-300h-99v-100h-100v100h99v200h-200zM201 300v100h100v-100h-100zM601 300v100h100v-100h-100z M700 700v100h200v-500h-100v400h-100z" /> +<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 500v200 l100 100h300v-100h-300v-200h300v-100h-300z" /> +<glyph unicode="" d="M0 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 400v400h300 l100 -100v-100h-100v100h-200v-100h200v-100h-200v-100h-100zM700 400v100h100v-100h-100z" /> +<glyph unicode="" d="M-14 494q0 -80 56.5 -137t135.5 -57h222v300h400v-300h128q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200h200v300h200v-300h200 l-300 -300z" /> +<glyph unicode="" d="M-14 494q0 -80 56.5 -137t135.5 -57h8l414 414l403 -403q94 26 154.5 104.5t60.5 178.5q0 120 -85 206.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200l300 300 l300 -300h-200v-300h-200v300h-200z" /> +<glyph unicode="" d="M100 200h400v-155l-75 -45h350l-75 45v155h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170z" /> +<glyph unicode="" d="M121 700q0 -53 28.5 -97t75.5 -65q-4 -16 -4 -38q0 -74 52.5 -126.5t126.5 -52.5q56 0 100 30v-306l-75 -45h350l-75 45v306q46 -30 100 -30q74 0 126.5 52.5t52.5 126.5q0 24 -9 55q50 32 79.5 83t29.5 112q0 90 -61.5 155.5t-150.5 71.5q-26 89 -99.5 145.5 t-167.5 56.5q-116 0 -197.5 -81.5t-81.5 -197.5q0 -4 1 -11.5t1 -11.5q-14 2 -23 2q-74 0 -126.5 -52.5t-52.5 -126.5z" /> +</font> +</defs></svg>
\ No newline at end of file diff --git a/src/fmt/doc/_static/fonts/glyphicons-halflings-regular.ttf b/src/fmt/doc/_static/fonts/glyphicons-halflings-regular.ttf Binary files differnew file mode 100644 index 000000000..67fa00bf8 --- /dev/null +++ b/src/fmt/doc/_static/fonts/glyphicons-halflings-regular.ttf diff --git a/src/fmt/doc/_static/fonts/glyphicons-halflings-regular.woff b/src/fmt/doc/_static/fonts/glyphicons-halflings-regular.woff Binary files differnew file mode 100644 index 000000000..8c54182aa --- /dev/null +++ b/src/fmt/doc/_static/fonts/glyphicons-halflings-regular.woff diff --git a/src/fmt/doc/_templates/layout.html b/src/fmt/doc/_templates/layout.html new file mode 100644 index 000000000..333a606c8 --- /dev/null +++ b/src/fmt/doc/_templates/layout.html @@ -0,0 +1,148 @@ +{% extends "!layout.html" %} + +{% block extrahead %} +<meta name="description" content="Small, safe and fast formatting library"> +<meta name="keywords" content="C++, formatting, printf, string, library"> +<meta name="author" content="Victor Zverovich"> +<link rel="stylesheet" href="_static/fmt.css"> +{# Google Analytics #} +<script async src="https://www.googletagmanager.com/gtag/js?id=UA-20116650-4"></script> +<script> + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'UA-20116650-4'); +</script> +{% endblock %} + +{%- macro searchform(classes, button) %} +<form class="{{classes}}" role="search" action="{{ pathto('search') }}" + method="get"> + <div class="form-group"> + <input type="text" name="q" class="form-control" + {{ 'placeholder="Search"' if not button }} > + </div> + <input type="hidden" name="check_keywords" value="yes" /> + <input type="hidden" name="area" value="default" /> + {% if button %} + <input type="submit" class="btn btn-default" value="search"> + {% endif %} +</form> +{%- endmacro %} + +{% block header %} +<nav class="navbar navbar-inverse"> + <div class="tb-container"> + <div class="row"> + <div class="navbar-content"> + {# Brand and toggle get grouped for better mobile display #} + <div class="navbar-header"> + <button type="button" class="navbar-toggle collapsed" + data-toggle="collapse" data-target=".navbar-collapse"> + <span class="sr-only">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <a class="navbar-brand" href="index.html">{fmt}</a> + </div> + + {# Collect the nav links, forms, and other content for toggling #} + <div class="collapse navbar-collapse"> + <ul class="nav navbar-nav"> + <li class="dropdown"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown" + role="button" aria-expanded="false">{{ version }} + <span class="caret"></span></a> + <ul class="dropdown-menu" role="menu"> + {% for v in versions.split(',') %} + <li><a href="https://fmt.dev/{{v}}">{{v}}</a></li> + {% endfor %} + </ul> + </li> + {% for name in ['Contents', 'Usage', 'API', 'Syntax'] %} + {% if pagename == name.lower() %} + <li class="active"><a href="{{name.lower()}}.html">{{name}} + <span class="sr-only">(current)</span></a></li> + {%else%} + <li><a href="{{name.lower()}}.html">{{name}}</a></li> + {%endif%} + {% endfor %} + </ul> + {% if pagename != 'search' %} + {{ searchform('navbar-form navbar-right', False) }} + {%endif%} + </div> {# /.navbar-collapse #} + </div> {# /.col-md-offset-2 #} + </div> {# /.row #} + </div> {# /.tb-container #} +</nav> +{% if pagename == "index" %} +{% set download_url = 'https://github.com/fmtlib/fmt/releases/download' %} +<div class="jumbotron"> + <div class="tb-container"> + <h1>{fmt}</h1> + <p class="lead">A modern formatting library</p> + <div class="btn-group" role="group"> + {% set name = 'fmt' if version.split('.')[0]|int >= 3 else 'cppformat' %} + <a class="btn btn-success" + href="{{download_url}}/{{version}}/{{name}}-{{version}}.zip"> + <span class="glyphicon glyphicon-download"></span> Download + </a> + <button type="button" class="btn btn-success dropdown-toggle" + data-toggle="dropdown"><span class="caret"></span></button> + <ul class="dropdown-menu"> + {% for v in versions.split(',') %} + {% set name = 'fmt' if v.split('.')[0]|int >= 3 else 'cppformat' %} + <li><a href="{{download_url}}/{{v}}/{{name}}-{{v}}.zip">Version {{v}} + </a></li> + {% endfor %} + </ul> + </div> + </div> +</div> +{% endif %} +{% endblock %} + +{# Disable relbars. #} +{% block relbar1 %} +{% endblock %} +{% block relbar2 %} +{% endblock %} + +{% block content %} +<div class="tb-container"> + <div class="row"> + {# Sidebar is currently disabled. + <div class="bs-sidebar"> + <div class="sphinxsidebar" role="navigation" aria-label="main navigation"> + <div class="sphinxsidebarwrapper"> + {%- block sidebarlogo %} + {%- if logo %} + <p class="logo"><a href="{{ pathto(master_doc) }}"> + <img class="logo" src="{{ pathto('_static/' + logo, 1) }}" + alt="Logo"/> + </a></p> + {%- endif %} + {%- endblock %} + {%- for sidebartemplate in sidebars %} + {%- include sidebartemplate %} + {%- endfor %} + </div> + </div> + </div> + #} + + <div class="content"> + {% block body %} {% endblock %} + </div> + </div> +</div> +{% endblock %} + +{% block footer %} +{{ super() }} +{# Placed at the end of the document so the pages load faster. #} +<script src="_static/bootstrap.min.js"></script> +{% endblock %} diff --git a/src/fmt/doc/_templates/search.html b/src/fmt/doc/_templates/search.html new file mode 100644 index 000000000..488e0b5d2 --- /dev/null +++ b/src/fmt/doc/_templates/search.html @@ -0,0 +1,55 @@ +{# + basic/search.html + ~~~~~~~~~~~~~~~~~ + + Template for the search page. + + :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- extends "layout.html" %} +{% set title = _('Search') %} +{% set script_files = script_files + ['_static/searchtools.js'] %} +{% block extrahead %} + <script type="text/javascript"> + jQuery(function() { Search.loadIndex("{{ pathto('searchindex.js', 1) }}"); }); + </script> + {# this is used when loading the search index using $.ajax fails, + such as on Chrome for documents on localhost #} + <script type="text/javascript" id="searchindexloader"></script> + {{ super() }} +{% endblock %} +{% block body %} + <h1 id="search-documentation">{{ _('Search') }}</h1> + <div id="fallback" class="admonition warning"> + <script type="text/javascript">$('#fallback').hide();</script> + <p> + {% trans %}Please activate JavaScript to enable the search + functionality.{% endtrans %} + </p> + </div> + <p> + {% trans %}From here you can search these documents. Enter your search + words into the box below and click "search". Note that the search + function will automatically search for all of the words. Pages + containing fewer words won't appear in the result list.{% endtrans %} + </p> + {{ searchform('form-inline', True) }} + {% if search_performed %} + <h2>{{ _('Search Results') }}</h2> + {% if not search_results %} + <p>{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}</p> + {% endif %} + {% endif %} + <div id="search-results"> + {% if search_results %} + <ul> + {% for href, caption, context in search_results %} + <li><a href="{{ pathto(item.href) }}">{{ caption }}</a> + <div class="context">{{ context|e }}</div> + </li> + {% endfor %} + </ul> + {% endif %} + </div> +{% endblock %} diff --git a/src/fmt/doc/api.rst b/src/fmt/doc/api.rst new file mode 100644 index 000000000..711d31095 --- /dev/null +++ b/src/fmt/doc/api.rst @@ -0,0 +1,616 @@ +.. _string-formatting-api: + +************* +API Reference +************* + +The {fmt} library API consists of the following parts: + +* :ref:`fmt/core.h <core-api>`: the core API providing main formatting functions + for ``char``/UTF-8 with C++20 compile-time checks and minimal dependencies +* :ref:`fmt/format.h <format-api>`: the full format API providing additional + formatting functions and locale support +* :ref:`fmt/ranges.h <ranges-api>`: formatting of ranges and tuples +* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting +* :ref:`fmt/std.h <std-api>`: formatters for standard library types +* :ref:`fmt/compile.h <compile-api>`: format string compilation +* :ref:`fmt/color.h <color-api>`: terminal color and text style +* :ref:`fmt/os.h <os-api>`: system APIs +* :ref:`fmt/ostream.h <ostream-api>`: ``std::ostream`` support +* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting +* :ref:`fmt/xchar.h <xchar-api>`: optional ``wchar_t`` support + +All functions and types provided by the library reside in namespace ``fmt`` and +macros have prefix ``FMT_``. + +.. _core-api: + +Core API +======== + +``fmt/core.h`` defines the core API which provides main formatting functions +for ``char``/UTF-8 with C++20 compile-time checks. It has minimal include +dependencies for better compile times. This header is only beneficial when +using {fmt} as a library and not in the header-only mode. + +The following functions use :ref:`format string syntax <syntax>` +similar to that of Python's `str.format +<https://docs.python.org/3/library/stdtypes.html#str.format>`_. +They take *fmt* and *args* as arguments. + +*fmt* is a format string that contains literal text and replacement fields +surrounded by braces ``{}``. The fields are replaced with formatted arguments +in the resulting string. `~fmt::format_string` is a format string which can be +implicitly constructed from a string literal or a ``constexpr`` string and is +checked at compile time in C++20. To pass a runtime format string wrap it in +`fmt::runtime`. + +*args* is an argument list representing objects to be formatted. + +.. _format: + +.. doxygenfunction:: format(format_string<T...> fmt, T&&... args) -> std::string +.. doxygenfunction:: vformat(string_view fmt, format_args args) -> std::string + +.. doxygenfunction:: format_to(OutputIt out, format_string<T...> fmt, T&&... args) -> OutputIt +.. doxygenfunction:: format_to_n(OutputIt out, size_t n, format_string<T...> fmt, T&&... args) -> format_to_n_result<OutputIt> +.. doxygenfunction:: formatted_size(format_string<T...> fmt, T&&... args) -> size_t + +.. doxygenstruct:: fmt::format_to_n_result + :members: + +.. _print: + +.. doxygenfunction:: fmt::print(format_string<T...> fmt, T&&... args) +.. doxygenfunction:: fmt::vprint(string_view fmt, format_args args) + +.. doxygenfunction:: print(std::FILE *f, format_string<T...> fmt, T&&... args) +.. doxygenfunction:: vprint(std::FILE *f, string_view fmt, format_args args) + +Compile-Time Format String Checks +--------------------------------- + +Compile-time checks are enabled by default on compilers that support C++20 +``consteval``. On older compilers you can use the ``FMT_STRING`` macro defined +in ``fmt/format.h`` instead. It requires C++14 and is a no-op in C++11. + +.. doxygendefine:: FMT_STRING + +To force the use of legacy compile-time checks, define the preprocessor variable +``FMT_ENFORCE_COMPILE_STRING``. When set, functions accepting ``FMT_STRING`` +will fail to compile with regular strings. Runtime-checked formatting is still +possible using ``fmt::vformat``, ``fmt::vprint``, etc. + +.. doxygenclass:: fmt::basic_format_string + :members: + +.. doxygentypedef:: fmt::format_string + +.. doxygenfunction:: fmt::runtime(string_view) -> basic_runtime<char> + +Named Arguments +--------------- + +.. doxygenfunction:: fmt::arg(const S&, const T&) + +Named arguments are not supported in compile-time checks at the moment. + +Argument Lists +-------------- + +You can create your own formatting function with compile-time checks and small +binary footprint, for example (https://godbolt.org/z/oba4Mc): + +.. code:: c++ + + #include <fmt/format.h> + + void vlog(const char* file, int line, fmt::string_view format, + fmt::format_args args) { + fmt::print("{}: {}: ", file, line); + fmt::vprint(format, args); + } + + template <typename S, typename... Args> + void log(const char* file, int line, const S& format, Args&&... args) { + vlog(file, line, format, fmt::make_format_args(args...)); + } + + #define MY_LOG(format, ...) \ + log(__FILE__, __LINE__, FMT_STRING(format), __VA_ARGS__) + + MY_LOG("invalid squishiness: {}", 42); + +Note that ``vlog`` is not parameterized on argument types which improves compile +times and reduces binary code size compared to a fully parameterized version. + +.. doxygenfunction:: fmt::make_format_args(const Args&...) + +.. doxygenclass:: fmt::format_arg_store + :members: + +.. doxygenclass:: fmt::dynamic_format_arg_store + :members: + +.. doxygenclass:: fmt::basic_format_args + :members: + +.. doxygentypedef:: fmt::format_args + +.. doxygenclass:: fmt::basic_format_arg + :members: + +.. doxygenclass:: fmt::basic_format_parse_context + :members: + +.. doxygenclass:: fmt::basic_format_context + :members: + +.. doxygentypedef:: fmt::format_context + +Compatibility +------------- + +.. doxygenclass:: fmt::basic_string_view + :members: + +.. doxygentypedef:: fmt::string_view + +Locale +------ + +All formatting is locale-independent by default. Use the ``'L'`` format +specifier to insert the appropriate number separator characters from the +locale:: + + #include <fmt/core.h> + #include <locale> + + std::locale::global(std::locale("en_US.UTF-8")); + auto s = fmt::format("{:L}", 1000000); // s == "1,000,000" + +.. _format-api: + +Format API +========== + +``fmt/format.h`` defines the full format API providing additional formatting +functions and locale support. + +.. _udt: + +Formatting User-Defined Types +----------------------------- + +The {fmt} library provides formatters for many standard C++ types. +See :ref:`fmt/ranges.h <ranges-api>` for ranges and tuples including standard +containers such as ``std::vector``, :ref:`fmt/chrono.h <chrono-api>` for date +and time formatting and :ref:`fmt/std.h <std-api>` for path and variant +formatting. + +To make a user-defined type formattable, specialize the ``formatter<T>`` struct +template and implement ``parse`` and ``format`` methods:: + + #include <fmt/format.h> + + struct point { + double x, y; + }; + + template <> struct fmt::formatter<point> { + // Presentation format: 'f' - fixed, 'e' - exponential. + char presentation = 'f'; + + // Parses format specifications of the form ['f' | 'e']. + constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + // [ctx.begin(), ctx.end()) is a character range that contains a part of + // the format string starting from the format specifications to be parsed, + // e.g. in + // + // fmt::format("{:f} - point of interest", point{1, 2}); + // + // the range will contain "f} - point of interest". The formatter should + // parse specifiers until '}' or the end of the range. In this example + // the formatter should parse the 'f' specifier and return an iterator + // pointing to '}'. + + // Please also note that this character range may be empty, in case of + // the "{}" format string, so therefore you should check ctx.begin() + // for equality with ctx.end(). + + // Parse the presentation format and store it in the formatter: + auto it = ctx.begin(), end = ctx.end(); + if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++; + + // Check if reached the end of the range: + if (it != end && *it != '}') throw format_error("invalid format"); + + // Return an iterator past the end of the parsed range: + return it; + } + + // Formats the point p using the parsed format specification (presentation) + // stored in this formatter. + template <typename FormatContext> + auto format(const point& p, FormatContext& ctx) const -> decltype(ctx.out()) { + // ctx.out() is an output iterator to write to. + return presentation == 'f' + ? fmt::format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y) + : fmt::format_to(ctx.out(), "({:.1e}, {:.1e})", p.x, p.y); + } + }; + +Then you can pass objects of type ``point`` to any formatting function:: + + point p = {1, 2}; + std::string s = fmt::format("{:f}", p); + // s == "(1.0, 2.0)" + +You can also reuse existing formatters via inheritance or composition, for +example:: + + enum class color {red, green, blue}; + + template <> struct fmt::formatter<color>: formatter<string_view> { + // parse is inherited from formatter<string_view>. + template <typename FormatContext> + auto format(color c, FormatContext& ctx) const { + string_view name = "unknown"; + switch (c) { + case color::red: name = "red"; break; + case color::green: name = "green"; break; + case color::blue: name = "blue"; break; + } + return formatter<string_view>::format(name, ctx); + } + }; + +Since ``parse`` is inherited from ``formatter<string_view>`` it will recognize +all string format specifications, for example + +.. code-block:: c++ + + fmt::format("{:>10}", color::blue) + +will return ``" blue"``. + +You can also write a formatter for a hierarchy of classes:: + + #include <type_traits> + #include <fmt/format.h> + + struct A { + virtual ~A() {} + virtual std::string name() const { return "A"; } + }; + + struct B : A { + virtual std::string name() const { return "B"; } + }; + + template <typename T> + struct fmt::formatter<T, std::enable_if_t<std::is_base_of<A, T>::value, char>> : + fmt::formatter<std::string> { + template <typename FormatCtx> + auto format(const A& a, FormatCtx& ctx) const { + return fmt::formatter<std::string>::format(a.name(), ctx); + } + }; + + int main() { + B b; + A& a = b; + fmt::print("{}", a); // prints "B" + } + +If a type provides both a ``formatter`` specialization and an implicit +conversion to a formattable type, the specialization takes precedence over the +conversion. + +For enums {fmt} also provides the ``format_as`` extension API. To format an enum +via this API define ``format_as`` that takes this enum and converts it to the +underlying type. ``format_as`` should be defined in the same namespace as the +enum. + +Example (https://godbolt.org/z/r7vvGE1v7):: + + #include <fmt/format.h> + + namespace kevin_namespacy { + enum class film { + house_of_cards, american_beauty, se7en = 7 + }; + auto format_as(film f) { return fmt::underlying(f); } + } + + int main() { + fmt::print("{}\n", kevin_namespacy::film::se7en); // prints "7" + } + +Literal-Based API +----------------- + +The following user-defined literals are defined in ``fmt/format.h``. + +.. doxygenfunction:: operator""_a() + +Utilities +--------- + +.. doxygenfunction:: fmt::ptr(T p) -> const void* +.. doxygenfunction:: fmt::ptr(const std::unique_ptr<T> &p) -> const void* +.. doxygenfunction:: fmt::ptr(const std::shared_ptr<T> &p) -> const void* + +.. doxygenfunction:: fmt::underlying(Enum e) -> typename std::underlying_type<Enum>::type + +.. doxygenfunction:: fmt::to_string(const T &value) -> std::string + +.. doxygenfunction:: fmt::join(Range &&range, string_view sep) -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>> + +.. doxygenfunction:: fmt::join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> + +.. doxygenfunction:: fmt::group_digits(T value) -> group_digits_view<T> + +.. doxygenclass:: fmt::detail::buffer + :members: + +.. doxygenclass:: fmt::basic_memory_buffer + :protected-members: + :members: + +System Errors +------------- + +{fmt} does not use ``errno`` to communicate errors to the user, but it may call +system functions which set ``errno``. Users should not make any assumptions +about the value of ``errno`` being preserved by library functions. + +.. doxygenfunction:: fmt::system_error + +.. doxygenfunction:: fmt::format_system_error + +Custom Allocators +----------------- + +The {fmt} library supports custom dynamic memory allocators. +A custom allocator class can be specified as a template argument to +:class:`fmt::basic_memory_buffer`:: + + using custom_memory_buffer = + fmt::basic_memory_buffer<char, fmt::inline_buffer_size, custom_allocator>; + +It is also possible to write a formatting function that uses a custom +allocator:: + + using custom_string = + std::basic_string<char, std::char_traits<char>, custom_allocator>; + + custom_string vformat(custom_allocator alloc, fmt::string_view format_str, + fmt::format_args args) { + auto buf = custom_memory_buffer(alloc); + fmt::vformat_to(std::back_inserter(buf), format_str, args); + return custom_string(buf.data(), buf.size(), alloc); + } + + template <typename ...Args> + inline custom_string format(custom_allocator alloc, + fmt::string_view format_str, + const Args& ... args) { + return vformat(alloc, format_str, fmt::make_format_args(args...)); + } + +The allocator will be used for the output container only. Formatting functions +normally don't do any allocations for built-in and string types except for +non-default floating-point formatting that occasionally falls back on +``sprintf``. + +.. _ranges-api: + +Range and Tuple Formatting +========================== + +The library also supports convenient formatting of ranges and tuples:: + + #include <fmt/ranges.h> + + std::tuple<char, int, float> t{'a', 1, 2.0f}; + // Prints "('a', 1, 2.0)" + fmt::print("{}", t); + + +NOTE: currently, the overload of ``fmt::join`` for iterables exists in the main +``format.h`` header, but expect this to change in the future. + +Using ``fmt::join``, you can separate tuple elements with a custom separator:: + + #include <fmt/ranges.h> + + std::tuple<int, char> t = {1, 'a'}; + // Prints "1, a" + fmt::print("{}", fmt::join(t, ", ")); + +.. _chrono-api: + +Date and Time Formatting +======================== + +``fmt/chrono.h`` provides formatters for + +* `std::chrono::duration <https://en.cppreference.com/w/cpp/chrono/duration>`_ +* `std::chrono::time_point + <https://en.cppreference.com/w/cpp/chrono/time_point>`_ +* `std::tm <https://en.cppreference.com/w/cpp/chrono/c/tm>`_ + +The format syntax is described in :ref:`chrono-specs`. + +**Example**:: + + #include <fmt/chrono.h> + + int main() { + std::time_t t = std::time(nullptr); + + // Prints "The date is 2020-11-07." (with the current date): + fmt::print("The date is {:%Y-%m-%d}.", fmt::localtime(t)); + + using namespace std::literals::chrono_literals; + + // Prints "Default format: 42s 100ms": + fmt::print("Default format: {} {}\n", 42s, 100ms); + + // Prints "strftime-like format: 03:15:30": + fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s); + } + +.. doxygenfunction:: localtime(std::time_t time) + +.. doxygenfunction:: gmtime(std::time_t time) + +.. _std-api: + +Standard Library Types Formatting +================================= + +``fmt/std.h`` provides formatters for: + +* `std::filesystem::path <https://en.cppreference.com/w/cpp/filesystem/path>`_ +* `std::thread::id <https://en.cppreference.com/w/cpp/thread/thread/id>`_ +* `std::monostate <https://en.cppreference.com/w/cpp/utility/variant/monostate>`_ +* `std::variant <https://en.cppreference.com/w/cpp/utility/variant/variant>`_ + +Formatting Variants +------------------- + +A ``std::variant`` is only formattable if every variant alternative is formattable, and requires the +``__cpp_lib_variant`` `library feature <https://en.cppreference.com/w/cpp/feature_test>`_. + +**Example**:: + + #include <fmt/std.h> + + std::variant<char, float> v0{'x'}; + // Prints "variant('x')" + fmt::print("{}", v0); + + std::variant<std::monostate, char> v1; + // Prints "variant(monostate)" + +.. _compile-api: + +Format String Compilation +========================= + +``fmt/compile.h`` provides format string compilation enabled via the +``FMT_COMPILE`` macro or the ``_cf`` user-defined literal. Format strings +marked with ``FMT_COMPILE`` or ``_cf`` are parsed, checked and converted into +efficient formatting code at compile-time. This supports arguments of built-in +and string types as well as user-defined types with ``constexpr`` ``parse`` +functions in their ``formatter`` specializations. Format string compilation can +generate more binary code compared to the default API and is only recommended in +places where formatting is a performance bottleneck. + +.. doxygendefine:: FMT_COMPILE + +.. doxygenfunction:: operator""_cf() + +.. _color-api: + +Terminal Color and Text Style +============================= + +``fmt/color.h`` provides support for terminal color and text style output. + +.. doxygenfunction:: print(const text_style &ts, const S &format_str, const Args&... args) + +.. doxygenfunction:: fg(detail::color_type) + +.. doxygenfunction:: bg(detail::color_type) + +.. doxygenfunction:: styled(const T& value, text_style ts) + +.. _os-api: + +System APIs +=========== + +.. doxygenclass:: fmt::ostream + :members: + +.. doxygenfunction:: fmt::windows_error + :members: + +.. _ostream-api: + +``std::ostream`` Support +======================== + +``fmt/ostream.h`` provides ``std::ostream`` support including formatting of +user-defined types that have an overloaded insertion operator (``operator<<``). +In order to make a type formattable via ``std::ostream`` you should provide a +``formatter`` specialization inherited from ``ostream_formatter``:: + + #include <fmt/ostream.h> + + struct date { + int year, month, day; + + friend std::ostream& operator<<(std::ostream& os, const date& d) { + return os << d.year << '-' << d.month << '-' << d.day; + } + }; + + template <> struct fmt::formatter<date> : ostream_formatter {}; + + std::string s = fmt::format("The date is {}", date{2012, 12, 9}); + // s == "The date is 2012-12-9" + +.. doxygenfunction:: streamed(const T &) + +.. doxygenfunction:: print(std::ostream &os, format_string<T...> fmt, T&&... args) + +.. _printf-api: + +``printf`` Formatting +===================== + +The header ``fmt/printf.h`` provides ``printf``-like formatting functionality. +The following functions use `printf format string syntax +<https://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html>`_ with +the POSIX extension for positional arguments. Unlike their standard +counterparts, the ``fmt`` functions are type-safe and throw an exception if an +argument type doesn't match its format specification. + +.. doxygenfunction:: printf(const S &format_str, const T&... args) + +.. doxygenfunction:: fprintf(std::FILE *f, const S &fmt, const T&... args) -> int + +.. doxygenfunction:: sprintf(const S&, const T&...) + +.. _xchar-api: + +``wchar_t`` Support +=================== + +The optional header ``fmt/xchar.h`` provides support for ``wchar_t`` and exotic +character types. + +.. doxygenstruct:: fmt::is_char + +.. doxygentypedef:: fmt::wstring_view + +.. doxygentypedef:: fmt::wformat_context + +.. doxygenfunction:: fmt::to_wstring(const T &value) + +Compatibility with C++20 ``std::format`` +======================================== + +{fmt} implements nearly all of the `C++20 formatting library +<https://en.cppreference.com/w/cpp/utility/format>`_ with the following +differences: + +* Names are defined in the ``fmt`` namespace instead of ``std`` to avoid + collisions with standard library implementations. +* Width calculation doesn't use grapheme clusterization. The latter has been + implemented in a separate branch but hasn't been integrated yet. +* Most C++20 chrono types are not supported yet. diff --git a/src/fmt/doc/basic-bootstrap/README b/src/fmt/doc/basic-bootstrap/README new file mode 100644 index 000000000..ba67700df --- /dev/null +++ b/src/fmt/doc/basic-bootstrap/README @@ -0,0 +1,2 @@ +Sphinx basic theme with Bootstrap support. Modifications are kept to +a minimum to simplify integration in case of changes to Sphinx theming. diff --git a/src/fmt/doc/basic-bootstrap/layout.html b/src/fmt/doc/basic-bootstrap/layout.html new file mode 100644 index 000000000..5519c4b5c --- /dev/null +++ b/src/fmt/doc/basic-bootstrap/layout.html @@ -0,0 +1,208 @@ +{# + basic/layout.html + ~~~~~~~~~~~~~~~~~ + + Master layout template for Sphinx themes. + + :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- block doctype -%} +<!DOCTYPE html> +{%- endblock %} +{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} +{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} +{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and + (sidebars != []) %} +{%- set url_root = pathto('', 1) %} +{# XXX necessary? #} +{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} +{%- if not embedded and docstitle %} + {%- set titlesuffix = " — "|safe + docstitle|e %} +{%- else %} + {%- set titlesuffix = "" %} +{%- endif %} + +{%- macro relbar() %} + <div class="related" role="navigation" aria-label="related navigation"> + <h3>{{ _('Navigation') }}</h3> + <ul> + {%- for rellink in rellinks %} + <li class="right" {% if loop.first %}style="margin-right: 10px"{% endif %}> + <a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags|e }}" + {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a> + {%- if not loop.first %}{{ reldelim2 }}{% endif %}</li> + {%- endfor %} + {%- block rootrellink %} + <li class="nav-item nav-item-0"><a href="{{ pathto(master_doc) }}">{{ shorttitle|e }}</a>{{ reldelim1 }}</li> + {%- endblock %} + {%- for parent in parents %} + <li class="nav-item nav-item-{{ loop.index }}"><a href="{{ parent.link|e }}" {% if loop.last %}{{ accesskey("U") }}{% endif %}>{{ parent.title }}</a>{{ reldelim1 }}</li> + {%- endfor %} + {%- block relbaritems %} {% endblock %} + </ul> + </div> +{%- endmacro %} + +{%- macro sidebar() %} + {%- if render_sidebar %} + <div class="sphinxsidebar" role="navigation" aria-label="main navigation"> + <div class="sphinxsidebarwrapper"> + {%- block sidebarlogo %} + {%- if logo %} + <p class="logo"><a href="{{ pathto(master_doc) }}"> + <img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/> + </a></p> + {%- endif %} + {%- endblock %} + {%- if sidebars != None %} + {#- new style sidebar: explicitly include/exclude templates #} + {%- for sidebartemplate in sidebars %} + {%- include sidebartemplate %} + {%- endfor %} + {%- else %} + {#- old style sidebars: using blocks -- should be deprecated #} + {%- block sidebartoc %} + {%- include "localtoc.html" %} + {%- endblock %} + {%- block sidebarrel %} + {%- include "relations.html" %} + {%- endblock %} + {%- block sidebarsourcelink %} + {%- include "sourcelink.html" %} + {%- endblock %} + {%- if customsidebar %} + {%- include customsidebar %} + {%- endif %} + {%- block sidebarsearch %} + {%- include "searchbox.html" %} + {%- endblock %} + {%- endif %} + </div> + </div> + {%- endif %} +{%- endmacro %} + +{%- macro script() %} + <script type="text/javascript"> + var DOCUMENTATION_OPTIONS = { + URL_ROOT: '{{ url_root }}', + VERSION: '{{ release|e }}', + COLLAPSE_INDEX: false, + FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}', + LINK_SUFFIX: '{{ link_suffix }}', + SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}', + HAS_SOURCE: {{ has_source|lower }}, + SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}' + }; + </script> + {%- for scriptfile in script_files %} + {{ js_tag(scriptfile) }} + {%- endfor %} +{%- endmacro %} + +{%- macro css() %} + <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" /> + <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" /> + {%- for cssfile in css_files %} + <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" /> + {%- endfor %} +{%- endmacro %} + +<html lang="en"> + <head> + <meta charset="{{ encoding }}"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + {# The above 3 meta tags *must* come first in the head; any other head content + must come *after* these tags. #} + {{ metatags }} + {%- block htmltitle %} + <title>{{ title|striptags|e }}{{ titlesuffix }}</title> + {%- endblock %} + {{ css() }} + {%- if not embedded %} + {{ script() }} + {%- if use_opensearch %} + <link rel="search" type="application/opensearchdescription+xml" + title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}" + href="{{ pathto('_static/opensearch.xml', 1) }}"/> + {%- endif %} + {%- if favicon %} + <link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/> + {%- endif %} + {%- endif %} +{%- block linktags %} + {%- if hasdoc('about') %} + <link rel="author" title="{{ _('About these documents') }}" href="{{ pathto('about') }}" /> + {%- endif %} + {%- if hasdoc('genindex') %} + <link rel="index" title="{{ _('Index') }}" href="{{ pathto('genindex') }}" /> + {%- endif %} + {%- if hasdoc('search') %} + <link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}" /> + {%- endif %} + {%- if hasdoc('copyright') %} + <link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}" /> + {%- endif %} + {%- if parents %} + <link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}" /> + {%- endif %} + {%- if next %} + <link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}" /> + {%- endif %} + {%- if prev %} + <link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}" /> + {%- endif %} +{%- endblock %} +{%- block extrahead %} {% endblock %} + </head> + <body role="document"> +{%- block header %}{% endblock %} + +{%- block relbar1 %}{{ relbar() }}{% endblock %} + +{%- block content %} + {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %} + + <div class="document"> + {%- block document %} + <div class="documentwrapper"> + {%- if render_sidebar %} + <div class="bodywrapper"> + {%- endif %} + <div class="body" role="main"> + {% block body %} {% endblock %} + </div> + {%- if render_sidebar %} + </div> + {%- endif %} + </div> + {%- endblock %} + + {%- block sidebar2 %}{{ sidebar() }}{% endblock %} + <div class="clearer"></div> + </div> +{%- endblock %} + +{%- block relbar2 %}{{ relbar() }}{% endblock %} + +{%- block footer %} + <div class="footer" role="contentinfo"> + {%- if show_copyright %} + {%- if hasdoc('copyright') %} + {% trans path=pathto('copyright'), copyright=copyright|e %}© <a href="{{ path }}">Copyright</a> {{ copyright }}.{% endtrans %} + {%- else %} + {% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} + {%- endif %} + {%- endif %} + {%- if last_updated %} + {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %} + {%- endif %} + {%- if show_sphinx %} + {% trans sphinx_version=sphinx_version|e %}Created using <a href="http://sphinx-doc.org/">Sphinx</a> {{ sphinx_version }}.{% endtrans %} + {%- endif %} + </div> +{%- endblock %} + </body> +</html> diff --git a/src/fmt/doc/basic-bootstrap/theme.conf b/src/fmt/doc/basic-bootstrap/theme.conf new file mode 100644 index 000000000..89e03bbda --- /dev/null +++ b/src/fmt/doc/basic-bootstrap/theme.conf @@ -0,0 +1,2 @@ +[theme] +inherit = basic diff --git a/src/fmt/doc/bootstrap/alerts.less b/src/fmt/doc/bootstrap/alerts.less new file mode 100644 index 000000000..c4199db92 --- /dev/null +++ b/src/fmt/doc/bootstrap/alerts.less @@ -0,0 +1,73 @@ +// +// Alerts +// -------------------------------------------------- + + +// Base styles +// ------------------------- + +.alert { + padding: @alert-padding; + margin-bottom: @line-height-computed; + border: 1px solid transparent; + border-radius: @alert-border-radius; + + // Headings for larger alerts + h4 { + margin-top: 0; + // Specified for the h4 to prevent conflicts of changing @headings-color + color: inherit; + } + + // Provide class for links that match alerts + .alert-link { + font-weight: @alert-link-font-weight; + } + + // Improve alignment and spacing of inner content + > p, + > ul { + margin-bottom: 0; + } + + > p + p { + margin-top: 5px; + } +} + +// Dismissible alerts +// +// Expand the right padding and account for the close button's positioning. + +.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0. +.alert-dismissible { + padding-right: (@alert-padding + 20); + + // Adjust close link position + .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; + } +} + +// Alternate styles +// +// Generate contextual modifier classes for colorizing the alert. + +.alert-success { + .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text); +} + +.alert-info { + .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text); +} + +.alert-warning { + .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text); +} + +.alert-danger { + .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text); +} diff --git a/src/fmt/doc/bootstrap/badges.less b/src/fmt/doc/bootstrap/badges.less new file mode 100644 index 000000000..c70bb939e --- /dev/null +++ b/src/fmt/doc/bootstrap/badges.less @@ -0,0 +1,66 @@ +// +// Badges +// -------------------------------------------------- + + +// Base class +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: @font-size-small; + font-weight: @badge-font-weight; + color: @badge-color; + line-height: @badge-line-height; + vertical-align: baseline; + white-space: nowrap; + text-align: center; + background-color: @badge-bg; + border-radius: @badge-border-radius; + + // Empty badges collapse automatically (not available in IE8) + &:empty { + display: none; + } + + // Quick fix for badges in buttons + .btn & { + position: relative; + top: -1px; + } + + .btn-xs &, + .btn-group-xs > .btn & { + top: 0; + padding: 1px 5px; + } + + // Hover state, but only for links + a& { + &:hover, + &:focus { + color: @badge-link-hover-color; + text-decoration: none; + cursor: pointer; + } + } + + // Account for badges in navs + .list-group-item.active > &, + .nav-pills > .active > a > & { + color: @badge-active-color; + background-color: @badge-active-bg; + } + + .list-group-item > & { + float: right; + } + + .list-group-item > & + & { + margin-right: 5px; + } + + .nav-pills > li > a > & { + margin-left: 3px; + } +} diff --git a/src/fmt/doc/bootstrap/bootstrap.less b/src/fmt/doc/bootstrap/bootstrap.less new file mode 100644 index 000000000..61b77474f --- /dev/null +++ b/src/fmt/doc/bootstrap/bootstrap.less @@ -0,0 +1,50 @@ +// Core variables and mixins +@import "variables.less"; +@import "mixins.less"; + +// Reset and dependencies +@import "normalize.less"; +@import "print.less"; +@import "glyphicons.less"; + +// Core CSS +@import "scaffolding.less"; +@import "type.less"; +@import "code.less"; +@import "grid.less"; +@import "tables.less"; +@import "forms.less"; +@import "buttons.less"; + +// Components +@import "component-animations.less"; +@import "dropdowns.less"; +@import "button-groups.less"; +@import "input-groups.less"; +@import "navs.less"; +@import "navbar.less"; +@import "breadcrumbs.less"; +@import "pagination.less"; +@import "pager.less"; +@import "labels.less"; +@import "badges.less"; +@import "jumbotron.less"; +@import "thumbnails.less"; +@import "alerts.less"; +@import "progress-bars.less"; +@import "media.less"; +@import "list-group.less"; +@import "panels.less"; +@import "responsive-embed.less"; +@import "wells.less"; +@import "close.less"; + +// Components w/ JavaScript +@import "modals.less"; +@import "tooltip.less"; +@import "popovers.less"; +@import "carousel.less"; + +// Utility classes +@import "utilities.less"; +@import "responsive-utilities.less"; diff --git a/src/fmt/doc/bootstrap/breadcrumbs.less b/src/fmt/doc/bootstrap/breadcrumbs.less new file mode 100644 index 000000000..cb01d503f --- /dev/null +++ b/src/fmt/doc/bootstrap/breadcrumbs.less @@ -0,0 +1,26 @@ +// +// Breadcrumbs +// -------------------------------------------------- + + +.breadcrumb { + padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal; + margin-bottom: @line-height-computed; + list-style: none; + background-color: @breadcrumb-bg; + border-radius: @border-radius-base; + + > li { + display: inline-block; + + + li:before { + content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space + padding: 0 5px; + color: @breadcrumb-color; + } + } + + > .active { + color: @breadcrumb-active-color; + } +} diff --git a/src/fmt/doc/bootstrap/button-groups.less b/src/fmt/doc/bootstrap/button-groups.less new file mode 100644 index 000000000..13c1800de --- /dev/null +++ b/src/fmt/doc/bootstrap/button-groups.less @@ -0,0 +1,243 @@ +// +// Button groups +// -------------------------------------------------- + +// Make the div behave like a button +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; // match .btn alignment given font-size hack above + > .btn { + position: relative; + float: left; + // Bring the "active" button to the front + &:hover, + &:focus, + &:active, + &.active { + z-index: 2; + } + } +} + +// Prevent double borders when buttons are next to each other +.btn-group { + .btn + .btn, + .btn + .btn-group, + .btn-group + .btn, + .btn-group + .btn-group { + margin-left: -1px; + } +} + +// Optional: Group multiple button groups together for a toolbar +.btn-toolbar { + margin-left: -5px; // Offset the first child's margin + &:extend(.clearfix all); + + .btn-group, + .input-group { + float: left; + } + > .btn, + > .btn-group, + > .input-group { + margin-left: 5px; + } +} + +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} + +// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match +.btn-group > .btn:first-child { + margin-left: 0; + &:not(:last-child):not(.dropdown-toggle) { + .border-right-radius(0); + } +} +// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + .border-left-radius(0); +} + +// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group) +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child:not(:last-child) { + > .btn:last-child, + > .dropdown-toggle { + .border-right-radius(0); + } +} +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + .border-left-radius(0); +} + +// On active and open, don't show outline +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} + + +// Sizing +// +// Remix the default button sizing classes into new ones for easier manipulation. + +.btn-group-xs > .btn { &:extend(.btn-xs); } +.btn-group-sm > .btn { &:extend(.btn-sm); } +.btn-group-lg > .btn { &:extend(.btn-lg); } + + +// Split button dropdowns +// ---------------------- + +// Give the line between buttons some depth +.btn-group > .btn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; +} + +// The clickable button for toggling the menu +// Remove the gradient and set the same inset shadow as the :active state +.btn-group.open .dropdown-toggle { + .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); + + // Show no shadow for `.btn-link` since it has no other button styles. + &.btn-link { + .box-shadow(none); + } +} + + +// Reposition the caret +.btn .caret { + margin-left: 0; +} +// Carets in other button sizes +.btn-lg .caret { + border-width: @caret-width-large @caret-width-large 0; + border-bottom-width: 0; +} +// Upside down carets for .dropup +.dropup .btn-lg .caret { + border-width: 0 @caret-width-large @caret-width-large; +} + + +// Vertical button groups +// ---------------------- + +.btn-group-vertical { + > .btn, + > .btn-group, + > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; + } + + // Clear floats so dropdown menus can be properly placed + > .btn-group { + &:extend(.clearfix all); + > .btn { + float: none; + } + } + + > .btn + .btn, + > .btn + .btn-group, + > .btn-group + .btn, + > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; + } +} + +.btn-group-vertical > .btn { + &:not(:first-child):not(:last-child) { + border-radius: 0; + } + &:first-child:not(:last-child) { + border-top-right-radius: @border-radius-base; + .border-bottom-radius(0); + } + &:last-child:not(:first-child) { + border-bottom-left-radius: @border-radius-base; + .border-top-radius(0); + } +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) { + > .btn:last-child, + > .dropdown-toggle { + .border-bottom-radius(0); + } +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + .border-top-radius(0); +} + + +// Justified button groups +// ---------------------- + +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; + > .btn, + > .btn-group { + float: none; + display: table-cell; + width: 1%; + } + > .btn-group .btn { + width: 100%; + } + + > .btn-group .dropdown-menu { + left: auto; + } +} + + +// Checkbox and radio options +// +// In order to support the browser's form validation feedback, powered by the +// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use +// `display: none;` or `visibility: hidden;` as that also hides the popover. +// Simply visually hiding the inputs via `opacity` would leave them clickable in +// certain cases which is prevented by using `clip` and `pointer-events`. +// This way, we ensure a DOM element is visible to position the popover from. +// +// See https://github.com/twbs/bootstrap/pull/12794 and +// https://github.com/twbs/bootstrap/pull/14559 for more information. + +[data-toggle="buttons"] { + > .btn, + > .btn-group > .btn { + input[type="radio"], + input[type="checkbox"] { + position: absolute; + clip: rect(0,0,0,0); + pointer-events: none; + } + } +} diff --git a/src/fmt/doc/bootstrap/buttons.less b/src/fmt/doc/bootstrap/buttons.less new file mode 100644 index 000000000..5a746049e --- /dev/null +++ b/src/fmt/doc/bootstrap/buttons.less @@ -0,0 +1,160 @@ +// +// Buttons +// -------------------------------------------------- + + +// Base styles +// -------------------------------------------------- + +.btn { + display: inline-block; + margin-bottom: 0; // For input.btn + font-weight: @btn-font-weight; + text-align: center; + vertical-align: middle; + touch-action: manipulation; + cursor: pointer; + background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 + border: 1px solid transparent; + white-space: nowrap; + .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base); + .user-select(none); + + &, + &:active, + &.active { + &:focus, + &.focus { + .tab-focus(); + } + } + + &:hover, + &:focus, + &.focus { + color: @btn-default-color; + text-decoration: none; + } + + &:active, + &.active { + outline: 0; + background-image: none; + .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); + } + + &.disabled, + &[disabled], + fieldset[disabled] & { + cursor: @cursor-disabled; + pointer-events: none; // Future-proof disabling of clicks + .opacity(.65); + .box-shadow(none); + } +} + + +// Alternate buttons +// -------------------------------------------------- + +.btn-default { + .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border); +} +.btn-primary { + .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border); +} +// Success appears as green +.btn-success { + .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border); +} +// Info appears as blue-green +.btn-info { + .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border); +} +// Warning appears as orange +.btn-warning { + .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border); +} +// Danger and error appear as red +.btn-danger { + .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border); +} + + +// Link buttons +// ------------------------- + +// Make a button look and behave like a link +.btn-link { + color: @link-color; + font-weight: normal; + border-radius: 0; + + &, + &:active, + &.active, + &[disabled], + fieldset[disabled] & { + background-color: transparent; + .box-shadow(none); + } + &, + &:hover, + &:focus, + &:active { + border-color: transparent; + } + &:hover, + &:focus { + color: @link-hover-color; + text-decoration: @link-hover-decoration; + background-color: transparent; + } + &[disabled], + fieldset[disabled] & { + &:hover, + &:focus { + color: @btn-link-disabled-color; + text-decoration: none; + } + } +} + + +// Button Sizes +// -------------------------------------------------- + +.btn-lg { + // line-height: ensure even-numbered height of button next to large input + .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large); +} +.btn-sm { + // line-height: ensure proper height of button next to small input + .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small); +} +.btn-xs { + .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @border-radius-small); +} + + +// Block button +// -------------------------------------------------- + +.btn-block { + display: block; + width: 100%; +} + +// Vertically space out multiple block buttons +.btn-block + .btn-block { + margin-top: 5px; +} + +// Specificity overrides +input[type="submit"], +input[type="reset"], +input[type="button"] { + &.btn-block { + width: 100%; + } +} diff --git a/src/fmt/doc/bootstrap/carousel.less b/src/fmt/doc/bootstrap/carousel.less new file mode 100644 index 000000000..4bbe946d3 --- /dev/null +++ b/src/fmt/doc/bootstrap/carousel.less @@ -0,0 +1,269 @@ +// +// Carousel +// -------------------------------------------------- + + +// Wrapper for the slide container and indicators +.carousel { + position: relative; +} + +.carousel-inner { + position: relative; + overflow: hidden; + width: 100%; + + > .item { + display: none; + position: relative; + .transition(.6s ease-in-out left); + + // Account for jankitude on images + > img, + > a > img { + &:extend(.img-responsive); + line-height: 1; + } + + // WebKit CSS3 transforms for supported devices + @media all and (transform-3d), (-webkit-transform-3d) { + .transition-transform(~'0.6s ease-in-out'); + .backface-visibility(~'hidden'); + .perspective(1000); + + &.next, + &.active.right { + .translate3d(100%, 0, 0); + left: 0; + } + &.prev, + &.active.left { + .translate3d(-100%, 0, 0); + left: 0; + } + &.next.left, + &.prev.right, + &.active { + .translate3d(0, 0, 0); + left: 0; + } + } + } + + > .active, + > .next, + > .prev { + display: block; + } + + > .active { + left: 0; + } + + > .next, + > .prev { + position: absolute; + top: 0; + width: 100%; + } + + > .next { + left: 100%; + } + > .prev { + left: -100%; + } + > .next.left, + > .prev.right { + left: 0; + } + + > .active.left { + left: -100%; + } + > .active.right { + left: 100%; + } + +} + +// Left/right controls for nav +// --------------------------- + +.carousel-control { + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: @carousel-control-width; + .opacity(@carousel-control-opacity); + font-size: @carousel-control-font-size; + color: @carousel-control-color; + text-align: center; + text-shadow: @carousel-text-shadow; + // We can't have this transition here because WebKit cancels the carousel + // animation if you trip this while in the middle of another animation. + + // Set gradients for backgrounds + &.left { + #gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001)); + } + &.right { + left: auto; + right: 0; + #gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5)); + } + + // Hover/focus state + &:hover, + &:focus { + outline: 0; + color: @carousel-control-color; + text-decoration: none; + .opacity(.9); + } + + // Toggles + .icon-prev, + .icon-next, + .glyphicon-chevron-left, + .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; + } + .icon-prev, + .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; + } + .icon-next, + .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; + } + .icon-prev, + .icon-next { + width: 20px; + height: 20px; + margin-top: -10px; + line-height: 1; + font-family: serif; + } + + + .icon-prev { + &:before { + content: '\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039) + } + } + .icon-next { + &:before { + content: '\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A) + } + } +} + +// Optional indicator pips +// +// Add an unordered list with the following class and add a list item for each +// slide your carousel holds. + +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + margin-left: -30%; + padding-left: 0; + list-style: none; + text-align: center; + + li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + border: 1px solid @carousel-indicator-border-color; + border-radius: 10px; + cursor: pointer; + + // IE8-9 hack for event handling + // + // Internet Explorer 8-9 does not support clicks on elements without a set + // `background-color`. We cannot use `filter` since that's not viewed as a + // background color by the browser. Thus, a hack is needed. + // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Internet_Explorer + // + // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we + // set alpha transparency for the best results possible. + background-color: #000 \9; // IE8 + background-color: rgba(0,0,0,0); // IE9 + } + .active { + margin: 0; + width: 12px; + height: 12px; + background-color: @carousel-indicator-active-bg; + } +} + +// Optional captions +// ----------------------------- +// Hidden by default for smaller viewports +.carousel-caption { + position: absolute; + left: 15%; + right: 15%; + bottom: 20px; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: @carousel-caption-color; + text-align: center; + text-shadow: @carousel-text-shadow; + & .btn { + text-shadow: none; // No shadow for button elements in carousel-caption + } +} + + +// Scale up controls for tablets and up +@media screen and (min-width: @screen-sm-min) { + + // Scale up the controls a smidge + .carousel-control { + .glyphicon-chevron-left, + .glyphicon-chevron-right, + .icon-prev, + .icon-next { + width: 30px; + height: 30px; + margin-top: -15px; + font-size: 30px; + } + .glyphicon-chevron-left, + .icon-prev { + margin-left: -15px; + } + .glyphicon-chevron-right, + .icon-next { + margin-right: -15px; + } + } + + // Show and left align the captions + .carousel-caption { + left: 20%; + right: 20%; + padding-bottom: 30px; + } + + // Move up the indicators + .carousel-indicators { + bottom: 20px; + } +} diff --git a/src/fmt/doc/bootstrap/close.less b/src/fmt/doc/bootstrap/close.less new file mode 100644 index 000000000..6d5bfe087 --- /dev/null +++ b/src/fmt/doc/bootstrap/close.less @@ -0,0 +1,34 @@ +// +// Close icons +// -------------------------------------------------- + + +.close { + float: right; + font-size: (@font-size-base * 1.5); + font-weight: @close-font-weight; + line-height: 1; + color: @close-color; + text-shadow: @close-text-shadow; + .opacity(.2); + + &:hover, + &:focus { + color: @close-color; + text-decoration: none; + cursor: pointer; + .opacity(.5); + } + + // Additional properties for button version + // iOS requires the button element instead of an anchor tag. + // If you want the anchor version, it requires `href="#"`. + // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile + button& { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; + } +} diff --git a/src/fmt/doc/bootstrap/code.less b/src/fmt/doc/bootstrap/code.less new file mode 100644 index 000000000..a08b4d48c --- /dev/null +++ b/src/fmt/doc/bootstrap/code.less @@ -0,0 +1,69 @@ +// +// Code (inline and block) +// -------------------------------------------------- + + +// Inline and block code styles +code, +kbd, +pre, +samp { + font-family: @font-family-monospace; +} + +// Inline code +code { + padding: 2px 4px; + font-size: 90%; + color: @code-color; + background-color: @code-bg; + border-radius: @border-radius-base; +} + +// User input typically entered via keyboard +kbd { + padding: 2px 4px; + font-size: 90%; + color: @kbd-color; + background-color: @kbd-bg; + border-radius: @border-radius-small; + box-shadow: inset 0 -1px 0 rgba(0,0,0,.25); + + kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + box-shadow: none; + } +} + +// Blocks of code +pre { + display: block; + padding: ((@line-height-computed - 1) / 2); + margin: 0 0 (@line-height-computed / 2); + font-size: (@font-size-base - 1); // 14px to 13px + line-height: @line-height-base; + word-break: break-all; + word-wrap: break-word; + color: @pre-color; + background-color: @pre-bg; + border: 1px solid @pre-border-color; + border-radius: @border-radius-base; + + // Account for some code outputs that place code tags in pre tags + code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; + } +} + +// Enable scrollable blocks of code +.pre-scrollable { + max-height: @pre-scrollable-max-height; + overflow-y: scroll; +} diff --git a/src/fmt/doc/bootstrap/component-animations.less b/src/fmt/doc/bootstrap/component-animations.less new file mode 100644 index 000000000..0bcee910a --- /dev/null +++ b/src/fmt/doc/bootstrap/component-animations.less @@ -0,0 +1,33 @@ +// +// Component animations +// -------------------------------------------------- + +// Heads up! +// +// We don't use the `.opacity()` mixin here since it causes a bug with text +// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552. + +.fade { + opacity: 0; + .transition(opacity .15s linear); + &.in { + opacity: 1; + } +} + +.collapse { + display: none; + + &.in { display: block; } + tr&.in { display: table-row; } + tbody&.in { display: table-row-group; } +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + .transition-property(~"height, visibility"); + .transition-duration(.35s); + .transition-timing-function(ease); +} diff --git a/src/fmt/doc/bootstrap/dropdowns.less b/src/fmt/doc/bootstrap/dropdowns.less new file mode 100644 index 000000000..af344607b --- /dev/null +++ b/src/fmt/doc/bootstrap/dropdowns.less @@ -0,0 +1,214 @@ +// +// Dropdown menus +// -------------------------------------------------- + + +// Dropdown arrow/caret +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: @caret-width-base dashed; + border-right: @caret-width-base solid transparent; + border-left: @caret-width-base solid transparent; +} + +// The dropdown wrapper (div) +.dropup, +.dropdown { + position: relative; +} + +// Prevent the focus on the dropdown toggle when closing dropdowns +.dropdown-toggle:focus { + outline: 0; +} + +// The dropdown menu (ul) +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: @zindex-dropdown; + display: none; // none by default, but block on "open" of the menu + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; // override default ul + list-style: none; + font-size: @font-size-base; + text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer) + background-color: @dropdown-bg; + border: 1px solid @dropdown-fallback-border; // IE8 fallback + border: 1px solid @dropdown-border; + border-radius: @border-radius-base; + .box-shadow(0 6px 12px rgba(0,0,0,.175)); + background-clip: padding-box; + + // Aligns the dropdown menu to right + // + // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]` + &.pull-right { + right: 0; + left: auto; + } + + // Dividers (basically an hr) within the dropdown + .divider { + .nav-divider(@dropdown-divider-bg); + } + + // Links within the dropdown menu + > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: @line-height-base; + color: @dropdown-link-color; + white-space: nowrap; // prevent links from randomly breaking onto new lines + } +} + +// Hover/Focus state +.dropdown-menu > li > a { + &:hover, + &:focus { + text-decoration: none; + color: @dropdown-link-hover-color; + background-color: @dropdown-link-hover-bg; + } +} + +// Active state +.dropdown-menu > .active > a { + &, + &:hover, + &:focus { + color: @dropdown-link-active-color; + text-decoration: none; + outline: 0; + background-color: @dropdown-link-active-bg; + } +} + +// Disabled state +// +// Gray out text and ensure the hover/focus state remains gray + +.dropdown-menu > .disabled > a { + &, + &:hover, + &:focus { + color: @dropdown-link-disabled-color; + } + + // Nuke hover/focus effects + &:hover, + &:focus { + text-decoration: none; + background-color: transparent; + background-image: none; // Remove CSS gradient + .reset-filter(); + cursor: @cursor-disabled; + } +} + +// Open state for the dropdown +.open { + // Show the menu + > .dropdown-menu { + display: block; + } + + // Remove the outline when :focus is triggered + > a { + outline: 0; + } +} + +// Menu positioning +// +// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown +// menu with the parent. +.dropdown-menu-right { + left: auto; // Reset the default from `.dropdown-menu` + right: 0; +} +// With v3, we enabled auto-flipping if you have a dropdown within a right +// aligned nav component. To enable the undoing of that, we provide an override +// to restore the default dropdown menu alignment. +// +// This is only for left-aligning a dropdown menu within a `.navbar-right` or +// `.pull-right` nav component. +.dropdown-menu-left { + left: 0; + right: auto; +} + +// Dropdown section headers +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: @font-size-small; + line-height: @line-height-base; + color: @dropdown-header-color; + white-space: nowrap; // as with > li > a +} + +// Backdrop to catch body clicks on mobile, etc. +.dropdown-backdrop { + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: 0; + z-index: (@zindex-dropdown - 10); +} + +// Right aligned dropdowns +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} + +// Allow for dropdowns to go bottom up (aka, dropup-menu) +// +// Just add .dropup after the standard .dropdown class and you're set, bro. +// TODO: abstract this so that the navbar fixed styles are not placed here? + +.dropup, +.navbar-fixed-bottom .dropdown { + // Reverse the caret + .caret { + border-top: 0; + border-bottom: @caret-width-base solid; + content: ""; + } + // Different positioning for bottom up menu + .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; + } +} + + +// Component alignment +// +// Reiterate per navbar.less and the modified component alignment there. + +@media (min-width: @grid-float-breakpoint) { + .navbar-right { + .dropdown-menu { + .dropdown-menu-right(); + } + // Necessary for overrides of the default right aligned menu. + // Will remove come v4 in all likelihood. + .dropdown-menu-left { + .dropdown-menu-left(); + } + } +} diff --git a/src/fmt/doc/bootstrap/forms.less b/src/fmt/doc/bootstrap/forms.less new file mode 100644 index 000000000..e4b50629e --- /dev/null +++ b/src/fmt/doc/bootstrap/forms.less @@ -0,0 +1,574 @@ +// +// Forms +// -------------------------------------------------- + + +// Normalize non-controls +// +// Restyle and baseline non-control form elements. + +fieldset { + padding: 0; + margin: 0; + border: 0; + // Chrome and Firefox set a `min-width: min-content;` on fieldsets, + // so we reset that to ensure it behaves more like a standard block element. + // See https://github.com/twbs/bootstrap/issues/12359. + min-width: 0; +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: @line-height-computed; + font-size: (@font-size-base * 1.5); + line-height: inherit; + color: @legend-color; + border: 0; + border-bottom: 1px solid @legend-border-color; +} + +label { + display: inline-block; + max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141) + margin-bottom: 5px; + font-weight: bold; +} + + +// Normalize form controls +// +// While most of our form styles require extra classes, some basic normalization +// is required to ensure optimum display with or without those classes to better +// address browser inconsistencies. + +// Override content-box in Normalize (* isn't specific enough) +input[type="search"] { + .box-sizing(border-box); +} + +// Position radios and checkboxes better +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; // IE8-9 + line-height: normal; +} + +// Set the height of file controls to match text inputs +input[type="file"] { + display: block; +} + +// Make range inputs behave like textual form controls +input[type="range"] { + display: block; + width: 100%; +} + +// Make multiple select elements height not fixed +select[multiple], +select[size] { + height: auto; +} + +// Focus for file, radio, and checkbox +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + .tab-focus(); +} + +// Adjust output element +output { + display: block; + padding-top: (@padding-base-vertical + 1); + font-size: @font-size-base; + line-height: @line-height-base; + color: @input-color; +} + + +// Common form controls +// +// Shared size and type resets for form controls. Apply `.form-control` to any +// of the following form controls: +// +// select +// textarea +// input[type="text"] +// input[type="password"] +// input[type="datetime"] +// input[type="datetime-local"] +// input[type="date"] +// input[type="month"] +// input[type="time"] +// input[type="week"] +// input[type="number"] +// input[type="email"] +// input[type="url"] +// input[type="search"] +// input[type="tel"] +// input[type="color"] + +.form-control { + display: block; + width: 100%; + height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border) + padding: @padding-base-vertical @padding-base-horizontal; + font-size: @font-size-base; + line-height: @line-height-base; + color: @input-color; + background-color: @input-bg; + background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 + border: 1px solid @input-border; + border-radius: @input-border-radius; // Note: This has no effect on <select>s in some browsers, due to the limited stylability of <select>s in CSS. + .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); + .transition(~"border-color ease-in-out .15s, box-shadow ease-in-out .15s"); + + // Customize the `:focus` state to imitate native WebKit styles. + .form-control-focus(); + + // Placeholder + .placeholder(); + + // Disabled and read-only inputs + // + // HTML5 says that controls under a fieldset > legend:first-child won't be + // disabled if the fieldset is disabled. Due to implementation difficulty, we + // don't honor that edge case; we style them as disabled anyway. + &[disabled], + &[readonly], + fieldset[disabled] & { + background-color: @input-bg-disabled; + opacity: 1; // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655 + } + + &[disabled], + fieldset[disabled] & { + cursor: @cursor-disabled; + } + + // Reset height for `textarea`s + textarea& { + height: auto; + } +} + + +// Search inputs in iOS +// +// This overrides the extra rounded corners on search inputs in iOS so that our +// `.form-control` class can properly style them. Note that this cannot simply +// be added to `.form-control` as it's not specific enough. For details, see +// https://github.com/twbs/bootstrap/issues/11586. + +input[type="search"] { + -webkit-appearance: none; +} + + +// Special styles for iOS temporal inputs +// +// In Mobile Safari, setting `display: block` on temporal inputs causes the +// text within the input to become vertically misaligned. As a workaround, we +// set a pixel line-height that matches the given height of the input, but only +// for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848 + +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"], + input[type="time"], + input[type="datetime-local"], + input[type="month"] { + line-height: @input-height-base; + + &.input-sm, + .input-group-sm & { + line-height: @input-height-small; + } + + &.input-lg, + .input-group-lg & { + line-height: @input-height-large; + } + } +} + + +// Form groups +// +// Designed to help with the organization and spacing of vertical forms. For +// horizontal forms, use the predefined grid classes. + +.form-group { + margin-bottom: @form-group-margin-bottom; +} + + +// Checkboxes and radios +// +// Indent the labels to position radios/checkboxes as hanging controls. + +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; + + label { + min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; + } +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-left: -20px; + margin-top: 4px \9; +} + +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing +} + +// Radios and checkboxes on same line +.radio-inline, +.checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + vertical-align: middle; + font-weight: normal; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; // space out consecutive inline controls +} + +// Apply same disabled cursor tweak as for inputs +// Some special care is needed because <label>s don't inherit their parent's `cursor`. +// +// Note: Neither radios nor checkboxes can be readonly. +input[type="radio"], +input[type="checkbox"] { + &[disabled], + &.disabled, + fieldset[disabled] & { + cursor: @cursor-disabled; + } +} +// These classes are used directly on <label>s +.radio-inline, +.checkbox-inline { + &.disabled, + fieldset[disabled] & { + cursor: @cursor-disabled; + } +} +// These classes are used on elements with <label> descendants +.radio, +.checkbox { + &.disabled, + fieldset[disabled] & { + label { + cursor: @cursor-disabled; + } + } +} + + +// Static form control text +// +// Apply class to a `p` element to make any string of text align with labels in +// a horizontal form layout. + +.form-control-static { + // Size it appropriately next to real form controls + padding-top: (@padding-base-vertical + 1); + padding-bottom: (@padding-base-vertical + 1); + // Remove default margin from `p` + margin-bottom: 0; + min-height: (@line-height-computed + @font-size-base); + + &.input-lg, + &.input-sm { + padding-left: 0; + padding-right: 0; + } +} + + +// Form control sizing +// +// Build on `.form-control` with modifier classes to decrease or increase the +// height and font-size of form controls. +// +// The `.form-group-* form-control` variations are sadly duplicated to avoid the +// issue documented in https://github.com/twbs/bootstrap/issues/15074. + +.input-sm { + .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @input-border-radius-small); +} +.form-group-sm { + .form-control { + .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @input-border-radius-small); + } + .form-control-static { + height: @input-height-small; + padding: @padding-small-vertical @padding-small-horizontal; + font-size: @font-size-small; + line-height: @line-height-small; + min-height: (@line-height-computed + @font-size-small); + } +} + +.input-lg { + .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @input-border-radius-large); +} +.form-group-lg { + .form-control { + .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @input-border-radius-large); + } + .form-control-static { + height: @input-height-large; + padding: @padding-large-vertical @padding-large-horizontal; + font-size: @font-size-large; + line-height: @line-height-large; + min-height: (@line-height-computed + @font-size-large); + } +} + + +// Form control feedback states +// +// Apply contextual and semantic states to individual form controls. + +.has-feedback { + // Enable absolute positioning + position: relative; + + // Ensure icons don't overlap text + .form-control { + padding-right: (@input-height-base * 1.25); + } +} +// Feedback icon (requires .glyphicon classes) +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; // Ensure icon is above input groups + display: block; + width: @input-height-base; + height: @input-height-base; + line-height: @input-height-base; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback { + width: @input-height-large; + height: @input-height-large; + line-height: @input-height-large; +} +.input-sm + .form-control-feedback { + width: @input-height-small; + height: @input-height-small; + line-height: @input-height-small; +} + +// Feedback states +.has-success { + .form-control-validation(@state-success-text; @state-success-text; @state-success-bg); +} +.has-warning { + .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg); +} +.has-error { + .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg); +} + +// Reposition feedback icon if input has visible label above +.has-feedback label { + + & ~ .form-control-feedback { + top: (@line-height-computed + 5); // Height of the `label` and its margin + } + &.sr-only ~ .form-control-feedback { + top: 0; + } +} + + +// Help text +// +// Apply to any element you wish to create light text for placement immediately +// below a form control. Use for general help, formatting, or instructional text. + +.help-block { + display: block; // account for any element using help-block + margin-top: 5px; + margin-bottom: 10px; + color: lighten(@text-color, 25%); // lighten the text some for contrast +} + + +// Inline forms +// +// Make forms appear inline(-block) by adding the `.form-inline` class. Inline +// forms begin stacked on extra small (mobile) devices and then go inline when +// viewports reach <768px. +// +// Requires wrapping inputs and labels with `.form-group` for proper display of +// default HTML form controls and our custom form controls (e.g., input groups). +// +// Heads up! This is mixin-ed into `.navbar-form` in navbars.less. + +.form-inline { + + // Kick in the inline + @media (min-width: @screen-sm-min) { + // Inline-block all the things for "inline" + .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + + // In navbar-form, allow folks to *not* use `.form-group` + .form-control { + display: inline-block; + width: auto; // Prevent labels from stacking above inputs in `.form-group` + vertical-align: middle; + } + + // Make static controls behave like regular ones + .form-control-static { + display: inline-block; + } + + .input-group { + display: inline-table; + vertical-align: middle; + + .input-group-addon, + .input-group-btn, + .form-control { + width: auto; + } + } + + // Input groups need that 100% width though + .input-group > .form-control { + width: 100%; + } + + .control-label { + margin-bottom: 0; + vertical-align: middle; + } + + // Remove default margin on radios/checkboxes that were used for stacking, and + // then undo the floating of radios and checkboxes to match. + .radio, + .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + + label { + padding-left: 0; + } + } + .radio input[type="radio"], + .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + + // Re-override the feedback icon. + .has-feedback .form-control-feedback { + top: 0; + } + } +} + + +// Horizontal forms +// +// Horizontal forms are built on grid classes and allow you to create forms with +// labels on the left and inputs on the right. + +.form-horizontal { + + // Consistent vertical alignment of radios and checkboxes + // + // Labels also get some reset styles, but that is scoped to a media query below. + .radio, + .checkbox, + .radio-inline, + .checkbox-inline { + margin-top: 0; + margin-bottom: 0; + padding-top: (@padding-base-vertical + 1); // Default padding plus a border + } + // Account for padding we're adding to ensure the alignment and of help text + // and other content below items + .radio, + .checkbox { + min-height: (@line-height-computed + (@padding-base-vertical + 1)); + } + + // Make form groups behave like rows + .form-group { + .make-row(); + } + + // Reset spacing and right align labels, but scope to media queries so that + // labels on narrow viewports stack the same as a default form example. + @media (min-width: @screen-sm-min) { + .control-label { + text-align: right; + margin-bottom: 0; + padding-top: (@padding-base-vertical + 1); // Default padding plus a border + } + } + + // Validation states + // + // Reposition the icon because it's now within a grid column and columns have + // `position: relative;` on them. Also accounts for the grid gutter padding. + .has-feedback .form-control-feedback { + right: (@grid-gutter-width / 2); + } + + // Form group sizes + // + // Quick utility class for applying `.input-lg` and `.input-sm` styles to the + // inputs and labels within a `.form-group`. + .form-group-lg { + @media (min-width: @screen-sm-min) { + .control-label { + padding-top: ((@padding-large-vertical * @line-height-large) + 1); + } + } + } + .form-group-sm { + @media (min-width: @screen-sm-min) { + .control-label { + padding-top: (@padding-small-vertical + 1); + } + } + } +} diff --git a/src/fmt/doc/bootstrap/glyphicons.less b/src/fmt/doc/bootstrap/glyphicons.less new file mode 100644 index 000000000..335d80aa6 --- /dev/null +++ b/src/fmt/doc/bootstrap/glyphicons.less @@ -0,0 +1,305 @@ +// +// Glyphicons for Bootstrap +// +// Since icons are fonts, they can be placed anywhere text is placed and are +// thus automatically sized to match the surrounding child. To use, create an +// inline element with the appropriate classes, like so: +// +// <a href="#"><span class="glyphicon glyphicon-star"></span> Star</a> + +// Import the fonts +@font-face { + font-family: 'Glyphicons Halflings'; + src: url('@{icon-font-path}@{icon-font-name}.eot'); + src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'), + url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'), + url('@{icon-font-path}@{icon-font-name}.woff') format('woff'), + url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'), + url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg'); +} + +// Catchall baseclass +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +// Individual icons +.glyphicon-asterisk { &:before { content: "\2a"; } } +.glyphicon-plus { &:before { content: "\2b"; } } +.glyphicon-euro, +.glyphicon-eur { &:before { content: "\20ac"; } } +.glyphicon-minus { &:before { content: "\2212"; } } +.glyphicon-cloud { &:before { content: "\2601"; } } +.glyphicon-envelope { &:before { content: "\2709"; } } +.glyphicon-pencil { &:before { content: "\270f"; } } +.glyphicon-glass { &:before { content: "\e001"; } } +.glyphicon-music { &:before { content: "\e002"; } } +.glyphicon-search { &:before { content: "\e003"; } } +.glyphicon-heart { &:before { content: "\e005"; } } +.glyphicon-star { &:before { content: "\e006"; } } +.glyphicon-star-empty { &:before { content: "\e007"; } } +.glyphicon-user { &:before { content: "\e008"; } } +.glyphicon-film { &:before { content: "\e009"; } } +.glyphicon-th-large { &:before { content: "\e010"; } } +.glyphicon-th { &:before { content: "\e011"; } } +.glyphicon-th-list { &:before { content: "\e012"; } } +.glyphicon-ok { &:before { content: "\e013"; } } +.glyphicon-remove { &:before { content: "\e014"; } } +.glyphicon-zoom-in { &:before { content: "\e015"; } } +.glyphicon-zoom-out { &:before { content: "\e016"; } } +.glyphicon-off { &:before { content: "\e017"; } } +.glyphicon-signal { &:before { content: "\e018"; } } +.glyphicon-cog { &:before { content: "\e019"; } } +.glyphicon-trash { &:before { content: "\e020"; } } +.glyphicon-home { &:before { content: "\e021"; } } +.glyphicon-file { &:before { content: "\e022"; } } +.glyphicon-time { &:before { content: "\e023"; } } +.glyphicon-road { &:before { content: "\e024"; } } +.glyphicon-download-alt { &:before { content: "\e025"; } } +.glyphicon-download { &:before { content: "\e026"; } } +.glyphicon-upload { &:before { content: "\e027"; } } +.glyphicon-inbox { &:before { content: "\e028"; } } +.glyphicon-play-circle { &:before { content: "\e029"; } } +.glyphicon-repeat { &:before { content: "\e030"; } } +.glyphicon-refresh { &:before { content: "\e031"; } } +.glyphicon-list-alt { &:before { content: "\e032"; } } +.glyphicon-lock { &:before { content: "\e033"; } } +.glyphicon-flag { &:before { content: "\e034"; } } +.glyphicon-headphones { &:before { content: "\e035"; } } +.glyphicon-volume-off { &:before { content: "\e036"; } } +.glyphicon-volume-down { &:before { content: "\e037"; } } +.glyphicon-volume-up { &:before { content: "\e038"; } } +.glyphicon-qrcode { &:before { content: "\e039"; } } +.glyphicon-barcode { &:before { content: "\e040"; } } +.glyphicon-tag { &:before { content: "\e041"; } } +.glyphicon-tags { &:before { content: "\e042"; } } +.glyphicon-book { &:before { content: "\e043"; } } +.glyphicon-bookmark { &:before { content: "\e044"; } } +.glyphicon-print { &:before { content: "\e045"; } } +.glyphicon-camera { &:before { content: "\e046"; } } +.glyphicon-font { &:before { content: "\e047"; } } +.glyphicon-bold { &:before { content: "\e048"; } } +.glyphicon-italic { &:before { content: "\e049"; } } +.glyphicon-text-height { &:before { content: "\e050"; } } +.glyphicon-text-width { &:before { content: "\e051"; } } +.glyphicon-align-left { &:before { content: "\e052"; } } +.glyphicon-align-center { &:before { content: "\e053"; } } +.glyphicon-align-right { &:before { content: "\e054"; } } +.glyphicon-align-justify { &:before { content: "\e055"; } } +.glyphicon-list { &:before { content: "\e056"; } } +.glyphicon-indent-left { &:before { content: "\e057"; } } +.glyphicon-indent-right { &:before { content: "\e058"; } } +.glyphicon-facetime-video { &:before { content: "\e059"; } } +.glyphicon-picture { &:before { content: "\e060"; } } +.glyphicon-map-marker { &:before { content: "\e062"; } } +.glyphicon-adjust { &:before { content: "\e063"; } } +.glyphicon-tint { &:before { content: "\e064"; } } +.glyphicon-edit { &:before { content: "\e065"; } } +.glyphicon-share { &:before { content: "\e066"; } } +.glyphicon-check { &:before { content: "\e067"; } } +.glyphicon-move { &:before { content: "\e068"; } } +.glyphicon-step-backward { &:before { content: "\e069"; } } +.glyphicon-fast-backward { &:before { content: "\e070"; } } +.glyphicon-backward { &:before { content: "\e071"; } } +.glyphicon-play { &:before { content: "\e072"; } } +.glyphicon-pause { &:before { content: "\e073"; } } +.glyphicon-stop { &:before { content: "\e074"; } } +.glyphicon-forward { &:before { content: "\e075"; } } +.glyphicon-fast-forward { &:before { content: "\e076"; } } +.glyphicon-step-forward { &:before { content: "\e077"; } } +.glyphicon-eject { &:before { content: "\e078"; } } +.glyphicon-chevron-left { &:before { content: "\e079"; } } +.glyphicon-chevron-right { &:before { content: "\e080"; } } +.glyphicon-plus-sign { &:before { content: "\e081"; } } +.glyphicon-minus-sign { &:before { content: "\e082"; } } +.glyphicon-remove-sign { &:before { content: "\e083"; } } +.glyphicon-ok-sign { &:before { content: "\e084"; } } +.glyphicon-question-sign { &:before { content: "\e085"; } } +.glyphicon-info-sign { &:before { content: "\e086"; } } +.glyphicon-screenshot { &:before { content: "\e087"; } } +.glyphicon-remove-circle { &:before { content: "\e088"; } } +.glyphicon-ok-circle { &:before { content: "\e089"; } } +.glyphicon-ban-circle { &:before { content: "\e090"; } } +.glyphicon-arrow-left { &:before { content: "\e091"; } } +.glyphicon-arrow-right { &:before { content: "\e092"; } } +.glyphicon-arrow-up { &:before { content: "\e093"; } } +.glyphicon-arrow-down { &:before { content: "\e094"; } } +.glyphicon-share-alt { &:before { content: "\e095"; } } +.glyphicon-resize-full { &:before { content: "\e096"; } } +.glyphicon-resize-small { &:before { content: "\e097"; } } +.glyphicon-exclamation-sign { &:before { content: "\e101"; } } +.glyphicon-gift { &:before { content: "\e102"; } } +.glyphicon-leaf { &:before { content: "\e103"; } } +.glyphicon-fire { &:before { content: "\e104"; } } +.glyphicon-eye-open { &:before { content: "\e105"; } } +.glyphicon-eye-close { &:before { content: "\e106"; } } +.glyphicon-warning-sign { &:before { content: "\e107"; } } +.glyphicon-plane { &:before { content: "\e108"; } } +.glyphicon-calendar { &:before { content: "\e109"; } } +.glyphicon-random { &:before { content: "\e110"; } } +.glyphicon-comment { &:before { content: "\e111"; } } +.glyphicon-magnet { &:before { content: "\e112"; } } +.glyphicon-chevron-up { &:before { content: "\e113"; } } +.glyphicon-chevron-down { &:before { content: "\e114"; } } +.glyphicon-retweet { &:before { content: "\e115"; } } +.glyphicon-shopping-cart { &:before { content: "\e116"; } } +.glyphicon-folder-close { &:before { content: "\e117"; } } +.glyphicon-folder-open { &:before { content: "\e118"; } } +.glyphicon-resize-vertical { &:before { content: "\e119"; } } +.glyphicon-resize-horizontal { &:before { content: "\e120"; } } +.glyphicon-hdd { &:before { content: "\e121"; } } +.glyphicon-bullhorn { &:before { content: "\e122"; } } +.glyphicon-bell { &:before { content: "\e123"; } } +.glyphicon-certificate { &:before { content: "\e124"; } } +.glyphicon-thumbs-up { &:before { content: "\e125"; } } +.glyphicon-thumbs-down { &:before { content: "\e126"; } } +.glyphicon-hand-right { &:before { content: "\e127"; } } +.glyphicon-hand-left { &:before { content: "\e128"; } } +.glyphicon-hand-up { &:before { content: "\e129"; } } +.glyphicon-hand-down { &:before { content: "\e130"; } } +.glyphicon-circle-arrow-right { &:before { content: "\e131"; } } +.glyphicon-circle-arrow-left { &:before { content: "\e132"; } } +.glyphicon-circle-arrow-up { &:before { content: "\e133"; } } +.glyphicon-circle-arrow-down { &:before { content: "\e134"; } } +.glyphicon-globe { &:before { content: "\e135"; } } +.glyphicon-wrench { &:before { content: "\e136"; } } +.glyphicon-tasks { &:before { content: "\e137"; } } +.glyphicon-filter { &:before { content: "\e138"; } } +.glyphicon-briefcase { &:before { content: "\e139"; } } +.glyphicon-fullscreen { &:before { content: "\e140"; } } +.glyphicon-dashboard { &:before { content: "\e141"; } } +.glyphicon-paperclip { &:before { content: "\e142"; } } +.glyphicon-heart-empty { &:before { content: "\e143"; } } +.glyphicon-link { &:before { content: "\e144"; } } +.glyphicon-phone { &:before { content: "\e145"; } } +.glyphicon-pushpin { &:before { content: "\e146"; } } +.glyphicon-usd { &:before { content: "\e148"; } } +.glyphicon-gbp { &:before { content: "\e149"; } } +.glyphicon-sort { &:before { content: "\e150"; } } +.glyphicon-sort-by-alphabet { &:before { content: "\e151"; } } +.glyphicon-sort-by-alphabet-alt { &:before { content: "\e152"; } } +.glyphicon-sort-by-order { &:before { content: "\e153"; } } +.glyphicon-sort-by-order-alt { &:before { content: "\e154"; } } +.glyphicon-sort-by-attributes { &:before { content: "\e155"; } } +.glyphicon-sort-by-attributes-alt { &:before { content: "\e156"; } } +.glyphicon-unchecked { &:before { content: "\e157"; } } +.glyphicon-expand { &:before { content: "\e158"; } } +.glyphicon-collapse-down { &:before { content: "\e159"; } } +.glyphicon-collapse-up { &:before { content: "\e160"; } } +.glyphicon-log-in { &:before { content: "\e161"; } } +.glyphicon-flash { &:before { content: "\e162"; } } +.glyphicon-log-out { &:before { content: "\e163"; } } +.glyphicon-new-window { &:before { content: "\e164"; } } +.glyphicon-record { &:before { content: "\e165"; } } +.glyphicon-save { &:before { content: "\e166"; } } +.glyphicon-open { &:before { content: "\e167"; } } +.glyphicon-saved { &:before { content: "\e168"; } } +.glyphicon-import { &:before { content: "\e169"; } } +.glyphicon-export { &:before { content: "\e170"; } } +.glyphicon-send { &:before { content: "\e171"; } } +.glyphicon-floppy-disk { &:before { content: "\e172"; } } +.glyphicon-floppy-saved { &:before { content: "\e173"; } } +.glyphicon-floppy-remove { &:before { content: "\e174"; } } +.glyphicon-floppy-save { &:before { content: "\e175"; } } +.glyphicon-floppy-open { &:before { content: "\e176"; } } +.glyphicon-credit-card { &:before { content: "\e177"; } } +.glyphicon-transfer { &:before { content: "\e178"; } } +.glyphicon-cutlery { &:before { content: "\e179"; } } +.glyphicon-header { &:before { content: "\e180"; } } +.glyphicon-compressed { &:before { content: "\e181"; } } +.glyphicon-earphone { &:before { content: "\e182"; } } +.glyphicon-phone-alt { &:before { content: "\e183"; } } +.glyphicon-tower { &:before { content: "\e184"; } } +.glyphicon-stats { &:before { content: "\e185"; } } +.glyphicon-sd-video { &:before { content: "\e186"; } } +.glyphicon-hd-video { &:before { content: "\e187"; } } +.glyphicon-subtitles { &:before { content: "\e188"; } } +.glyphicon-sound-stereo { &:before { content: "\e189"; } } +.glyphicon-sound-dolby { &:before { content: "\e190"; } } +.glyphicon-sound-5-1 { &:before { content: "\e191"; } } +.glyphicon-sound-6-1 { &:before { content: "\e192"; } } +.glyphicon-sound-7-1 { &:before { content: "\e193"; } } +.glyphicon-copyright-mark { &:before { content: "\e194"; } } +.glyphicon-registration-mark { &:before { content: "\e195"; } } +.glyphicon-cloud-download { &:before { content: "\e197"; } } +.glyphicon-cloud-upload { &:before { content: "\e198"; } } +.glyphicon-tree-conifer { &:before { content: "\e199"; } } +.glyphicon-tree-deciduous { &:before { content: "\e200"; } } +.glyphicon-cd { &:before { content: "\e201"; } } +.glyphicon-save-file { &:before { content: "\e202"; } } +.glyphicon-open-file { &:before { content: "\e203"; } } +.glyphicon-level-up { &:before { content: "\e204"; } } +.glyphicon-copy { &:before { content: "\e205"; } } +.glyphicon-paste { &:before { content: "\e206"; } } +// The following 2 Glyphicons are omitted for the time being because +// they currently use Unicode codepoints that are outside the +// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle +// non-BMP codepoints in CSS string escapes, and thus can't display these two icons. +// Notably, the bug affects some older versions of the Android Browser. +// More info: https://github.com/twbs/bootstrap/issues/10106 +// .glyphicon-door { &:before { content: "\1f6aa"; } } +// .glyphicon-key { &:before { content: "\1f511"; } } +.glyphicon-alert { &:before { content: "\e209"; } } +.glyphicon-equalizer { &:before { content: "\e210"; } } +.glyphicon-king { &:before { content: "\e211"; } } +.glyphicon-queen { &:before { content: "\e212"; } } +.glyphicon-pawn { &:before { content: "\e213"; } } +.glyphicon-bishop { &:before { content: "\e214"; } } +.glyphicon-knight { &:before { content: "\e215"; } } +.glyphicon-baby-formula { &:before { content: "\e216"; } } +.glyphicon-tent { &:before { content: "\26fa"; } } +.glyphicon-blackboard { &:before { content: "\e218"; } } +.glyphicon-bed { &:before { content: "\e219"; } } +.glyphicon-apple { &:before { content: "\f8ff"; } } +.glyphicon-erase { &:before { content: "\e221"; } } +.glyphicon-hourglass { &:before { content: "\231b"; } } +.glyphicon-lamp { &:before { content: "\e223"; } } +.glyphicon-duplicate { &:before { content: "\e224"; } } +.glyphicon-piggy-bank { &:before { content: "\e225"; } } +.glyphicon-scissors { &:before { content: "\e226"; } } +.glyphicon-bitcoin { &:before { content: "\e227"; } } +.glyphicon-btc { &:before { content: "\e227"; } } +.glyphicon-xbt { &:before { content: "\e227"; } } +.glyphicon-yen { &:before { content: "\00a5"; } } +.glyphicon-jpy { &:before { content: "\00a5"; } } +.glyphicon-ruble { &:before { content: "\20bd"; } } +.glyphicon-rub { &:before { content: "\20bd"; } } +.glyphicon-scale { &:before { content: "\e230"; } } +.glyphicon-ice-lolly { &:before { content: "\e231"; } } +.glyphicon-ice-lolly-tasted { &:before { content: "\e232"; } } +.glyphicon-education { &:before { content: "\e233"; } } +.glyphicon-option-horizontal { &:before { content: "\e234"; } } +.glyphicon-option-vertical { &:before { content: "\e235"; } } +.glyphicon-menu-hamburger { &:before { content: "\e236"; } } +.glyphicon-modal-window { &:before { content: "\e237"; } } +.glyphicon-oil { &:before { content: "\e238"; } } +.glyphicon-grain { &:before { content: "\e239"; } } +.glyphicon-sunglasses { &:before { content: "\e240"; } } +.glyphicon-text-size { &:before { content: "\e241"; } } +.glyphicon-text-color { &:before { content: "\e242"; } } +.glyphicon-text-background { &:before { content: "\e243"; } } +.glyphicon-object-align-top { &:before { content: "\e244"; } } +.glyphicon-object-align-bottom { &:before { content: "\e245"; } } +.glyphicon-object-align-horizontal{ &:before { content: "\e246"; } } +.glyphicon-object-align-left { &:before { content: "\e247"; } } +.glyphicon-object-align-vertical { &:before { content: "\e248"; } } +.glyphicon-object-align-right { &:before { content: "\e249"; } } +.glyphicon-triangle-right { &:before { content: "\e250"; } } +.glyphicon-triangle-left { &:before { content: "\e251"; } } +.glyphicon-triangle-bottom { &:before { content: "\e252"; } } +.glyphicon-triangle-top { &:before { content: "\e253"; } } +.glyphicon-console { &:before { content: "\e254"; } } +.glyphicon-superscript { &:before { content: "\e255"; } } +.glyphicon-subscript { &:before { content: "\e256"; } } +.glyphicon-menu-left { &:before { content: "\e257"; } } +.glyphicon-menu-right { &:before { content: "\e258"; } } +.glyphicon-menu-down { &:before { content: "\e259"; } } +.glyphicon-menu-up { &:before { content: "\e260"; } } diff --git a/src/fmt/doc/bootstrap/grid.less b/src/fmt/doc/bootstrap/grid.less new file mode 100644 index 000000000..2e61c9426 --- /dev/null +++ b/src/fmt/doc/bootstrap/grid.less @@ -0,0 +1,84 @@ +// +// Grid system +// -------------------------------------------------- + + +// Container widths +// +// Set the container width, and override it for fixed navbars in media queries. + +.tb-container { + .container-fixed(); + + @media (min-width: @screen-sm-min) { + width: @container-sm; + } + @media (min-width: @screen-md-min) { + width: @container-md; + } + @media (min-width: @screen-lg-min) { + width: @container-lg; + } +} + + +// Fluid container +// +// Utilizes the mixin meant for fixed width containers, but without any defined +// width for fluid, full width layouts. + +.container-fluid { + .container-fixed(); +} + + +// Row +// +// Rows contain and clear the floats of your columns. + +.row { + .make-row(); +} + + +// Columns +// +// Common styles for small and large grid columns + +.make-grid-columns(); + + +// Extra small grid +// +// Columns, offsets, pushes, and pulls for extra small devices like +// smartphones. + +.make-grid(xs); + + +// Small grid +// +// Columns, offsets, pushes, and pulls for the small device range, from phones +// to tablets. + +@media (min-width: @screen-sm-min) { + .make-grid(sm); +} + + +// Medium grid +// +// Columns, offsets, pushes, and pulls for the desktop device range. + +@media (min-width: @screen-md-min) { + .make-grid(md); +} + + +// Large grid +// +// Columns, offsets, pushes, and pulls for the large desktop device range. + +@media (min-width: @screen-lg-min) { + .make-grid(lg); +} diff --git a/src/fmt/doc/bootstrap/input-groups.less b/src/fmt/doc/bootstrap/input-groups.less new file mode 100644 index 000000000..a8712f25b --- /dev/null +++ b/src/fmt/doc/bootstrap/input-groups.less @@ -0,0 +1,166 @@ +// +// Input groups +// -------------------------------------------------- + +// Base styles +// ------------------------- +.input-group { + position: relative; // For dropdowns + display: table; + border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table + + // Undo padding and float of grid classes + &[class*="col-"] { + float: none; + padding-left: 0; + padding-right: 0; + } + + .form-control { + // Ensure that the input is always above the *appended* addon button for + // proper border colors. + position: relative; + z-index: 2; + + // IE9 fubars the placeholder attribute in text inputs and the arrows on + // select elements in input groups. To fix it, we float the input. Details: + // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855 + float: left; + + width: 100%; + margin-bottom: 0; + } +} + +// Sizing options +// +// Remix the default form control sizing classes into new ones for easier +// manipulation. + +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + .input-lg(); +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + .input-sm(); +} + + +// Display as table-cell +// ------------------------- +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; + + &:not(:first-child):not(:last-child) { + border-radius: 0; + } +} +// Addon and addon wrapper for buttons +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; // Match the inputs +} + +// Text input groups +// ------------------------- +.input-group-addon { + padding: @padding-base-vertical @padding-base-horizontal; + font-size: @font-size-base; + font-weight: normal; + line-height: 1; + color: @input-color; + text-align: center; + background-color: @input-group-addon-bg; + border: 1px solid @input-group-addon-border-color; + border-radius: @border-radius-base; + + // Sizing + &.input-sm { + padding: @padding-small-vertical @padding-small-horizontal; + font-size: @font-size-small; + border-radius: @border-radius-small; + } + &.input-lg { + padding: @padding-large-vertical @padding-large-horizontal; + font-size: @font-size-large; + border-radius: @border-radius-large; + } + + // Nuke default margins from checkboxes and radios to vertically center within. + input[type="radio"], + input[type="checkbox"] { + margin-top: 0; + } +} + +// Reset rounded corners +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + .border-right-radius(0); +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + .border-left-radius(0); +} +.input-group-addon:last-child { + border-left: 0; +} + +// Button input groups +// ------------------------- +.input-group-btn { + position: relative; + // Jankily prevent input button groups from wrapping with `white-space` and + // `font-size` in combination with `inline-block` on buttons. + font-size: 0; + white-space: nowrap; + + // Negative margin for spacing, position for bringing hovered/focused/actived + // element above the siblings. + > .btn { + position: relative; + + .btn { + margin-left: -1px; + } + // Bring the "active" button to the front + &:hover, + &:focus, + &:active { + z-index: 2; + } + } + + // Negative margin to only have a 1px border between the two + &:first-child { + > .btn, + > .btn-group { + margin-right: -1px; + } + } + &:last-child { + > .btn, + > .btn-group { + margin-left: -1px; + } + } +} diff --git a/src/fmt/doc/bootstrap/jumbotron.less b/src/fmt/doc/bootstrap/jumbotron.less new file mode 100644 index 000000000..909580f98 --- /dev/null +++ b/src/fmt/doc/bootstrap/jumbotron.less @@ -0,0 +1,50 @@ +// +// Jumbotron +// -------------------------------------------------- + + +.jumbotron { + padding: @jumbotron-padding (@jumbotron-padding / 2); + margin-bottom: @jumbotron-padding; + color: @jumbotron-color; + background-color: @jumbotron-bg; + + h1, + .h1 { + color: @jumbotron-heading-color; + } + + p { + margin-bottom: (@jumbotron-padding / 2); + font-size: @jumbotron-font-size; + font-weight: 200; + } + + > hr { + border-top-color: darken(@jumbotron-bg, 10%); + } + + .tb-container &, + .container-fluid & { + border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container + } + + .tb-container { + max-width: 100%; + } + + @media screen and (min-width: @screen-sm-min) { + padding: (@jumbotron-padding * 1.6) 0; + + .tb-container &, + .container-fluid & { + padding-left: (@jumbotron-padding * 2); + padding-right: (@jumbotron-padding * 2); + } + + h1, + .h1 { + font-size: (@font-size-base * 4.5); + } + } +} diff --git a/src/fmt/doc/bootstrap/labels.less b/src/fmt/doc/bootstrap/labels.less new file mode 100644 index 000000000..9a5a27006 --- /dev/null +++ b/src/fmt/doc/bootstrap/labels.less @@ -0,0 +1,64 @@ +// +// Labels +// -------------------------------------------------- + +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: @label-color; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; + + // Add hover effects, but only for links + a& { + &:hover, + &:focus { + color: @label-link-hover-color; + text-decoration: none; + cursor: pointer; + } + } + + // Empty labels collapse automatically (not available in IE8) + &:empty { + display: none; + } + + // Quick fix for labels in buttons + .btn & { + position: relative; + top: -1px; + } +} + +// Colors +// Contextual variations (linked labels get darker on :hover) + +.label-default { + .label-variant(@label-default-bg); +} + +.label-primary { + .label-variant(@label-primary-bg); +} + +.label-success { + .label-variant(@label-success-bg); +} + +.label-info { + .label-variant(@label-info-bg); +} + +.label-warning { + .label-variant(@label-warning-bg); +} + +.label-danger { + .label-variant(@label-danger-bg); +} diff --git a/src/fmt/doc/bootstrap/list-group.less b/src/fmt/doc/bootstrap/list-group.less new file mode 100644 index 000000000..1462ce16b --- /dev/null +++ b/src/fmt/doc/bootstrap/list-group.less @@ -0,0 +1,124 @@ +// +// List groups +// -------------------------------------------------- + + +// Base class +// +// Easily usable on <ul>, <ol>, or <div>. + +.list-group { + // No need to set list-style: none; since .list-group-item is block level + margin-bottom: 20px; + padding-left: 0; // reset padding because ul and ol +} + + +// Individual list items +// +// Use on `li`s or `div`s within the `.list-group` parent. + +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + // Place the border on the list items and negative margin up for better styling + margin-bottom: -1px; + background-color: @list-group-bg; + border: 1px solid @list-group-border; + + // Round the first and last items + &:first-child { + .border-top-radius(@list-group-border-radius); + } + &:last-child { + margin-bottom: 0; + .border-bottom-radius(@list-group-border-radius); + } +} + + +// Linked list items +// +// Use anchor elements instead of `li`s or `div`s to create linked list items. +// Includes an extra `.active` modifier class for showing selected items. + +a.list-group-item { + color: @list-group-link-color; + + .list-group-item-heading { + color: @list-group-link-heading-color; + } + + // Hover state + &:hover, + &:focus { + text-decoration: none; + color: @list-group-link-hover-color; + background-color: @list-group-hover-bg; + } +} + +.list-group-item { + // Disabled state + &.disabled, + &.disabled:hover, + &.disabled:focus { + background-color: @list-group-disabled-bg; + color: @list-group-disabled-color; + cursor: @cursor-disabled; + + // Force color to inherit for custom content + .list-group-item-heading { + color: inherit; + } + .list-group-item-text { + color: @list-group-disabled-text-color; + } + } + + // Active class on item itself, not parent + &.active, + &.active:hover, + &.active:focus { + z-index: 2; // Place active items above their siblings for proper border styling + color: @list-group-active-color; + background-color: @list-group-active-bg; + border-color: @list-group-active-border; + + // Force color to inherit for custom content + .list-group-item-heading, + .list-group-item-heading > small, + .list-group-item-heading > .small { + color: inherit; + } + .list-group-item-text { + color: @list-group-active-text-color; + } + } +} + + +// Contextual variants +// +// Add modifier classes to change text and background color on individual items. +// Organizationally, this must come after the `:hover` states. + +.list-group-item-variant(success; @state-success-bg; @state-success-text); +.list-group-item-variant(info; @state-info-bg; @state-info-text); +.list-group-item-variant(warning; @state-warning-bg; @state-warning-text); +.list-group-item-variant(danger; @state-danger-bg; @state-danger-text); + + +// Custom content options +// +// Extra classes for creating well-formatted content within `.list-group-item`s. + +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} diff --git a/src/fmt/doc/bootstrap/media.less b/src/fmt/doc/bootstrap/media.less new file mode 100644 index 000000000..a6bf78192 --- /dev/null +++ b/src/fmt/doc/bootstrap/media.less @@ -0,0 +1,61 @@ +.media { + // Proper spacing between instances of .media + margin-top: 15px; + + &:first-child { + margin-top: 0; + } +} + +.media, +.media-body { + zoom: 1; + overflow: hidden; +} + +.media-body { + width: 10000px; +} + +.media-object { + display: block; +} + +.media-right, +.media > .pull-right { + padding-left: 10px; +} + +.media-left, +.media > .pull-left { + padding-right: 10px; +} + +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} + +.media-middle { + vertical-align: middle; +} + +.media-bottom { + vertical-align: bottom; +} + +// Reset margins on headings for tighter default spacing +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} + +// Media list variation +// +// Undo default ul/ol styles +.media-list { + padding-left: 0; + list-style: none; +} diff --git a/src/fmt/doc/bootstrap/mixins.less b/src/fmt/doc/bootstrap/mixins.less new file mode 100644 index 000000000..af4408fc2 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins.less @@ -0,0 +1,39 @@ +// Mixins +// -------------------------------------------------- + +// Utilities +@import "mixins/hide-text.less"; +@import "mixins/opacity.less"; +@import "mixins/image.less"; +@import "mixins/labels.less"; +@import "mixins/reset-filter.less"; +@import "mixins/resize.less"; +@import "mixins/responsive-visibility.less"; +@import "mixins/size.less"; +@import "mixins/tab-focus.less"; +@import "mixins/text-emphasis.less"; +@import "mixins/text-overflow.less"; +@import "mixins/vendor-prefixes.less"; + +// Components +@import "mixins/alerts.less"; +@import "mixins/buttons.less"; +@import "mixins/panels.less"; +@import "mixins/pagination.less"; +@import "mixins/list-group.less"; +@import "mixins/nav-divider.less"; +@import "mixins/forms.less"; +@import "mixins/progress-bar.less"; +@import "mixins/table-row.less"; + +// Skins +@import "mixins/background-variant.less"; +@import "mixins/border-radius.less"; +@import "mixins/gradients.less"; + +// Layout +@import "mixins/clearfix.less"; +@import "mixins/center-block.less"; +@import "mixins/nav-vertical-align.less"; +@import "mixins/grid-framework.less"; +@import "mixins/grid.less"; diff --git a/src/fmt/doc/bootstrap/mixins/alerts.less b/src/fmt/doc/bootstrap/mixins/alerts.less new file mode 100644 index 000000000..396196f43 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/alerts.less @@ -0,0 +1,14 @@ +// Alerts + +.alert-variant(@background; @border; @text-color) { + background-color: @background; + border-color: @border; + color: @text-color; + + hr { + border-top-color: darken(@border, 5%); + } + .alert-link { + color: darken(@text-color, 10%); + } +} diff --git a/src/fmt/doc/bootstrap/mixins/background-variant.less b/src/fmt/doc/bootstrap/mixins/background-variant.less new file mode 100644 index 000000000..556e490d4 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/background-variant.less @@ -0,0 +1,8 @@ +// Contextual backgrounds + +.bg-variant(@color) { + background-color: @color; + a&:hover { + background-color: darken(@color, 10%); + } +} diff --git a/src/fmt/doc/bootstrap/mixins/border-radius.less b/src/fmt/doc/bootstrap/mixins/border-radius.less new file mode 100644 index 000000000..ca05dbf45 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/border-radius.less @@ -0,0 +1,18 @@ +// Single side border-radius + +.border-top-radius(@radius) { + border-top-right-radius: @radius; + border-top-left-radius: @radius; +} +.border-right-radius(@radius) { + border-bottom-right-radius: @radius; + border-top-right-radius: @radius; +} +.border-bottom-radius(@radius) { + border-bottom-right-radius: @radius; + border-bottom-left-radius: @radius; +} +.border-left-radius(@radius) { + border-bottom-left-radius: @radius; + border-top-left-radius: @radius; +} diff --git a/src/fmt/doc/bootstrap/mixins/buttons.less b/src/fmt/doc/bootstrap/mixins/buttons.less new file mode 100644 index 000000000..92d8a056c --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/buttons.less @@ -0,0 +1,52 @@ +// Button variants +// +// Easily pump out default styles, as well as :hover, :focus, :active, +// and disabled options for all buttons + +.button-variant(@color; @background; @border) { + color: @color; + background-color: @background; + border-color: @border; + + &:hover, + &:focus, + &.focus, + &:active, + &.active, + .open > .dropdown-toggle& { + color: @color; + background-color: darken(@background, 10%); + border-color: darken(@border, 12%); + } + &:active, + &.active, + .open > .dropdown-toggle& { + background-image: none; + } + &.disabled, + &[disabled], + fieldset[disabled] & { + &, + &:hover, + &:focus, + &.focus, + &:active, + &.active { + background-color: @background; + border-color: @border; + } + } + + .badge { + color: @background; + background-color: @color; + } +} + +// Button sizes +.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) { + padding: @padding-vertical @padding-horizontal; + font-size: @font-size; + line-height: @line-height; + border-radius: @border-radius; +} diff --git a/src/fmt/doc/bootstrap/mixins/center-block.less b/src/fmt/doc/bootstrap/mixins/center-block.less new file mode 100644 index 000000000..d18d6de9e --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/center-block.less @@ -0,0 +1,7 @@ +// Center-align a block level element + +.center-block() { + display: block; + margin-left: auto; + margin-right: auto; +} diff --git a/src/fmt/doc/bootstrap/mixins/clearfix.less b/src/fmt/doc/bootstrap/mixins/clearfix.less new file mode 100644 index 000000000..3f7a3820c --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/clearfix.less @@ -0,0 +1,22 @@ +// Clearfix +// +// For modern browsers +// 1. The space content is one way to avoid an Opera bug when the +// contenteditable attribute is included anywhere else in the document. +// Otherwise it causes space to appear at the top and bottom of elements +// that are clearfixed. +// 2. The use of `table` rather than `block` is only necessary if using +// `:before` to contain the top-margins of child elements. +// +// Source: http://nicolasgallagher.com/micro-clearfix-hack/ + +.clearfix() { + &:before, + &:after { + content: " "; // 1 + display: table; // 2 + } + &:after { + clear: both; + } +} diff --git a/src/fmt/doc/bootstrap/mixins/forms.less b/src/fmt/doc/bootstrap/mixins/forms.less new file mode 100644 index 000000000..6f55ed967 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/forms.less @@ -0,0 +1,85 @@ +// Form validation states +// +// Used in forms.less to generate the form validation CSS for warnings, errors, +// and successes. + +.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) { + // Color the label and help text + .help-block, + .control-label, + .radio, + .checkbox, + .radio-inline, + .checkbox-inline, + &.radio label, + &.checkbox label, + &.radio-inline label, + &.checkbox-inline label { + color: @text-color; + } + // Set the border and box shadow on specific inputs to match + .form-control { + border-color: @border-color; + .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work + &:focus { + border-color: darken(@border-color, 10%); + @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%); + .box-shadow(@shadow); + } + } + // Set validation states also for addons + .input-group-addon { + color: @text-color; + border-color: @border-color; + background-color: @background-color; + } + // Optional feedback icon + .form-control-feedback { + color: @text-color; + } +} + + +// Form control focus state +// +// Generate a customized focus state and for any input with the specified color, +// which defaults to the `@input-border-focus` variable. +// +// We highly encourage you to not customize the default value, but instead use +// this to tweak colors on an as-needed basis. This aesthetic change is based on +// WebKit's default styles, but applicable to a wider range of browsers. Its +// usability and accessibility should be taken into account with any change. +// +// Example usage: change the default blue border and shadow to white for better +// contrast against a dark gray background. +.form-control-focus(@color: @input-border-focus) { + @color-rgba: rgba(red(@color), green(@color), blue(@color), .6); + &:focus { + border-color: @color; + outline: 0; + .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}"); + } +} + +// Form control sizing +// +// Relative text size, padding, and border-radii changes for form controls. For +// horizontal sizing, wrap controls in the predefined grid classes. `<select>` +// element gets special love because it's special, and that's a fact! +.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) { + height: @input-height; + padding: @padding-vertical @padding-horizontal; + font-size: @font-size; + line-height: @line-height; + border-radius: @border-radius; + + select& { + height: @input-height; + line-height: @input-height; + } + + textarea&, + select[multiple]& { + height: auto; + } +} diff --git a/src/fmt/doc/bootstrap/mixins/gradients.less b/src/fmt/doc/bootstrap/mixins/gradients.less new file mode 100644 index 000000000..0b88a89cc --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/gradients.less @@ -0,0 +1,59 @@ +// Gradients + +#gradient { + + // Horizontal gradient, from left to right + // + // Creates two color stops, start and end, by specifying a color and position for each color stop. + // Color stops are not available in IE9 and below. + .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) { + background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+ + background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12 + background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+ + background-repeat: repeat-x; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",argb(@start-color),argb(@end-color))); // IE9 and down + } + + // Vertical gradient, from top to bottom + // + // Creates two color stops, start and end, by specifying a color and position for each color stop. + // Color stops are not available in IE9 and below. + .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) { + background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+ + background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12 + background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+ + background-repeat: repeat-x; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@start-color),argb(@end-color))); // IE9 and down + } + + .directional(@start-color: #555; @end-color: #333; @deg: 45deg) { + background-repeat: repeat-x; + background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+ + background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12 + background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+ + } + .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) { + background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color); + background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color); + background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color); + background-repeat: no-repeat; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback + } + .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) { + background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color); + background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color); + background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color); + background-repeat: no-repeat; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback + } + .radial(@inner-color: #555; @outer-color: #333) { + background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color); + background-image: radial-gradient(circle, @inner-color, @outer-color); + background-repeat: no-repeat; + } + .striped(@color: rgba(255,255,255,.15); @angle: 45deg) { + background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent); + background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent); + } +} diff --git a/src/fmt/doc/bootstrap/mixins/grid-framework.less b/src/fmt/doc/bootstrap/mixins/grid-framework.less new file mode 100644 index 000000000..f3b3929d6 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/grid-framework.less @@ -0,0 +1,91 @@ +// Framework grid generation +// +// Used only by Bootstrap to generate the correct number of grid classes given +// any value of `@grid-columns`. + +.make-grid-columns() { + // Common styles for all sizes of grid columns, widths 1-12 + .col(@index) { // initial + @item: ~".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}"; + .col((@index + 1), @item); + } + .col(@index, @list) when (@index =< @grid-columns) { // general; "=<" isn't a typo + @item: ~".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}"; + .col((@index + 1), ~"@{list}, @{item}"); + } + .col(@index, @list) when (@index > @grid-columns) { // terminal + @{list} { + position: relative; + // Prevent columns from collapsing when empty + min-height: 1px; + // Inner gutter via padding + padding-left: (@grid-gutter-width / 2); + padding-right: (@grid-gutter-width / 2); + } + } + .col(1); // kickstart it +} + +.float-grid-columns(@class) { + .col(@index) { // initial + @item: ~".col-@{class}-@{index}"; + .col((@index + 1), @item); + } + .col(@index, @list) when (@index =< @grid-columns) { // general + @item: ~".col-@{class}-@{index}"; + .col((@index + 1), ~"@{list}, @{item}"); + } + .col(@index, @list) when (@index > @grid-columns) { // terminal + @{list} { + float: left; + } + } + .col(1); // kickstart it +} + +.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) { + .col-@{class}-@{index} { + width: percentage((@index / @grid-columns)); + } +} +.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) { + .col-@{class}-push-@{index} { + left: percentage((@index / @grid-columns)); + } +} +.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) { + .col-@{class}-push-0 { + left: auto; + } +} +.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) { + .col-@{class}-pull-@{index} { + right: percentage((@index / @grid-columns)); + } +} +.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) { + .col-@{class}-pull-0 { + right: auto; + } +} +.calc-grid-column(@index, @class, @type) when (@type = offset) { + .col-@{class}-offset-@{index} { + margin-left: percentage((@index / @grid-columns)); + } +} + +// Basic looping in LESS +.loop-grid-columns(@index, @class, @type) when (@index >= 0) { + .calc-grid-column(@index, @class, @type); + // next iteration + .loop-grid-columns((@index - 1), @class, @type); +} + +// Create grid for specific class +.make-grid(@class) { + .float-grid-columns(@class); + .loop-grid-columns(@grid-columns, @class, width); + .loop-grid-columns(@grid-columns, @class, pull); + .loop-grid-columns(@grid-columns, @class, push); + .loop-grid-columns(@grid-columns, @class, offset); +} diff --git a/src/fmt/doc/bootstrap/mixins/grid.less b/src/fmt/doc/bootstrap/mixins/grid.less new file mode 100644 index 000000000..cae5eaff9 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/grid.less @@ -0,0 +1,122 @@ +// Grid system +// +// Generate semantic grid columns with these mixins. + +// Centered container element +.container-fixed(@gutter: @grid-gutter-width) { + margin-right: auto; + margin-left: auto; + padding-left: (@gutter / 2); + padding-right: (@gutter / 2); + &:extend(.clearfix all); +} + +// Creates a wrapper for a series of columns +.make-row(@gutter: @grid-gutter-width) { + margin-left: (@gutter / -2); + margin-right: (@gutter / -2); + &:extend(.clearfix all); +} + +// Generate the extra small columns +.make-xs-column(@columns; @gutter: @grid-gutter-width) { + position: relative; + float: left; + width: percentage((@columns / @grid-columns)); + min-height: 1px; + padding-left: (@gutter / 2); + padding-right: (@gutter / 2); +} +.make-xs-column-offset(@columns) { + margin-left: percentage((@columns / @grid-columns)); +} +.make-xs-column-push(@columns) { + left: percentage((@columns / @grid-columns)); +} +.make-xs-column-pull(@columns) { + right: percentage((@columns / @grid-columns)); +} + +// Generate the small columns +.make-sm-column(@columns; @gutter: @grid-gutter-width) { + position: relative; + min-height: 1px; + padding-left: (@gutter / 2); + padding-right: (@gutter / 2); + + @media (min-width: @screen-sm-min) { + float: left; + width: percentage((@columns / @grid-columns)); + } +} +.make-sm-column-offset(@columns) { + @media (min-width: @screen-sm-min) { + margin-left: percentage((@columns / @grid-columns)); + } +} +.make-sm-column-push(@columns) { + @media (min-width: @screen-sm-min) { + left: percentage((@columns / @grid-columns)); + } +} +.make-sm-column-pull(@columns) { + @media (min-width: @screen-sm-min) { + right: percentage((@columns / @grid-columns)); + } +} + +// Generate the medium columns +.make-md-column(@columns; @gutter: @grid-gutter-width) { + position: relative; + min-height: 1px; + padding-left: (@gutter / 2); + padding-right: (@gutter / 2); + + @media (min-width: @screen-md-min) { + float: left; + width: percentage((@columns / @grid-columns)); + } +} +.make-md-column-offset(@columns) { + @media (min-width: @screen-md-min) { + margin-left: percentage((@columns / @grid-columns)); + } +} +.make-md-column-push(@columns) { + @media (min-width: @screen-md-min) { + left: percentage((@columns / @grid-columns)); + } +} +.make-md-column-pull(@columns) { + @media (min-width: @screen-md-min) { + right: percentage((@columns / @grid-columns)); + } +} + +// Generate the large columns +.make-lg-column(@columns; @gutter: @grid-gutter-width) { + position: relative; + min-height: 1px; + padding-left: (@gutter / 2); + padding-right: (@gutter / 2); + + @media (min-width: @screen-lg-min) { + float: left; + width: percentage((@columns / @grid-columns)); + } +} +.make-lg-column-offset(@columns) { + @media (min-width: @screen-lg-min) { + margin-left: percentage((@columns / @grid-columns)); + } +} +.make-lg-column-push(@columns) { + @media (min-width: @screen-lg-min) { + left: percentage((@columns / @grid-columns)); + } +} +.make-lg-column-pull(@columns) { + @media (min-width: @screen-lg-min) { + right: percentage((@columns / @grid-columns)); + } +} diff --git a/src/fmt/doc/bootstrap/mixins/hide-text.less b/src/fmt/doc/bootstrap/mixins/hide-text.less new file mode 100644 index 000000000..c2315e572 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/hide-text.less @@ -0,0 +1,21 @@ +// CSS image replacement +// +// Heads up! v3 launched with with only `.hide-text()`, but per our pattern for +// mixins being reused as classes with the same name, this doesn't hold up. As +// of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`. +// +// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757 + +// Deprecated as of v3.0.1 (will be removed in v4) +.hide-text() { + font: ~"0/0" a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +// New mixin to use as of v3.0.1 +.text-hide() { + .hide-text(); +} diff --git a/src/fmt/doc/bootstrap/mixins/image.less b/src/fmt/doc/bootstrap/mixins/image.less new file mode 100644 index 000000000..f233cb3e1 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/image.less @@ -0,0 +1,33 @@ +// Image Mixins +// - Responsive image +// - Retina image + + +// Responsive image +// +// Keep images from scaling beyond the width of their parents. +.img-responsive(@display: block) { + display: @display; + max-width: 100%; // Part 1: Set a maximum relative to the parent + height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching +} + + +// Retina image +// +// Short retina mixin for setting background-image and -size. Note that the +// spelling of `min--moz-device-pixel-ratio` is intentional. +.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) { + background-image: url("@{file-1x}"); + + @media + only screen and (-webkit-min-device-pixel-ratio: 2), + only screen and ( min--moz-device-pixel-ratio: 2), + only screen and ( -o-min-device-pixel-ratio: 2/1), + only screen and ( min-device-pixel-ratio: 2), + only screen and ( min-resolution: 192dpi), + only screen and ( min-resolution: 2dppx) { + background-image: url("@{file-2x}"); + background-size: @width-1x @height-1x; + } +} diff --git a/src/fmt/doc/bootstrap/mixins/labels.less b/src/fmt/doc/bootstrap/mixins/labels.less new file mode 100644 index 000000000..9f7a67ee3 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/labels.less @@ -0,0 +1,12 @@ +// Labels + +.label-variant(@color) { + background-color: @color; + + &[href] { + &:hover, + &:focus { + background-color: darken(@color, 10%); + } + } +} diff --git a/src/fmt/doc/bootstrap/mixins/list-group.less b/src/fmt/doc/bootstrap/mixins/list-group.less new file mode 100644 index 000000000..8b5b065cb --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/list-group.less @@ -0,0 +1,29 @@ +// List Groups + +.list-group-item-variant(@state; @background; @color) { + .list-group-item-@{state} { + color: @color; + background-color: @background; + + a& { + color: @color; + + .list-group-item-heading { + color: inherit; + } + + &:hover, + &:focus { + color: @color; + background-color: darken(@background, 5%); + } + &.active, + &.active:hover, + &.active:focus { + color: #fff; + background-color: @color; + border-color: @color; + } + } + } +} diff --git a/src/fmt/doc/bootstrap/mixins/nav-divider.less b/src/fmt/doc/bootstrap/mixins/nav-divider.less new file mode 100644 index 000000000..feb1e9ed0 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/nav-divider.less @@ -0,0 +1,10 @@ +// Horizontal dividers +// +// Dividers (basically an hr) within dropdowns and nav lists + +.nav-divider(@color: #e5e5e5) { + height: 1px; + margin: ((@line-height-computed / 2) - 1) 0; + overflow: hidden; + background-color: @color; +} diff --git a/src/fmt/doc/bootstrap/mixins/nav-vertical-align.less b/src/fmt/doc/bootstrap/mixins/nav-vertical-align.less new file mode 100644 index 000000000..d458c7861 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/nav-vertical-align.less @@ -0,0 +1,9 @@ +// Navbar vertical align +// +// Vertically center elements in the navbar. +// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin. + +.navbar-vertical-align(@element-height) { + margin-top: ((@navbar-height - @element-height) / 2); + margin-bottom: ((@navbar-height - @element-height) / 2); +} diff --git a/src/fmt/doc/bootstrap/mixins/opacity.less b/src/fmt/doc/bootstrap/mixins/opacity.less new file mode 100644 index 000000000..33ed25ce6 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/opacity.less @@ -0,0 +1,8 @@ +// Opacity + +.opacity(@opacity) { + opacity: @opacity; + // IE8 filter + @opacity-ie: (@opacity * 100); + filter: ~"alpha(opacity=@{opacity-ie})"; +} diff --git a/src/fmt/doc/bootstrap/mixins/pagination.less b/src/fmt/doc/bootstrap/mixins/pagination.less new file mode 100644 index 000000000..7deb505d2 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/pagination.less @@ -0,0 +1,23 @@ +// Pagination + +.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @border-radius) { + > li { + > a, + > span { + padding: @padding-vertical @padding-horizontal; + font-size: @font-size; + } + &:first-child { + > a, + > span { + .border-left-radius(@border-radius); + } + } + &:last-child { + > a, + > span { + .border-right-radius(@border-radius); + } + } + } +} diff --git a/src/fmt/doc/bootstrap/mixins/panels.less b/src/fmt/doc/bootstrap/mixins/panels.less new file mode 100644 index 000000000..49ee10d4a --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/panels.less @@ -0,0 +1,24 @@ +// Panels + +.panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) { + border-color: @border; + + & > .panel-heading { + color: @heading-text-color; + background-color: @heading-bg-color; + border-color: @heading-border; + + + .panel-collapse > .panel-body { + border-top-color: @border; + } + .badge { + color: @heading-bg-color; + background-color: @heading-text-color; + } + } + & > .panel-footer { + + .panel-collapse > .panel-body { + border-bottom-color: @border; + } + } +} diff --git a/src/fmt/doc/bootstrap/mixins/progress-bar.less b/src/fmt/doc/bootstrap/mixins/progress-bar.less new file mode 100644 index 000000000..f07996a34 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/progress-bar.less @@ -0,0 +1,10 @@ +// Progress bars + +.progress-bar-variant(@color) { + background-color: @color; + + // Deprecated parent class requirement as of v3.2.0 + .progress-striped & { + #gradient > .striped(); + } +} diff --git a/src/fmt/doc/bootstrap/mixins/reset-filter.less b/src/fmt/doc/bootstrap/mixins/reset-filter.less new file mode 100644 index 000000000..68cdb5e18 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/reset-filter.less @@ -0,0 +1,8 @@ +// Reset filters for IE +// +// When you need to remove a gradient background, do not forget to use this to reset +// the IE filter for IE9 and below. + +.reset-filter() { + filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)")); +} diff --git a/src/fmt/doc/bootstrap/mixins/resize.less b/src/fmt/doc/bootstrap/mixins/resize.less new file mode 100644 index 000000000..3acd3afdb --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/resize.less @@ -0,0 +1,6 @@ +// Resize anything + +.resizable(@direction) { + resize: @direction; // Options: horizontal, vertical, both + overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible` +} diff --git a/src/fmt/doc/bootstrap/mixins/responsive-visibility.less b/src/fmt/doc/bootstrap/mixins/responsive-visibility.less new file mode 100644 index 000000000..f7951c3d7 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/responsive-visibility.less @@ -0,0 +1,15 @@ +// Responsive utilities + +// +// More easily include all the states for responsive-utilities.less. +.responsive-visibility() { + display: block !important; + table& { display: table; } + tr& { display: table-row !important; } + th&, + td& { display: table-cell !important; } +} + +.responsive-invisibility() { + display: none !important; +} diff --git a/src/fmt/doc/bootstrap/mixins/size.less b/src/fmt/doc/bootstrap/mixins/size.less new file mode 100644 index 000000000..a8be65089 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/size.less @@ -0,0 +1,10 @@ +// Sizing shortcuts + +.size(@width; @height) { + width: @width; + height: @height; +} + +.square(@size) { + .size(@size; @size); +} diff --git a/src/fmt/doc/bootstrap/mixins/tab-focus.less b/src/fmt/doc/bootstrap/mixins/tab-focus.less new file mode 100644 index 000000000..1f1f05ab0 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/tab-focus.less @@ -0,0 +1,9 @@ +// WebKit-style focus + +.tab-focus() { + // Default + outline: thin dotted; + // WebKit + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} diff --git a/src/fmt/doc/bootstrap/mixins/table-row.less b/src/fmt/doc/bootstrap/mixins/table-row.less new file mode 100644 index 000000000..0f287f1a8 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/table-row.less @@ -0,0 +1,28 @@ +// Tables + +.table-row-variant(@state; @background) { + // Exact selectors below required to override `.table-striped` and prevent + // inheritance to nested tables. + .table > thead > tr, + .table > tbody > tr, + .table > tfoot > tr { + > td.@{state}, + > th.@{state}, + &.@{state} > td, + &.@{state} > th { + background-color: @background; + } + } + + // Hover states for `.table-hover` + // Note: this is not available for cells or rows within `thead` or `tfoot`. + .table-hover > tbody > tr { + > td.@{state}:hover, + > th.@{state}:hover, + &.@{state}:hover > td, + &:hover > .@{state}, + &.@{state}:hover > th { + background-color: darken(@background, 5%); + } + } +} diff --git a/src/fmt/doc/bootstrap/mixins/text-emphasis.less b/src/fmt/doc/bootstrap/mixins/text-emphasis.less new file mode 100644 index 000000000..0868ef9f2 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/text-emphasis.less @@ -0,0 +1,8 @@ +// Typography + +.text-emphasis-variant(@color) { + color: @color; + a&:hover { + color: darken(@color, 10%); + } +} diff --git a/src/fmt/doc/bootstrap/mixins/text-overflow.less b/src/fmt/doc/bootstrap/mixins/text-overflow.less new file mode 100644 index 000000000..c11ad2fb7 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/text-overflow.less @@ -0,0 +1,8 @@ +// Text overflow +// Requires inline-block or block for proper styling + +.text-overflow() { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/src/fmt/doc/bootstrap/mixins/vendor-prefixes.less b/src/fmt/doc/bootstrap/mixins/vendor-prefixes.less new file mode 100644 index 000000000..afd3331c3 --- /dev/null +++ b/src/fmt/doc/bootstrap/mixins/vendor-prefixes.less @@ -0,0 +1,227 @@ +// Vendor Prefixes +// +// All vendor mixins are deprecated as of v3.2.0 due to the introduction of +// Autoprefixer in our Gruntfile. They will be removed in v4. + +// - Animations +// - Backface visibility +// - Box shadow +// - Box sizing +// - Content columns +// - Hyphens +// - Placeholder text +// - Transformations +// - Transitions +// - User Select + + +// Animations +.animation(@animation) { + -webkit-animation: @animation; + -o-animation: @animation; + animation: @animation; +} +.animation-name(@name) { + -webkit-animation-name: @name; + animation-name: @name; +} +.animation-duration(@duration) { + -webkit-animation-duration: @duration; + animation-duration: @duration; +} +.animation-timing-function(@timing-function) { + -webkit-animation-timing-function: @timing-function; + animation-timing-function: @timing-function; +} +.animation-delay(@delay) { + -webkit-animation-delay: @delay; + animation-delay: @delay; +} +.animation-iteration-count(@iteration-count) { + -webkit-animation-iteration-count: @iteration-count; + animation-iteration-count: @iteration-count; +} +.animation-direction(@direction) { + -webkit-animation-direction: @direction; + animation-direction: @direction; +} +.animation-fill-mode(@fill-mode) { + -webkit-animation-fill-mode: @fill-mode; + animation-fill-mode: @fill-mode; +} + +// Backface visibility +// Prevent browsers from flickering when using CSS 3D transforms. +// Default value is `visible`, but can be changed to `hidden` + +.backface-visibility(@visibility){ + -webkit-backface-visibility: @visibility; + -moz-backface-visibility: @visibility; + backface-visibility: @visibility; +} + +// Drop shadows +// +// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's +// supported browsers that have box shadow capabilities now support it. + +.box-shadow(@shadow) { + -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1 + box-shadow: @shadow; +} + +// Box sizing +.box-sizing(@boxmodel) { + -webkit-box-sizing: @boxmodel; + -moz-box-sizing: @boxmodel; + box-sizing: @boxmodel; +} + +// CSS3 Content Columns +.content-columns(@column-count; @column-gap: @grid-gutter-width) { + -webkit-column-count: @column-count; + -moz-column-count: @column-count; + column-count: @column-count; + -webkit-column-gap: @column-gap; + -moz-column-gap: @column-gap; + column-gap: @column-gap; +} + +// Optional hyphenation +.hyphens(@mode: auto) { + word-wrap: break-word; + -webkit-hyphens: @mode; + -moz-hyphens: @mode; + -ms-hyphens: @mode; // IE10+ + -o-hyphens: @mode; + hyphens: @mode; +} + +// Placeholder text +.placeholder(@color: @input-color-placeholder) { + // Firefox + &::-moz-placeholder { + color: @color; + opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526 + } + &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+ + &::-webkit-input-placeholder { color: @color; } // Safari and Chrome +} + +// Transformations +.scale(@ratio) { + -webkit-transform: scale(@ratio); + -ms-transform: scale(@ratio); // IE9 only + -o-transform: scale(@ratio); + transform: scale(@ratio); +} +.scale(@ratioX; @ratioY) { + -webkit-transform: scale(@ratioX, @ratioY); + -ms-transform: scale(@ratioX, @ratioY); // IE9 only + -o-transform: scale(@ratioX, @ratioY); + transform: scale(@ratioX, @ratioY); +} +.scaleX(@ratio) { + -webkit-transform: scaleX(@ratio); + -ms-transform: scaleX(@ratio); // IE9 only + -o-transform: scaleX(@ratio); + transform: scaleX(@ratio); +} +.scaleY(@ratio) { + -webkit-transform: scaleY(@ratio); + -ms-transform: scaleY(@ratio); // IE9 only + -o-transform: scaleY(@ratio); + transform: scaleY(@ratio); +} +.skew(@x; @y) { + -webkit-transform: skewX(@x) skewY(@y); + -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+ + -o-transform: skewX(@x) skewY(@y); + transform: skewX(@x) skewY(@y); +} +.translate(@x; @y) { + -webkit-transform: translate(@x, @y); + -ms-transform: translate(@x, @y); // IE9 only + -o-transform: translate(@x, @y); + transform: translate(@x, @y); +} +.translate3d(@x; @y; @z) { + -webkit-transform: translate3d(@x, @y, @z); + transform: translate3d(@x, @y, @z); +} +.rotate(@degrees) { + -webkit-transform: rotate(@degrees); + -ms-transform: rotate(@degrees); // IE9 only + -o-transform: rotate(@degrees); + transform: rotate(@degrees); +} +.rotateX(@degrees) { + -webkit-transform: rotateX(@degrees); + -ms-transform: rotateX(@degrees); // IE9 only + -o-transform: rotateX(@degrees); + transform: rotateX(@degrees); +} +.rotateY(@degrees) { + -webkit-transform: rotateY(@degrees); + -ms-transform: rotateY(@degrees); // IE9 only + -o-transform: rotateY(@degrees); + transform: rotateY(@degrees); +} +.perspective(@perspective) { + -webkit-perspective: @perspective; + -moz-perspective: @perspective; + perspective: @perspective; +} +.perspective-origin(@perspective) { + -webkit-perspective-origin: @perspective; + -moz-perspective-origin: @perspective; + perspective-origin: @perspective; +} +.transform-origin(@origin) { + -webkit-transform-origin: @origin; + -moz-transform-origin: @origin; + -ms-transform-origin: @origin; // IE9 only + transform-origin: @origin; +} + + +// Transitions + +.transition(@transition) { + -webkit-transition: @transition; + -o-transition: @transition; + transition: @transition; +} +.transition-property(@transition-property) { + -webkit-transition-property: @transition-property; + transition-property: @transition-property; +} +.transition-delay(@transition-delay) { + -webkit-transition-delay: @transition-delay; + transition-delay: @transition-delay; +} +.transition-duration(@transition-duration) { + -webkit-transition-duration: @transition-duration; + transition-duration: @transition-duration; +} +.transition-timing-function(@timing-function) { + -webkit-transition-timing-function: @timing-function; + transition-timing-function: @timing-function; +} +.transition-transform(@transition) { + -webkit-transition: -webkit-transform @transition; + -moz-transition: -moz-transform @transition; + -o-transition: -o-transform @transition; + transition: transform @transition; +} + + +// User select +// For selecting text on the page + +.user-select(@select) { + -webkit-user-select: @select; + -moz-user-select: @select; + -ms-user-select: @select; // IE10+ + user-select: @select; +} diff --git a/src/fmt/doc/bootstrap/modals.less b/src/fmt/doc/bootstrap/modals.less new file mode 100644 index 000000000..1de622050 --- /dev/null +++ b/src/fmt/doc/bootstrap/modals.less @@ -0,0 +1,150 @@ +// +// Modals +// -------------------------------------------------- + +// .modal-open - body class for killing the scroll +// .modal - container to scroll within +// .modal-dialog - positioning shell for the actual modal +// .modal-content - actual modal w/ bg and corners and shit + +// Kill the scroll on the body +.modal-open { + overflow: hidden; +} + +// Container that the modal scrolls within +.modal { + display: none; + overflow: hidden; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: @zindex-modal; + -webkit-overflow-scrolling: touch; + + // Prevent Chrome on Windows from adding a focus outline. For details, see + // https://github.com/twbs/bootstrap/pull/10951. + outline: 0; + + // When fading in the modal, animate it to slide down + &.fade .modal-dialog { + .translate(0, -25%); + .transition-transform(~"0.3s ease-out"); + } + &.in .modal-dialog { .translate(0, 0) } +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} + +// Shell div to position the modal with bottom padding +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} + +// Actual modal +.modal-content { + position: relative; + background-color: @modal-content-bg; + border: 1px solid @modal-content-fallback-border-color; //old browsers fallback (ie8 etc) + border: 1px solid @modal-content-border-color; + border-radius: @border-radius-large; + .box-shadow(0 3px 9px rgba(0,0,0,.5)); + background-clip: padding-box; + // Remove focus outline from opened modal + outline: 0; +} + +// Modal background +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: @zindex-modal-background; + background-color: @modal-backdrop-bg; + // Fade for backdrop + &.fade { .opacity(0); } + &.in { .opacity(@modal-backdrop-opacity); } +} + +// Modal header +// Top section of the modal w/ title and dismiss +.modal-header { + padding: @modal-title-padding; + border-bottom: 1px solid @modal-header-border-color; + min-height: (@modal-title-padding + @modal-title-line-height); +} +// Close icon +.modal-header .close { + margin-top: -2px; +} + +// Title text within header +.modal-title { + margin: 0; + line-height: @modal-title-line-height; +} + +// Modal body +// Where all modal content resides (sibling of .modal-header and .modal-footer) +.modal-body { + position: relative; + padding: @modal-inner-padding; +} + +// Footer (for actions) +.modal-footer { + padding: @modal-inner-padding; + text-align: right; // right align buttons + border-top: 1px solid @modal-footer-border-color; + &:extend(.clearfix all); // clear it in case folks use .pull-* classes on buttons + + // Properly space out buttons + .btn + .btn { + margin-left: 5px; + margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs + } + // but override that for button groups + .btn-group .btn + .btn { + margin-left: -1px; + } + // and override it for block buttons as well + .btn-block + .btn-block { + margin-left: 0; + } +} + +// Measure scrollbar width for padding body during modal show/hide +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} + +// Scale up the modal +@media (min-width: @screen-sm-min) { + // Automatically set modal's width for larger viewports + .modal-dialog { + width: @modal-md; + margin: 30px auto; + } + .modal-content { + .box-shadow(0 5px 15px rgba(0,0,0,.5)); + } + + // Modal sizes + .modal-sm { width: @modal-sm; } +} + +@media (min-width: @screen-md-min) { + .modal-lg { width: @modal-lg; } +} diff --git a/src/fmt/doc/bootstrap/navbar.less b/src/fmt/doc/bootstrap/navbar.less new file mode 100644 index 000000000..03f4e2ac7 --- /dev/null +++ b/src/fmt/doc/bootstrap/navbar.less @@ -0,0 +1,660 @@ +// +// Navbars +// -------------------------------------------------- + + +// Wrapper and base class +// +// Provide a static navbar from which we expand to create full-width, fixed, and +// other navbar variations. + +.navbar { + position: relative; + min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode) + margin-bottom: @navbar-margin-bottom; + border: 1px solid transparent; + + // Prevent floats from breaking the navbar + &:extend(.clearfix all); + + @media (min-width: @grid-float-breakpoint) { + border-radius: @navbar-border-radius; + } +} + + +// Navbar heading +// +// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy +// styling of responsive aspects. + +.navbar-header { + &:extend(.clearfix all); + + @media (min-width: @grid-float-breakpoint) { + float: left; + } +} + + +// Navbar collapse (body) +// +// Group your navbar content into this for easy collapsing and expanding across +// various device sizes. By default, this content is collapsed when <768px, but +// will expand past that for a horizontal display. +// +// To start (on mobile devices) the navbar links, forms, and buttons are stacked +// vertically and include a `max-height` to overflow in case you have too much +// content for the user's viewport. + +.navbar-collapse { + overflow-x: visible; + padding-right: @navbar-padding-horizontal; + padding-left: @navbar-padding-horizontal; + border-top: 1px solid transparent; + box-shadow: inset 0 1px 0 rgba(255,255,255,.1); + &:extend(.clearfix all); + -webkit-overflow-scrolling: touch; + + &.in { + overflow-y: auto; + } + + @media (min-width: @grid-float-breakpoint) { + width: auto; + border-top: 0; + box-shadow: none; + + &.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; // Override default setting + overflow: visible !important; + } + + &.in { + overflow-y: visible; + } + + // Undo the collapse side padding for navbars with containers to ensure + // alignment of right-aligned contents. + .navbar-fixed-top &, + .navbar-static-top &, + .navbar-fixed-bottom & { + padding-left: 0; + padding-right: 0; + } + } +} + +.navbar-fixed-top, +.navbar-fixed-bottom { + .navbar-collapse { + max-height: @navbar-collapse-max-height; + + @media (max-device-width: @screen-xs-min) and (orientation: landscape) { + max-height: 200px; + } + } +} + + +// Both navbar header and collapse +// +// When a container is present, change the behavior of the header and collapse. + +.tb-container, +.container-fluid { + > .navbar-header, + > .navbar-collapse { + margin-right: -@navbar-padding-horizontal; + margin-left: -@navbar-padding-horizontal; + + @media (min-width: @grid-float-breakpoint) { + margin-right: 0; + margin-left: 0; + } + } +} + + +// +// Navbar alignment options +// +// Display the navbar across the entirety of the page or fixed it to the top or +// bottom of the page. + +// Static top (unfixed, but 100% wide) navbar +.navbar-static-top { + z-index: @zindex-navbar; + border-width: 0 0 1px; + + @media (min-width: @grid-float-breakpoint) { + border-radius: 0; + } +} + +// Fix the top/bottom navbars when screen real estate supports it +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: @zindex-navbar-fixed; + + // Undo the rounded corners + @media (min-width: @grid-float-breakpoint) { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; // override .navbar defaults + border-width: 1px 0 0; +} + + +// Brand/project name + +.navbar-brand { + float: left; + padding: @navbar-padding-vertical @navbar-padding-horizontal; + font-size: @font-size-large; + line-height: @line-height-computed; + height: @navbar-height; + + &:hover, + &:focus { + text-decoration: none; + } + + > img { + display: block; + } + + @media (min-width: @grid-float-breakpoint) { + .navbar > .tb-container &, + .navbar > .container-fluid & { + margin-left: -@navbar-padding-horizontal; + } + } +} + + +// Navbar toggle +// +// Custom button for toggling the `.navbar-collapse`, powered by the collapse +// JavaScript plugin. + +.navbar-toggle { + position: relative; + float: right; + margin-right: @navbar-padding-horizontal; + padding: 9px 10px; + .navbar-vertical-align(34px); + background-color: transparent; + background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 + border: 1px solid transparent; + border-radius: @border-radius-base; + + // We remove the `outline` here, but later compensate by attaching `:hover` + // styles to `:focus`. + &:focus { + outline: 0; + } + + // Bars + .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; + } + .icon-bar + .icon-bar { + margin-top: 4px; + } + + @media (min-width: @grid-float-breakpoint) { + display: none; + } +} + + +// Navbar nav links +// +// Builds on top of the `.nav` components with its own modifier class to make +// the nav the full height of the horizontal nav (above 768px). + +.navbar-nav { + margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal; + + > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: @line-height-computed; + } + + @media (max-width: @grid-float-breakpoint-max) { + // Dropdowns get custom display when collapsed + .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + box-shadow: none; + > li > a, + .dropdown-header { + padding: 5px 15px 5px 25px; + } + > li > a { + line-height: @line-height-computed; + &:hover, + &:focus { + background-image: none; + } + } + } + } + + // Uncollapse the nav + @media (min-width: @grid-float-breakpoint) { + float: left; + margin: 0; + + > li { + float: left; + > a { + padding-top: @navbar-padding-vertical; + padding-bottom: @navbar-padding-vertical; + } + } + } +} + + +// Navbar form +// +// Extension of the `.form-inline` with some extra flavor for optimum display in +// our navbars. + +.navbar-form { + margin-left: -@navbar-padding-horizontal; + margin-right: -@navbar-padding-horizontal; + padding: 10px @navbar-padding-horizontal; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1); + .box-shadow(@shadow); + + // Mixin behavior for optimum display + .form-inline(); + + .form-group { + @media (max-width: @grid-float-breakpoint-max) { + margin-bottom: 5px; + + &:last-child { + margin-bottom: 0; + } + } + } + + // Vertically center in expanded, horizontal navbar + .navbar-vertical-align(@input-height-base); + + // Undo 100% width for pull classes + @media (min-width: @grid-float-breakpoint) { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + .box-shadow(none); + } +} + + +// Dropdown menus + +// Menu position and menu carets +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + .border-top-radius(0); +} +// Menu position and menu caret support for dropups via extra dropup class +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + .border-top-radius(@navbar-border-radius); + .border-bottom-radius(0); +} + + +// Buttons in navbars +// +// Vertically center a button within a navbar (when *not* in a form). + +.navbar-btn { + .navbar-vertical-align(@input-height-base); + + &.btn-sm { + .navbar-vertical-align(@input-height-small); + } + &.btn-xs { + .navbar-vertical-align(22); + } +} + + +// Text in navbars +// +// Add a class to make any element properly align itself vertically within the navbars. + +.navbar-text { + .navbar-vertical-align(@line-height-computed); + + @media (min-width: @grid-float-breakpoint) { + float: left; + margin-left: @navbar-padding-horizontal; + margin-right: @navbar-padding-horizontal; + } +} + + +// Component alignment +// +// Repurpose the pull utilities as their own navbar utilities to avoid specificity +// issues with parents and chaining. Only do this when the navbar is uncollapsed +// though so that navbar contents properly stack and align in mobile. +// +// Declared after the navbar components to ensure more specificity on the margins. + +@media (min-width: @grid-float-breakpoint) { + .navbar-left { .pull-left(); } + .navbar-right { + .pull-right(); + margin-right: -@navbar-padding-horizontal; + + ~ .navbar-right { + margin-right: 0; + } + } +} + + +// Alternate navbars +// -------------------------------------------------- + +// Default navbar +.navbar-default { + background-color: @navbar-default-bg; + border-color: @navbar-default-border; + + .navbar-brand { + color: @navbar-default-brand-color; + &:hover, + &:focus { + color: @navbar-default-brand-hover-color; + background-color: @navbar-default-brand-hover-bg; + } + } + + .navbar-text { + color: @navbar-default-color; + } + + .navbar-nav { + > li > a { + color: @navbar-default-link-color; + + &:hover, + &:focus { + color: @navbar-default-link-hover-color; + background-color: @navbar-default-link-hover-bg; + } + } + > .active > a { + &, + &:hover, + &:focus { + color: @navbar-default-link-active-color; + background-color: @navbar-default-link-active-bg; + } + } + > .disabled > a { + &, + &:hover, + &:focus { + color: @navbar-default-link-disabled-color; + background-color: @navbar-default-link-disabled-bg; + } + } + } + + .navbar-toggle { + border-color: @navbar-default-toggle-border-color; + &:hover, + &:focus { + background-color: @navbar-default-toggle-hover-bg; + } + .icon-bar { + background-color: @navbar-default-toggle-icon-bar-bg; + } + } + + .navbar-collapse, + .navbar-form { + border-color: @navbar-default-border; + } + + // Dropdown menu items + .navbar-nav { + // Remove background color from open dropdown + > .open > a { + &, + &:hover, + &:focus { + background-color: @navbar-default-link-active-bg; + color: @navbar-default-link-active-color; + } + } + + @media (max-width: @grid-float-breakpoint-max) { + // Dropdowns get custom display when collapsed + .open .dropdown-menu { + > li > a { + color: @navbar-default-link-color; + &:hover, + &:focus { + color: @navbar-default-link-hover-color; + background-color: @navbar-default-link-hover-bg; + } + } + > .active > a { + &, + &:hover, + &:focus { + color: @navbar-default-link-active-color; + background-color: @navbar-default-link-active-bg; + } + } + > .disabled > a { + &, + &:hover, + &:focus { + color: @navbar-default-link-disabled-color; + background-color: @navbar-default-link-disabled-bg; + } + } + } + } + } + + + // Links in navbars + // + // Add a class to ensure links outside the navbar nav are colored correctly. + + .navbar-link { + color: @navbar-default-link-color; + &:hover { + color: @navbar-default-link-hover-color; + } + } + + .btn-link { + color: @navbar-default-link-color; + &:hover, + &:focus { + color: @navbar-default-link-hover-color; + } + &[disabled], + fieldset[disabled] & { + &:hover, + &:focus { + color: @navbar-default-link-disabled-color; + } + } + } +} + +// Inverse navbar + +.navbar-inverse { + background-color: @navbar-inverse-bg; + border-color: @navbar-inverse-border; + + .navbar-brand { + color: @navbar-inverse-brand-color; + &:hover, + &:focus { + color: @navbar-inverse-brand-hover-color; + background-color: @navbar-inverse-brand-hover-bg; + } + } + + .navbar-text { + color: @navbar-inverse-color; + } + + .navbar-nav { + > li > a { + color: @navbar-inverse-link-color; + + &:hover, + &:focus { + color: @navbar-inverse-link-hover-color; + background-color: @navbar-inverse-link-hover-bg; + } + } + > .active > a { + &, + &:hover, + &:focus { + color: @navbar-inverse-link-active-color; + background-color: @navbar-inverse-link-active-bg; + } + } + > .disabled > a { + &, + &:hover, + &:focus { + color: @navbar-inverse-link-disabled-color; + background-color: @navbar-inverse-link-disabled-bg; + } + } + } + + // Darken the responsive nav toggle + .navbar-toggle { + border-color: @navbar-inverse-toggle-border-color; + &:hover, + &:focus { + background-color: @navbar-inverse-toggle-hover-bg; + } + .icon-bar { + background-color: @navbar-inverse-toggle-icon-bar-bg; + } + } + + .navbar-collapse, + .navbar-form { + border-color: darken(@navbar-inverse-bg, 7%); + } + + // Dropdowns + .navbar-nav { + > .open > a { + &, + &:hover, + &:focus { + background-color: @navbar-inverse-link-active-bg; + color: @navbar-inverse-link-active-color; + } + } + + @media (max-width: @grid-float-breakpoint-max) { + // Dropdowns get custom display + .open .dropdown-menu { + > .dropdown-header { + border-color: @navbar-inverse-border; + } + .divider { + background-color: @navbar-inverse-border; + } + > li > a { + color: @navbar-inverse-link-color; + &:hover, + &:focus { + color: @navbar-inverse-link-hover-color; + background-color: @navbar-inverse-link-hover-bg; + } + } + > .active > a { + &, + &:hover, + &:focus { + color: @navbar-inverse-link-active-color; + background-color: @navbar-inverse-link-active-bg; + } + } + > .disabled > a { + &, + &:hover, + &:focus { + color: @navbar-inverse-link-disabled-color; + background-color: @navbar-inverse-link-disabled-bg; + } + } + } + } + } + + .navbar-link { + color: @navbar-inverse-link-color; + &:hover { + color: @navbar-inverse-link-hover-color; + } + } + + .btn-link { + color: @navbar-inverse-link-color; + &:hover, + &:focus { + color: @navbar-inverse-link-hover-color; + } + &[disabled], + fieldset[disabled] & { + &:hover, + &:focus { + color: @navbar-inverse-link-disabled-color; + } + } + } +} diff --git a/src/fmt/doc/bootstrap/navs.less b/src/fmt/doc/bootstrap/navs.less new file mode 100644 index 000000000..a3d11b136 --- /dev/null +++ b/src/fmt/doc/bootstrap/navs.less @@ -0,0 +1,242 @@ +// +// Navs +// -------------------------------------------------- + + +// Base class +// -------------------------------------------------- + +.nav { + margin-bottom: 0; + padding-left: 0; // Override default ul/ol + list-style: none; + &:extend(.clearfix all); + + > li { + position: relative; + display: block; + + > a { + position: relative; + display: block; + padding: @nav-link-padding; + &:hover, + &:focus { + text-decoration: none; + background-color: @nav-link-hover-bg; + } + } + + // Disabled state sets text to gray and nukes hover/tab effects + &.disabled > a { + color: @nav-disabled-link-color; + + &:hover, + &:focus { + color: @nav-disabled-link-hover-color; + text-decoration: none; + background-color: transparent; + cursor: @cursor-disabled; + } + } + } + + // Open dropdowns + .open > a { + &, + &:hover, + &:focus { + background-color: @nav-link-hover-bg; + border-color: @link-color; + } + } + + // Nav dividers (deprecated with v3.0.1) + // + // This should have been removed in v3 with the dropping of `.nav-list`, but + // we missed it. We don't currently support this anywhere, but in the interest + // of maintaining backward compatibility in case you use it, it's deprecated. + .nav-divider { + .nav-divider(); + } + + // Prevent IE8 from misplacing imgs + // + // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989 + > li > a > img { + max-width: none; + } +} + + +// Tabs +// ------------------------- + +// Give the tabs something to sit on +.nav-tabs { + border-bottom: 1px solid @nav-tabs-border-color; + > li { + float: left; + // Make the list-items overlay the bottom border + margin-bottom: -1px; + + // Actual tabs (as links) + > a { + margin-right: 2px; + line-height: @line-height-base; + border: 1px solid transparent; + border-radius: @border-radius-base @border-radius-base 0 0; + &:hover { + border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color; + } + } + + // Active state, and its :hover to override normal :hover + &.active > a { + &, + &:hover, + &:focus { + color: @nav-tabs-active-link-hover-color; + background-color: @nav-tabs-active-link-hover-bg; + border: 1px solid @nav-tabs-active-link-hover-border-color; + border-bottom-color: transparent; + cursor: default; + } + } + } + // pulling this in mainly for less shorthand + &.nav-justified { + .nav-justified(); + .nav-tabs-justified(); + } +} + + +// Pills +// ------------------------- +.nav-pills { + > li { + float: left; + + // Links rendered as pills + > a { + border-radius: @nav-pills-border-radius; + } + + li { + margin-left: 2px; + } + + // Active state + &.active > a { + &, + &:hover, + &:focus { + color: @nav-pills-active-link-hover-color; + background-color: @nav-pills-active-link-hover-bg; + } + } + } +} + + +// Stacked pills +.nav-stacked { + > li { + float: none; + + li { + margin-top: 2px; + margin-left: 0; // no need for this gap between nav items + } + } +} + + +// Nav variations +// -------------------------------------------------- + +// Justified nav links +// ------------------------- + +.nav-justified { + width: 100%; + + > li { + float: none; + > a { + text-align: center; + margin-bottom: 5px; + } + } + + > .dropdown .dropdown-menu { + top: auto; + left: auto; + } + + @media (min-width: @screen-sm-min) { + > li { + display: table-cell; + width: 1%; + > a { + margin-bottom: 0; + } + } + } +} + +// Move borders to anchors instead of bottom of list +// +// Mixin for adding on top the shared `.nav-justified` styles for our tabs +.nav-tabs-justified { + border-bottom: 0; + + > li > a { + // Override margin from .nav-tabs + margin-right: 0; + border-radius: @border-radius-base; + } + + > .active > a, + > .active > a:hover, + > .active > a:focus { + border: 1px solid @nav-tabs-justified-link-border-color; + } + + @media (min-width: @screen-sm-min) { + > li > a { + border-bottom: 1px solid @nav-tabs-justified-link-border-color; + border-radius: @border-radius-base @border-radius-base 0 0; + } + > .active > a, + > .active > a:hover, + > .active > a:focus { + border-bottom-color: @nav-tabs-justified-active-link-border-color; + } + } +} + + +// Tabbable tabs +// ------------------------- + +// Hide tabbable panes to start, show them when `.active` +.tab-content { + > .tab-pane { + display: none; + } + > .active { + display: block; + } +} + + +// Dropdowns +// ------------------------- + +// Specific dropdowns +.nav-tabs .dropdown-menu { + // make dropdown border overlap tab border + margin-top: -1px; + // Remove the top rounded corners here since there is a hard edge above the menu + .border-top-radius(0); +} diff --git a/src/fmt/doc/bootstrap/normalize.less b/src/fmt/doc/bootstrap/normalize.less new file mode 100644 index 000000000..62a085a48 --- /dev/null +++ b/src/fmt/doc/bootstrap/normalize.less @@ -0,0 +1,427 @@ +/*! normalize.css v3.0.2 | MIT License | git.io/normalize */ + +// +// 1. Set default font family to sans-serif. +// 2. Prevent iOS text size adjust after orientation change, without disabling +// user zoom. +// + +html { + font-family: sans-serif; // 1 + -ms-text-size-adjust: 100%; // 2 + -webkit-text-size-adjust: 100%; // 2 +} + +// +// Remove default margin. +// + +body { + margin: 0; +} + +// HTML5 display definitions +// ========================================================================== + +// +// Correct `block` display not defined for any HTML5 element in IE 8/9. +// Correct `block` display not defined for `details` or `summary` in IE 10/11 +// and Firefox. +// Correct `block` display not defined for `main` in IE 11. +// + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} + +// +// 1. Correct `inline-block` display not defined in IE 8/9. +// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. +// + +audio, +canvas, +progress, +video { + display: inline-block; // 1 + vertical-align: baseline; // 2 +} + +// +// Prevent modern browsers from displaying `audio` without controls. +// Remove excess height in iOS 5 devices. +// + +audio:not([controls]) { + display: none; + height: 0; +} + +// +// Address `[hidden]` styling not present in IE 8/9/10. +// Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. +// + +[hidden], +template { + display: none; +} + +// Links +// ========================================================================== + +// +// Remove the gray background color from active links in IE 10. +// + +a { + background-color: transparent; +} + +// +// Improve readability when focused and also mouse hovered in all browsers. +// + +a:active, +a:hover { + outline: 0; +} + +// Text-level semantics +// ========================================================================== + +// +// Address styling not present in IE 8/9/10/11, Safari, and Chrome. +// + +abbr[title] { + border-bottom: 1px dotted; +} + +// +// Address style set to `bolder` in Firefox 4+, Safari, and Chrome. +// + +b, +strong { + font-weight: bold; +} + +// +// Address styling not present in Safari and Chrome. +// + +dfn { + font-style: italic; +} + +// +// Address variable `h1` font-size and margin within `section` and `article` +// contexts in Firefox 4+, Safari, and Chrome. +// + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +// +// Address styling not present in IE 8/9. +// + +mark { + background: #ff0; + color: #000; +} + +// +// Address inconsistent and variable font size in all browsers. +// + +small { + font-size: 80%; +} + +// +// Prevent `sub` and `sup` affecting `line-height` in all browsers. +// + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +// Embedded content +// ========================================================================== + +// +// Remove border when inside `a` element in IE 8/9/10. +// + +img { + border: 0; +} + +// +// Correct overflow not hidden in IE 9/10/11. +// + +svg:not(:root) { + overflow: hidden; +} + +// Grouping content +// ========================================================================== + +// +// Address margin not present in IE 8/9 and Safari. +// + +figure { + margin: 1em 40px; +} + +// +// Address differences between Firefox and other browsers. +// + +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} + +// +// Contain overflow in all browsers. +// + +pre { + overflow: auto; +} + +// +// Address odd `em`-unit font size rendering in all browsers. +// + +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +// Forms +// ========================================================================== + +// +// Known limitation: by default, Chrome and Safari on OS X allow very limited +// styling of `select`, unless a `border` property is set. +// + +// +// 1. Correct color not being inherited. +// Known issue: affects color of disabled elements. +// 2. Correct font properties not being inherited. +// 3. Address margins set differently in Firefox 4+, Safari, and Chrome. +// + +button, +input, +optgroup, +select, +textarea { + color: inherit; // 1 + font: inherit; // 2 + margin: 0; // 3 +} + +// +// Address `overflow` set to `hidden` in IE 8/9/10/11. +// + +button { + overflow: visible; +} + +// +// Address inconsistent `text-transform` inheritance for `button` and `select`. +// All other form control elements do not inherit `text-transform` values. +// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. +// Correct `select` style inheritance in Firefox. +// + +button, +select { + text-transform: none; +} + +// +// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` +// and `video` controls. +// 2. Correct inability to style clickable `input` types in iOS. +// 3. Improve usability and consistency of cursor style between image-type +// `input` and others. +// + +button, +html input[type="button"], // 1 +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; // 2 + cursor: pointer; // 3 +} + +// +// Re-set default cursor for disabled elements. +// + +button[disabled], +html input[disabled] { + cursor: default; +} + +// +// Remove inner padding and border in Firefox 4+. +// + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +// +// Address Firefox 4+ setting `line-height` on `input` using `!important` in +// the UA stylesheet. +// + +input { + line-height: normal; +} + +// +// It's recommended that you don't attempt to style these elements. +// Firefox's implementation doesn't respect box-sizing, padding, or width. +// +// 1. Address box sizing set to `content-box` in IE 8/9/10. +// 2. Remove excess padding in IE 8/9/10. +// + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; // 1 + padding: 0; // 2 +} + +// +// Fix the cursor style for Chrome's increment/decrement buttons. For certain +// `font-size` values of the `input`, it causes the cursor style of the +// decrement button to change from `default` to `text`. +// + +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +// +// 1. Address `appearance` set to `searchfield` in Safari and Chrome. +// 2. Address `box-sizing` set to `border-box` in Safari and Chrome +// (include `-moz` to future-proof). +// + +input[type="search"] { + -webkit-appearance: textfield; // 1 + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; // 2 + box-sizing: content-box; +} + +// +// Remove inner padding and search cancel button in Safari and Chrome on OS X. +// Safari (but not Chrome) clips the cancel button when the search input has +// padding (and `textfield` appearance). +// + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +// +// Define consistent border, margin, and padding. +// + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +// +// 1. Correct `color` not being inherited in IE 8/9/10/11. +// 2. Remove padding so people aren't caught out if they zero out fieldsets. +// + +legend { + border: 0; // 1 + padding: 0; // 2 +} + +// +// Remove default vertical scrollbar in IE 8/9/10/11. +// + +textarea { + overflow: auto; +} + +// +// Don't inherit the `font-weight` (applied by a rule above). +// NOTE: the default cannot safely be changed in Chrome and Safari on OS X. +// + +optgroup { + font-weight: bold; +} + +// Tables +// ========================================================================== + +// +// Remove most spacing between table cells. +// + +table { + border-collapse: collapse; + border-spacing: 0; +} + +td, +th { + padding: 0; +} diff --git a/src/fmt/doc/bootstrap/pager.less b/src/fmt/doc/bootstrap/pager.less new file mode 100644 index 000000000..41abaaadc --- /dev/null +++ b/src/fmt/doc/bootstrap/pager.less @@ -0,0 +1,54 @@ +// +// Pager pagination +// -------------------------------------------------- + + +.pager { + padding-left: 0; + margin: @line-height-computed 0; + list-style: none; + text-align: center; + &:extend(.clearfix all); + li { + display: inline; + > a, + > span { + display: inline-block; + padding: 5px 14px; + background-color: @pager-bg; + border: 1px solid @pager-border; + border-radius: @pager-border-radius; + } + + > a:hover, + > a:focus { + text-decoration: none; + background-color: @pager-hover-bg; + } + } + + .next { + > a, + > span { + float: right; + } + } + + .previous { + > a, + > span { + float: left; + } + } + + .disabled { + > a, + > a:hover, + > a:focus, + > span { + color: @pager-disabled-color; + background-color: @pager-bg; + cursor: @cursor-disabled; + } + } +} diff --git a/src/fmt/doc/bootstrap/pagination.less b/src/fmt/doc/bootstrap/pagination.less new file mode 100644 index 000000000..38c4c3d34 --- /dev/null +++ b/src/fmt/doc/bootstrap/pagination.less @@ -0,0 +1,88 @@ +// +// Pagination (multiple pages) +// -------------------------------------------------- +.pagination { + display: inline-block; + padding-left: 0; + margin: @line-height-computed 0; + border-radius: @border-radius-base; + + > li { + display: inline; // Remove list-style and block-level defaults + > a, + > span { + position: relative; + float: left; // Collapse white-space + padding: @padding-base-vertical @padding-base-horizontal; + line-height: @line-height-base; + text-decoration: none; + color: @pagination-color; + background-color: @pagination-bg; + border: 1px solid @pagination-border; + margin-left: -1px; + } + &:first-child { + > a, + > span { + margin-left: 0; + .border-left-radius(@border-radius-base); + } + } + &:last-child { + > a, + > span { + .border-right-radius(@border-radius-base); + } + } + } + + > li > a, + > li > span { + &:hover, + &:focus { + color: @pagination-hover-color; + background-color: @pagination-hover-bg; + border-color: @pagination-hover-border; + } + } + + > .active > a, + > .active > span { + &, + &:hover, + &:focus { + z-index: 2; + color: @pagination-active-color; + background-color: @pagination-active-bg; + border-color: @pagination-active-border; + cursor: default; + } + } + + > .disabled { + > span, + > span:hover, + > span:focus, + > a, + > a:hover, + > a:focus { + color: @pagination-disabled-color; + background-color: @pagination-disabled-bg; + border-color: @pagination-disabled-border; + cursor: @cursor-disabled; + } + } +} + +// Sizing +// -------------------------------------------------- + +// Large +.pagination-lg { + .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @border-radius-large); +} + +// Small +.pagination-sm { + .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @border-radius-small); +} diff --git a/src/fmt/doc/bootstrap/panels.less b/src/fmt/doc/bootstrap/panels.less new file mode 100644 index 000000000..04eb2b39c --- /dev/null +++ b/src/fmt/doc/bootstrap/panels.less @@ -0,0 +1,265 @@ +// +// Panels +// -------------------------------------------------- + + +// Base class +.panel { + margin-bottom: @line-height-computed; + background-color: @panel-bg; + border: 1px solid transparent; + border-radius: @panel-border-radius; + .box-shadow(0 1px 1px rgba(0,0,0,.05)); +} + +// Panel contents +.panel-body { + padding: @panel-body-padding; + &:extend(.clearfix all); +} + +// Optional heading +.panel-heading { + padding: @panel-heading-padding; + border-bottom: 1px solid transparent; + .border-top-radius((@panel-border-radius - 1)); + + > .dropdown .dropdown-toggle { + color: inherit; + } +} + +// Within heading, strip any `h*` tag of its default margins for spacing. +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: ceil((@font-size-base * 1.125)); + color: inherit; + + > a, + > small, + > .small, + > small > a, + > .small > a { + color: inherit; + } +} + +// Optional footer (stays gray in every modifier class) +.panel-footer { + padding: @panel-footer-padding; + background-color: @panel-footer-bg; + border-top: 1px solid @panel-inner-border; + .border-bottom-radius((@panel-border-radius - 1)); +} + + +// List groups in panels +// +// By default, space out list group content from panel headings to account for +// any kind of custom content between the two. + +.panel { + > .list-group, + > .panel-collapse > .list-group { + margin-bottom: 0; + + .list-group-item { + border-width: 1px 0; + border-radius: 0; + } + + // Add border top radius for first one + &:first-child { + .list-group-item:first-child { + border-top: 0; + .border-top-radius((@panel-border-radius - 1)); + } + } + // Add border bottom radius for last one + &:last-child { + .list-group-item:last-child { + border-bottom: 0; + .border-bottom-radius((@panel-border-radius - 1)); + } + } + } +} +// Collapse space between when there's no additional content. +.panel-heading + .list-group { + .list-group-item:first-child { + border-top-width: 0; + } +} +.list-group + .panel-footer { + border-top-width: 0; +} + +// Tables in panels +// +// Place a non-bordered `.table` within a panel (not within a `.panel-body`) and +// watch it go full width. + +.panel { + > .table, + > .table-responsive > .table, + > .panel-collapse > .table { + margin-bottom: 0; + + caption { + padding-left: @panel-body-padding; + padding-right: @panel-body-padding; + } + } + // Add border top radius for first one + > .table:first-child, + > .table-responsive:first-child > .table:first-child { + .border-top-radius((@panel-border-radius - 1)); + + > thead:first-child, + > tbody:first-child { + > tr:first-child { + border-top-left-radius: (@panel-border-radius - 1); + border-top-right-radius: (@panel-border-radius - 1); + + td:first-child, + th:first-child { + border-top-left-radius: (@panel-border-radius - 1); + } + td:last-child, + th:last-child { + border-top-right-radius: (@panel-border-radius - 1); + } + } + } + } + // Add border bottom radius for last one + > .table:last-child, + > .table-responsive:last-child > .table:last-child { + .border-bottom-radius((@panel-border-radius - 1)); + + > tbody:last-child, + > tfoot:last-child { + > tr:last-child { + border-bottom-left-radius: (@panel-border-radius - 1); + border-bottom-right-radius: (@panel-border-radius - 1); + + td:first-child, + th:first-child { + border-bottom-left-radius: (@panel-border-radius - 1); + } + td:last-child, + th:last-child { + border-bottom-right-radius: (@panel-border-radius - 1); + } + } + } + } + > .panel-body + .table, + > .panel-body + .table-responsive, + > .table + .panel-body, + > .table-responsive + .panel-body { + border-top: 1px solid @table-border-color; + } + > .table > tbody:first-child > tr:first-child th, + > .table > tbody:first-child > tr:first-child td { + border-top: 0; + } + > .table-bordered, + > .table-responsive > .table-bordered { + border: 0; + > thead, + > tbody, + > tfoot { + > tr { + > th:first-child, + > td:first-child { + border-left: 0; + } + > th:last-child, + > td:last-child { + border-right: 0; + } + } + } + > thead, + > tbody { + > tr:first-child { + > td, + > th { + border-bottom: 0; + } + } + } + > tbody, + > tfoot { + > tr:last-child { + > td, + > th { + border-bottom: 0; + } + } + } + } + > .table-responsive { + border: 0; + margin-bottom: 0; + } +} + + +// Collapsable panels (aka, accordion) +// +// Wrap a series of panels in `.panel-group` to turn them into an accordion with +// the help of our collapse JavaScript plugin. + +.panel-group { + margin-bottom: @line-height-computed; + + // Tighten up margin so it's only between panels + .panel { + margin-bottom: 0; + border-radius: @panel-border-radius; + + + .panel { + margin-top: 5px; + } + } + + .panel-heading { + border-bottom: 0; + + + .panel-collapse > .panel-body, + + .panel-collapse > .list-group { + border-top: 1px solid @panel-inner-border; + } + } + + .panel-footer { + border-top: 0; + + .panel-collapse .panel-body { + border-bottom: 1px solid @panel-inner-border; + } + } +} + + +// Contextual variations +.panel-default { + .panel-variant(@panel-default-border; @panel-default-text; @panel-default-heading-bg; @panel-default-border); +} +.panel-primary { + .panel-variant(@panel-primary-border; @panel-primary-text; @panel-primary-heading-bg; @panel-primary-border); +} +.panel-success { + .panel-variant(@panel-success-border; @panel-success-text; @panel-success-heading-bg; @panel-success-border); +} +.panel-info { + .panel-variant(@panel-info-border; @panel-info-text; @panel-info-heading-bg; @panel-info-border); +} +.panel-warning { + .panel-variant(@panel-warning-border; @panel-warning-text; @panel-warning-heading-bg; @panel-warning-border); +} +.panel-danger { + .panel-variant(@panel-danger-border; @panel-danger-text; @panel-danger-heading-bg; @panel-danger-border); +} diff --git a/src/fmt/doc/bootstrap/popovers.less b/src/fmt/doc/bootstrap/popovers.less new file mode 100644 index 000000000..2748eead1 --- /dev/null +++ b/src/fmt/doc/bootstrap/popovers.less @@ -0,0 +1,135 @@ +// +// Popovers +// -------------------------------------------------- + + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: @zindex-popover; + display: none; + max-width: @popover-max-width; + padding: 1px; + // Reset font and text properties given new insertion method + font-family: @font-family-base; + font-size: @font-size-base; + font-weight: normal; + line-height: @line-height-base; + text-align: left; + background-color: @popover-bg; + background-clip: padding-box; + border: 1px solid @popover-fallback-border-color; + border: 1px solid @popover-border-color; + border-radius: @border-radius-large; + .box-shadow(0 5px 10px rgba(0,0,0,.2)); + + // Overrides for proper insertion + white-space: normal; + + // Offset the popover to account for the popover arrow + &.top { margin-top: -@popover-arrow-width; } + &.right { margin-left: @popover-arrow-width; } + &.bottom { margin-top: @popover-arrow-width; } + &.left { margin-left: -@popover-arrow-width; } +} + +.popover-title { + margin: 0; // reset heading margin + padding: 8px 14px; + font-size: @font-size-base; + background-color: @popover-title-bg; + border-bottom: 1px solid darken(@popover-title-bg, 5%); + border-radius: (@border-radius-large - 1) (@border-radius-large - 1) 0 0; +} + +.popover-content { + padding: 9px 14px; +} + +// Arrows +// +// .arrow is outer, .arrow:after is inner + +.popover > .arrow { + &, + &:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + } +} +.popover > .arrow { + border-width: @popover-arrow-outer-width; +} +.popover > .arrow:after { + border-width: @popover-arrow-width; + content: ""; +} + +.popover { + &.top > .arrow { + left: 50%; + margin-left: -@popover-arrow-outer-width; + border-bottom-width: 0; + border-top-color: @popover-arrow-outer-fallback-color; // IE8 fallback + border-top-color: @popover-arrow-outer-color; + bottom: -@popover-arrow-outer-width; + &:after { + content: " "; + bottom: 1px; + margin-left: -@popover-arrow-width; + border-bottom-width: 0; + border-top-color: @popover-arrow-color; + } + } + &.right > .arrow { + top: 50%; + left: -@popover-arrow-outer-width; + margin-top: -@popover-arrow-outer-width; + border-left-width: 0; + border-right-color: @popover-arrow-outer-fallback-color; // IE8 fallback + border-right-color: @popover-arrow-outer-color; + &:after { + content: " "; + left: 1px; + bottom: -@popover-arrow-width; + border-left-width: 0; + border-right-color: @popover-arrow-color; + } + } + &.bottom > .arrow { + left: 50%; + margin-left: -@popover-arrow-outer-width; + border-top-width: 0; + border-bottom-color: @popover-arrow-outer-fallback-color; // IE8 fallback + border-bottom-color: @popover-arrow-outer-color; + top: -@popover-arrow-outer-width; + &:after { + content: " "; + top: 1px; + margin-left: -@popover-arrow-width; + border-top-width: 0; + border-bottom-color: @popover-arrow-color; + } + } + + &.left > .arrow { + top: 50%; + right: -@popover-arrow-outer-width; + margin-top: -@popover-arrow-outer-width; + border-right-width: 0; + border-left-color: @popover-arrow-outer-fallback-color; // IE8 fallback + border-left-color: @popover-arrow-outer-color; + &:after { + content: " "; + right: 1px; + border-right-width: 0; + border-left-color: @popover-arrow-color; + bottom: -@popover-arrow-width; + } + } +} diff --git a/src/fmt/doc/bootstrap/print.less b/src/fmt/doc/bootstrap/print.less new file mode 100644 index 000000000..94ca58f12 --- /dev/null +++ b/src/fmt/doc/bootstrap/print.less @@ -0,0 +1,107 @@ +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ + +// ========================================================================== +// Print styles. +// Inlined to avoid the additional HTTP request: h5bp.com/r +// ========================================================================== + +@media print { + *, + *:before, + *:after { + background: transparent !important; + color: #000 !important; // Black prints faster: h5bp.com/s + box-shadow: none !important; + text-shadow: none !important; + } + + a, + a:visited { + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + // Don't show links that are fragment identifiers, + // or use the `javascript:` pseudo protocol + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + + thead { + display: table-header-group; // h5bp.com/t + } + + tr, + img { + page-break-inside: avoid; + } + + img { + max-width: 100% !important; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } + + // Bootstrap specific changes start + // + // Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245 + // Once fixed, we can just straight up remove this. + select { + background: #fff !important; + } + + // Bootstrap components + .navbar { + display: none; + } + .btn, + .dropup > .btn { + > .caret { + border-top-color: #000 !important; + } + } + .label { + border: 1px solid #000; + } + + .table { + border-collapse: collapse !important; + + td, + th { + background-color: #fff !important; + } + } + .table-bordered { + th, + td { + border: 1px solid #ddd !important; + } + } + + // Bootstrap specific changes end +} diff --git a/src/fmt/doc/bootstrap/progress-bars.less b/src/fmt/doc/bootstrap/progress-bars.less new file mode 100644 index 000000000..8868a1fee --- /dev/null +++ b/src/fmt/doc/bootstrap/progress-bars.less @@ -0,0 +1,87 @@ +// +// Progress bars +// -------------------------------------------------- + + +// Bar animations +// ------------------------- + +// WebKit +@-webkit-keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + +// Spec and IE10+ +@keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + + +// Bar itself +// ------------------------- + +// Outer container +.progress { + overflow: hidden; + height: @line-height-computed; + margin-bottom: @line-height-computed; + background-color: @progress-bg; + border-radius: @progress-border-radius; + .box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); +} + +// Bar of progress +.progress-bar { + float: left; + width: 0%; + height: 100%; + font-size: @font-size-small; + line-height: @line-height-computed; + color: @progress-bar-color; + text-align: center; + background-color: @progress-bar-bg; + .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); + .transition(width .6s ease); +} + +// Striped bars +// +// `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the +// `.progress-bar-striped` class, which you just add to an existing +// `.progress-bar`. +.progress-striped .progress-bar, +.progress-bar-striped { + #gradient > .striped(); + background-size: 40px 40px; +} + +// Call animation for the active one +// +// `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the +// `.progress-bar.active` approach. +.progress.active .progress-bar, +.progress-bar.active { + .animation(progress-bar-stripes 2s linear infinite); +} + + +// Variations +// ------------------------- + +.progress-bar-success { + .progress-bar-variant(@progress-bar-success-bg); +} + +.progress-bar-info { + .progress-bar-variant(@progress-bar-info-bg); +} + +.progress-bar-warning { + .progress-bar-variant(@progress-bar-warning-bg); +} + +.progress-bar-danger { + .progress-bar-variant(@progress-bar-danger-bg); +} diff --git a/src/fmt/doc/bootstrap/responsive-embed.less b/src/fmt/doc/bootstrap/responsive-embed.less new file mode 100644 index 000000000..080a5118f --- /dev/null +++ b/src/fmt/doc/bootstrap/responsive-embed.less @@ -0,0 +1,35 @@ +// Embeds responsive +// +// Credit: Nicolas Gallagher and SUIT CSS. + +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; + + .embed-responsive-item, + iframe, + embed, + object, + video { + position: absolute; + top: 0; + left: 0; + bottom: 0; + height: 100%; + width: 100%; + border: 0; + } +} + +// Modifier class for 16:9 aspect ratio +.embed-responsive-16by9 { + padding-bottom: 56.25%; +} + +// Modifier class for 4:3 aspect ratio +.embed-responsive-4by3 { + padding-bottom: 75%; +} diff --git a/src/fmt/doc/bootstrap/responsive-utilities.less b/src/fmt/doc/bootstrap/responsive-utilities.less new file mode 100644 index 000000000..b1db31d7b --- /dev/null +++ b/src/fmt/doc/bootstrap/responsive-utilities.less @@ -0,0 +1,194 @@ +// +// Responsive: Utility classes +// -------------------------------------------------- + + +// IE10 in Windows (Phone) 8 +// +// Support for responsive views via media queries is kind of borked in IE10, for +// Surface/desktop in split view and for Windows Phone 8. This particular fix +// must be accompanied by a snippet of JavaScript to sniff the user agent and +// apply some conditional CSS to *only* the Surface/desktop Windows 8. Look at +// our Getting Started page for more information on this bug. +// +// For more information, see the following: +// +// Issue: https://github.com/twbs/bootstrap/issues/10497 +// Docs: http://getbootstrap.com/getting-started/#support-ie10-width +// Source: http://timkadlec.com/2013/01/windows-phone-8-and-device-width/ +// Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/ + +@-ms-viewport { + width: device-width; +} + + +// Visibility utilities +// Note: Deprecated .visible-xs, .visible-sm, .visible-md, and .visible-lg as of v3.2.0 +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + .responsive-invisibility(); +} + +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} + +.visible-xs { + @media (max-width: @screen-xs-max) { + .responsive-visibility(); + } +} +.visible-xs-block { + @media (max-width: @screen-xs-max) { + display: block !important; + } +} +.visible-xs-inline { + @media (max-width: @screen-xs-max) { + display: inline !important; + } +} +.visible-xs-inline-block { + @media (max-width: @screen-xs-max) { + display: inline-block !important; + } +} + +.visible-sm { + @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { + .responsive-visibility(); + } +} +.visible-sm-block { + @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { + display: block !important; + } +} +.visible-sm-inline { + @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { + display: inline !important; + } +} +.visible-sm-inline-block { + @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { + display: inline-block !important; + } +} + +.visible-md { + @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { + .responsive-visibility(); + } +} +.visible-md-block { + @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { + display: block !important; + } +} +.visible-md-inline { + @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { + display: inline !important; + } +} +.visible-md-inline-block { + @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { + display: inline-block !important; + } +} + +.visible-lg { + @media (min-width: @screen-lg-min) { + .responsive-visibility(); + } +} +.visible-lg-block { + @media (min-width: @screen-lg-min) { + display: block !important; + } +} +.visible-lg-inline { + @media (min-width: @screen-lg-min) { + display: inline !important; + } +} +.visible-lg-inline-block { + @media (min-width: @screen-lg-min) { + display: inline-block !important; + } +} + +.hidden-xs { + @media (max-width: @screen-xs-max) { + .responsive-invisibility(); + } +} +.hidden-sm { + @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { + .responsive-invisibility(); + } +} +.hidden-md { + @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { + .responsive-invisibility(); + } +} +.hidden-lg { + @media (min-width: @screen-lg-min) { + .responsive-invisibility(); + } +} + + +// Print utilities +// +// Media queries are placed on the inside to be mixin-friendly. + +// Note: Deprecated .visible-print as of v3.2.0 +.visible-print { + .responsive-invisibility(); + + @media print { + .responsive-visibility(); + } +} +.visible-print-block { + display: none !important; + + @media print { + display: block !important; + } +} +.visible-print-inline { + display: none !important; + + @media print { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; + + @media print { + display: inline-block !important; + } +} + +.hidden-print { + @media print { + .responsive-invisibility(); + } +} diff --git a/src/fmt/doc/bootstrap/scaffolding.less b/src/fmt/doc/bootstrap/scaffolding.less new file mode 100644 index 000000000..47b1d5a1a --- /dev/null +++ b/src/fmt/doc/bootstrap/scaffolding.less @@ -0,0 +1,162 @@ +// +// Scaffolding +// -------------------------------------------------- + + +// Reset the box-sizing +// +// Heads up! This reset may cause conflicts with some third-party widgets. +// For recommendations on resolving such conflicts, see +// http://getbootstrap.com/getting-started/#third-box-sizing +* { + .box-sizing(border-box); +} +*:before, +*:after { + .box-sizing(border-box); +} + + +// Body reset + +html { + font-size: 10px; + -webkit-tap-highlight-color: rgba(0,0,0,0); +} + +body { + font-family: @font-family-base; + font-size: @font-size-base; + line-height: @line-height-base; + color: @text-color; + background-color: @body-bg; +} + +// Reset fonts for relevant elements +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + + +// Links + +a { + color: @link-color; + text-decoration: none; + + &:hover, + &:focus { + color: @link-hover-color; + text-decoration: @link-hover-decoration; + } + + &:focus { + .tab-focus(); + } +} + + +// Figures +// +// We reset this here because previously Normalize had no `figure` margins. This +// ensures we don't break anyone's use of the element. + +figure { + margin: 0; +} + + +// Images + +img { + vertical-align: middle; +} + +// Responsive images (ensure images don't scale beyond their parents) +.img-responsive { + .img-responsive(); +} + +// Rounded corners +.img-rounded { + border-radius: @border-radius-large; +} + +// Image thumbnails +// +// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`. +.img-thumbnail { + padding: @thumbnail-padding; + line-height: @line-height-base; + background-color: @thumbnail-bg; + border: 1px solid @thumbnail-border; + border-radius: @thumbnail-border-radius; + .transition(all .2s ease-in-out); + + // Keep them at most 100% wide + .img-responsive(inline-block); +} + +// Perfect circle +.img-circle { + border-radius: 50%; // set radius in percents +} + + +// Horizontal rules + +hr { + margin-top: @line-height-computed; + margin-bottom: @line-height-computed; + border: 0; + border-top: 1px solid @hr-border; +} + + +// Only display content to screen readers +// +// See: http://a11yproject.com/posts/how-to-hide-content/ + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0,0,0,0); + border: 0; +} + +// Use in conjunction with .sr-only to only display content when it's focused. +// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 +// Credit: HTML5 Boilerplate + +.sr-only-focusable { + &:active, + &:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; + } +} + + +// iOS "clickable elements" fix for role="button" +// +// Fixes "clickability" issue (and more generally, the firing of events such as focus as well) +// for traditionally non-focusable elements with role="button" +// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile +// Upstream patch for normalize.css submitted: https://github.com/necolas/normalize.css/pull/379 - remove this fix once that is merged + +[role="button"] { + cursor: pointer; +}
\ No newline at end of file diff --git a/src/fmt/doc/bootstrap/tables.less b/src/fmt/doc/bootstrap/tables.less new file mode 100644 index 000000000..2242c0368 --- /dev/null +++ b/src/fmt/doc/bootstrap/tables.less @@ -0,0 +1,234 @@ +// +// Tables +// -------------------------------------------------- + + +table { + background-color: @table-bg; +} +caption { + padding-top: @table-cell-padding; + padding-bottom: @table-cell-padding; + color: @text-muted; + text-align: left; +} +th { + text-align: left; +} + + +// Baseline styles + +.table { + width: 100%; + max-width: 100%; + margin-bottom: @line-height-computed; + // Cells + > thead, + > tbody, + > tfoot { + > tr { + > th, + > td { + padding: @table-cell-padding; + line-height: @line-height-base; + vertical-align: top; + border-top: 1px solid @table-border-color; + } + } + } + // Bottom align for column headings + > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid @table-border-color; + } + // Remove top border from thead by default + > caption + thead, + > colgroup + thead, + > thead:first-child { + > tr:first-child { + > th, + > td { + border-top: 0; + } + } + } + // Account for multiple tbody instances + > tbody + tbody { + border-top: 2px solid @table-border-color; + } + + // Nesting + .table { + background-color: @body-bg; + } +} + + +// Condensed table w/ half padding + +.table-condensed { + > thead, + > tbody, + > tfoot { + > tr { + > th, + > td { + padding: @table-condensed-cell-padding; + } + } + } +} + + +// Bordered version +// +// Add borders all around the table and between all the columns. + +.table-bordered { + border: 1px solid @table-border-color; + > thead, + > tbody, + > tfoot { + > tr { + > th, + > td { + border: 1px solid @table-border-color; + } + } + } + > thead > tr { + > th, + > td { + border-bottom-width: 2px; + } + } +} + + +// Zebra-striping +// +// Default zebra-stripe styles (alternating gray and transparent backgrounds) + +.table-striped { + > tbody > tr:nth-of-type(odd) { + background-color: @table-bg-accent; + } +} + + +// Hover effect +// +// Placed here since it has to come after the potential zebra striping + +.table-hover { + > tbody > tr:hover { + background-color: @table-bg-hover; + } +} + + +// Table cell sizing +// +// Reset default table behavior + +table col[class*="col-"] { + position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623) + float: none; + display: table-column; +} +table { + td, + th { + &[class*="col-"] { + position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623) + float: none; + display: table-cell; + } + } +} + + +// Table backgrounds +// +// Exact selectors below required to override `.table-striped` and prevent +// inheritance to nested tables. + +// Generate the contextual variants +.table-row-variant(active; @table-bg-active); +.table-row-variant(success; @state-success-bg); +.table-row-variant(info; @state-info-bg); +.table-row-variant(warning; @state-warning-bg); +.table-row-variant(danger; @state-danger-bg); + + +// Responsive tables +// +// Wrap your tables in `.table-responsive` and we'll make them mobile friendly +// by enabling horizontal scrolling. Only applies <768px. Everything above that +// will display normally. + +.table-responsive { + overflow-x: auto; + min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837) + + @media screen and (max-width: @screen-xs-max) { + width: 100%; + margin-bottom: (@line-height-computed * 0.75); + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid @table-border-color; + + // Tighten up spacing + > .table { + margin-bottom: 0; + + // Ensure the content doesn't wrap + > thead, + > tbody, + > tfoot { + > tr { + > th, + > td { + white-space: nowrap; + } + } + } + } + + // Special overrides for the bordered tables + > .table-bordered { + border: 0; + + // Nuke the appropriate borders so that the parent can handle them + > thead, + > tbody, + > tfoot { + > tr { + > th:first-child, + > td:first-child { + border-left: 0; + } + > th:last-child, + > td:last-child { + border-right: 0; + } + } + } + + // Only nuke the last row's bottom-border in `tbody` and `tfoot` since + // chances are there will be only one `tr` in a `thead` and that would + // remove the border altogether. + > tbody, + > tfoot { + > tr:last-child { + > th, + > td { + border-bottom: 0; + } + } + } + + } + } +} diff --git a/src/fmt/doc/bootstrap/theme.less b/src/fmt/doc/bootstrap/theme.less new file mode 100644 index 000000000..6f2eb6212 --- /dev/null +++ b/src/fmt/doc/bootstrap/theme.less @@ -0,0 +1,273 @@ + +// +// Load core variables and mixins +// -------------------------------------------------- + +@import "variables.less"; +@import "mixins.less"; + + +// +// Buttons +// -------------------------------------------------- + +// Common styles +.btn-default, +.btn-primary, +.btn-success, +.btn-info, +.btn-warning, +.btn-danger { + text-shadow: 0 -1px 0 rgba(0,0,0,.2); + @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075); + .box-shadow(@shadow); + + // Reset the shadow + &:active, + &.active { + .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); + } + + .badge { + text-shadow: none; + } +} + +// Mixin for generating new styles +.btn-styles(@btn-color: #555) { + #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%)); + .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620 + background-repeat: repeat-x; + border-color: darken(@btn-color, 14%); + + &:hover, + &:focus { + background-color: darken(@btn-color, 12%); + background-position: 0 -15px; + } + + &:active, + &.active { + background-color: darken(@btn-color, 12%); + border-color: darken(@btn-color, 14%); + } + + &.disabled, + &:disabled, + &[disabled] { + background-color: darken(@btn-color, 12%); + background-image: none; + } +} + +// Common styles +.btn { + // Remove the gradient for the pressed/active state + &:active, + &.active { + background-image: none; + } +} + +// Apply the mixin to the buttons +.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; } +.btn-primary { .btn-styles(@btn-primary-bg); } +.btn-success { .btn-styles(@btn-success-bg); } +.btn-info { .btn-styles(@btn-info-bg); } +.btn-warning { .btn-styles(@btn-warning-bg); } +.btn-danger { .btn-styles(@btn-danger-bg); } + + +// +// Images +// -------------------------------------------------- + +.thumbnail, +.img-thumbnail { + .box-shadow(0 1px 2px rgba(0,0,0,.075)); +} + + +// +// Dropdowns +// -------------------------------------------------- + +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%)); + background-color: darken(@dropdown-link-hover-bg, 5%); +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%)); + background-color: darken(@dropdown-link-active-bg, 5%); +} + + +// +// Navbar +// -------------------------------------------------- + +// Default navbar +.navbar-default { + #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg); + .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered + border-radius: @navbar-border-radius; + @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075); + .box-shadow(@shadow); + + .navbar-nav > .open > a, + .navbar-nav > .active > a { + #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%)); + .box-shadow(inset 0 3px 9px rgba(0,0,0,.075)); + } +} +.navbar-brand, +.navbar-nav > li > a { + text-shadow: 0 1px 0 rgba(255,255,255,.25); +} + +// Inverted navbar +.navbar-inverse { + #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg); + .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257 + + .navbar-nav > .open > a, + .navbar-nav > .active > a { + #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%)); + .box-shadow(inset 0 3px 9px rgba(0,0,0,.25)); + } + + .navbar-brand, + .navbar-nav > li > a { + text-shadow: 0 -1px 0 rgba(0,0,0,.25); + } +} + +// Undo rounded corners in static and fixed navbars +.navbar-static-top, +.navbar-fixed-top, +.navbar-fixed-bottom { + border-radius: 0; +} + +// Fix active state of dropdown items in collapsed mode +@media (max-width: @grid-float-breakpoint-max) { + .navbar .navbar-nav .open .dropdown-menu > .active > a { + &, + &:hover, + &:focus { + color: #fff; + #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%)); + } + } +} + + +// +// Alerts +// -------------------------------------------------- + +// Common styles +.alert { + text-shadow: 0 1px 0 rgba(255,255,255,.2); + @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05); + .box-shadow(@shadow); +} + +// Mixin for generating new styles +.alert-styles(@color) { + #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%)); + border-color: darken(@color, 15%); +} + +// Apply the mixin to the alerts +.alert-success { .alert-styles(@alert-success-bg); } +.alert-info { .alert-styles(@alert-info-bg); } +.alert-warning { .alert-styles(@alert-warning-bg); } +.alert-danger { .alert-styles(@alert-danger-bg); } + + +// +// Progress bars +// -------------------------------------------------- + +// Give the progress background some depth +.progress { + #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg) +} + +// Mixin for generating new styles +.progress-bar-styles(@color) { + #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%)); +} + +// Apply the mixin to the progress bars +.progress-bar { .progress-bar-styles(@progress-bar-bg); } +.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); } +.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); } +.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); } +.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); } + +// Reset the striped class because our mixins don't do multiple gradients and +// the above custom styles override the new `.progress-bar-striped` in v3.2.0. +.progress-bar-striped { + #gradient > .striped(); +} + + +// +// List groups +// -------------------------------------------------- + +.list-group { + border-radius: @border-radius-base; + .box-shadow(0 1px 2px rgba(0,0,0,.075)); +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%); + #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%)); + border-color: darken(@list-group-active-border, 7.5%); + + .badge { + text-shadow: none; + } +} + + +// +// Panels +// -------------------------------------------------- + +// Common styles +.panel { + .box-shadow(0 1px 2px rgba(0,0,0,.05)); +} + +// Mixin for generating new styles +.panel-heading-styles(@color) { + #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%)); +} + +// Apply the mixin to the panel headings only +.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); } +.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); } +.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); } +.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); } +.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); } +.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); } + + +// +// Wells +// -------------------------------------------------- + +.well { + #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg); + border-color: darken(@well-bg, 10%); + @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1); + .box-shadow(@shadow); +} diff --git a/src/fmt/doc/bootstrap/thumbnails.less b/src/fmt/doc/bootstrap/thumbnails.less new file mode 100644 index 000000000..0713e67d0 --- /dev/null +++ b/src/fmt/doc/bootstrap/thumbnails.less @@ -0,0 +1,36 @@ +// +// Thumbnails +// -------------------------------------------------- + + +// Mixin and adjust the regular image class +.thumbnail { + display: block; + padding: @thumbnail-padding; + margin-bottom: @line-height-computed; + line-height: @line-height-base; + background-color: @thumbnail-bg; + border: 1px solid @thumbnail-border; + border-radius: @thumbnail-border-radius; + .transition(border .2s ease-in-out); + + > img, + a > img { + &:extend(.img-responsive); + margin-left: auto; + margin-right: auto; + } + + // Add a hover state for linked versions only + a&:hover, + a&:focus, + a&.active { + border-color: @link-color; + } + + // Image captions + .caption { + padding: @thumbnail-caption-padding; + color: @thumbnail-caption-color; + } +} diff --git a/src/fmt/doc/bootstrap/tooltip.less b/src/fmt/doc/bootstrap/tooltip.less new file mode 100644 index 000000000..9d5684743 --- /dev/null +++ b/src/fmt/doc/bootstrap/tooltip.less @@ -0,0 +1,102 @@ +// +// Tooltips +// -------------------------------------------------- + + +// Base class +.tooltip { + position: absolute; + z-index: @zindex-tooltip; + display: block; + // Reset font and text properties given new insertion method + font-family: @font-family-base; + font-size: @font-size-small; + font-weight: normal; + line-height: 1.4; + .opacity(0); + + &.in { .opacity(@tooltip-opacity); } + &.top { margin-top: -3px; padding: @tooltip-arrow-width 0; } + &.right { margin-left: 3px; padding: 0 @tooltip-arrow-width; } + &.bottom { margin-top: 3px; padding: @tooltip-arrow-width 0; } + &.left { margin-left: -3px; padding: 0 @tooltip-arrow-width; } +} + +// Wrapper for the tooltip content +.tooltip-inner { + max-width: @tooltip-max-width; + padding: 3px 8px; + color: @tooltip-color; + text-align: center; + text-decoration: none; + background-color: @tooltip-bg; + border-radius: @border-radius-base; +} + +// Arrows +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +// Note: Deprecated .top-left, .top-right, .bottom-left, and .bottom-right as of v3.3.1 +.tooltip { + &.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -@tooltip-arrow-width; + border-width: @tooltip-arrow-width @tooltip-arrow-width 0; + border-top-color: @tooltip-arrow-color; + } + &.top-left .tooltip-arrow { + bottom: 0; + right: @tooltip-arrow-width; + margin-bottom: -@tooltip-arrow-width; + border-width: @tooltip-arrow-width @tooltip-arrow-width 0; + border-top-color: @tooltip-arrow-color; + } + &.top-right .tooltip-arrow { + bottom: 0; + left: @tooltip-arrow-width; + margin-bottom: -@tooltip-arrow-width; + border-width: @tooltip-arrow-width @tooltip-arrow-width 0; + border-top-color: @tooltip-arrow-color; + } + &.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -@tooltip-arrow-width; + border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0; + border-right-color: @tooltip-arrow-color; + } + &.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -@tooltip-arrow-width; + border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width; + border-left-color: @tooltip-arrow-color; + } + &.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -@tooltip-arrow-width; + border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; + border-bottom-color: @tooltip-arrow-color; + } + &.bottom-left .tooltip-arrow { + top: 0; + right: @tooltip-arrow-width; + margin-top: -@tooltip-arrow-width; + border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; + border-bottom-color: @tooltip-arrow-color; + } + &.bottom-right .tooltip-arrow { + top: 0; + left: @tooltip-arrow-width; + margin-top: -@tooltip-arrow-width; + border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; + border-bottom-color: @tooltip-arrow-color; + } +} diff --git a/src/fmt/doc/bootstrap/type.less b/src/fmt/doc/bootstrap/type.less new file mode 100644 index 000000000..68ba6017b --- /dev/null +++ b/src/fmt/doc/bootstrap/type.less @@ -0,0 +1,302 @@ +// +// Typography +// -------------------------------------------------- + + +// Headings +// ------------------------- + +h1, h2, h3, h4, h5, h6, +.h1, .h2, .h3, .h4, .h5, .h6 { + font-family: @headings-font-family; + font-weight: @headings-font-weight; + line-height: @headings-line-height; + color: @headings-color; + + small, + .small { + font-weight: normal; + line-height: 1; + color: @headings-small-color; + } +} + +h1, .h1, +h2, .h2, +h3, .h3 { + margin-top: @line-height-computed; + margin-bottom: (@line-height-computed / 2); + + small, + .small { + font-size: 65%; + } +} +h4, .h4, +h5, .h5, +h6, .h6 { + margin-top: (@line-height-computed / 2); + margin-bottom: (@line-height-computed / 2); + + small, + .small { + font-size: 75%; + } +} + +h1, .h1 { font-size: @font-size-h1; } +h2, .h2 { font-size: @font-size-h2; } +h3, .h3 { font-size: @font-size-h3; } +h4, .h4 { font-size: @font-size-h4; } +h5, .h5 { font-size: @font-size-h5; } +h6, .h6 { font-size: @font-size-h6; } + + +// Body text +// ------------------------- + +p { + margin: 0 0 (@line-height-computed / 2); +} + +.lead { + margin-bottom: @line-height-computed; + font-size: floor((@font-size-base * 1.15)); + font-weight: 300; + line-height: 1.4; + + @media (min-width: @screen-sm-min) { + font-size: (@font-size-base * 1.5); + } +} + + +// Emphasis & misc +// ------------------------- + +// Ex: (12px small font / 14px base font) * 100% = about 85% +small, +.small { + font-size: floor((100% * @font-size-small / @font-size-base)); +} + +mark, +.mark { + background-color: @state-warning-bg; + padding: .2em; +} + +// Alignment +.text-left { text-align: left; } +.text-right { text-align: right; } +.text-center { text-align: center; } +.text-justify { text-align: justify; } +.text-nowrap { white-space: nowrap; } + +// Transformation +.text-lowercase { text-transform: lowercase; } +.text-uppercase { text-transform: uppercase; } +.text-capitalize { text-transform: capitalize; } + +// Contextual colors +.text-muted { + color: @text-muted; +} +.text-primary { + .text-emphasis-variant(@brand-primary); +} +.text-success { + .text-emphasis-variant(@state-success-text); +} +.text-info { + .text-emphasis-variant(@state-info-text); +} +.text-warning { + .text-emphasis-variant(@state-warning-text); +} +.text-danger { + .text-emphasis-variant(@state-danger-text); +} + +// Contextual backgrounds +// For now we'll leave these alongside the text classes until v4 when we can +// safely shift things around (per SemVer rules). +.bg-primary { + // Given the contrast here, this is the only class to have its color inverted + // automatically. + color: #fff; + .bg-variant(@brand-primary); +} +.bg-success { + .bg-variant(@state-success-bg); +} +.bg-info { + .bg-variant(@state-info-bg); +} +.bg-warning { + .bg-variant(@state-warning-bg); +} +.bg-danger { + .bg-variant(@state-danger-bg); +} + + +// Page header +// ------------------------- + +.page-header { + padding-bottom: ((@line-height-computed / 2) - 1); + margin: (@line-height-computed * 2) 0 @line-height-computed; + border-bottom: 1px solid @page-header-border-color; +} + + +// Lists +// ------------------------- + +// Unordered and Ordered lists +ul, +ol { + margin-top: 0; + margin-bottom: (@line-height-computed / 2); + ul, + ol { + margin-bottom: 0; + } +} + +// List options + +// Unstyled keeps list items block level, just removes default browser padding and list-style +.list-unstyled { + padding-left: 0; + list-style: none; +} + +// Inline turns list items into inline-block +.list-inline { + .list-unstyled(); + margin-left: -5px; + + > li { + display: inline-block; + padding-left: 5px; + padding-right: 5px; + } +} + +// Description Lists +dl { + margin-top: 0; // Remove browser default + margin-bottom: @line-height-computed; +} +dt, +dd { + line-height: @line-height-base; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; // Undo browser default +} + +// Horizontal description lists +// +// Defaults to being stacked without any of the below styles applied, until the +// grid breakpoint is reached (default of ~768px). + +.dl-horizontal { + dd { + &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present + } + + @media (min-width: @grid-float-breakpoint) { + dt { + float: left; + width: (@dl-horizontal-offset - 20); + clear: left; + text-align: right; + .text-overflow(); + } + dd { + margin-left: @dl-horizontal-offset; + } + } +} + + +// Misc +// ------------------------- + +// Abbreviations and acronyms +abbr[title], +// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257 +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted @abbr-border-color; +} +.initialism { + font-size: 90%; + .text-uppercase(); +} + +// Blockquotes +blockquote { + padding: (@line-height-computed / 2) @line-height-computed; + margin: 0 0 @line-height-computed; + font-size: @blockquote-font-size; + border-left: 5px solid @blockquote-border-color; + + p, + ul, + ol { + &:last-child { + margin-bottom: 0; + } + } + + // Note: Deprecated small and .small as of v3.1.0 + // Context: https://github.com/twbs/bootstrap/issues/11660 + footer, + small, + .small { + display: block; + font-size: 80%; // back to default font-size + line-height: @line-height-base; + color: @blockquote-small-color; + + &:before { + content: '\2014 \00A0'; // em dash, nbsp + } + } +} + +// Opposite alignment of blockquote +// +// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0. +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + border-right: 5px solid @blockquote-border-color; + border-left: 0; + text-align: right; + + // Account for citation + footer, + small, + .small { + &:before { content: ''; } + &:after { + content: '\00A0 \2014'; // nbsp, em dash + } + } +} + +// Addresses +address { + margin-bottom: @line-height-computed; + font-style: normal; + line-height: @line-height-base; +} diff --git a/src/fmt/doc/bootstrap/utilities.less b/src/fmt/doc/bootstrap/utilities.less new file mode 100644 index 000000000..7a8ca27a8 --- /dev/null +++ b/src/fmt/doc/bootstrap/utilities.less @@ -0,0 +1,55 @@ +// +// Utility classes +// -------------------------------------------------- + + +// Floats +// ------------------------- + +.clearfix { + .clearfix(); +} +.center-block { + .center-block(); +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} + + +// Toggling content +// ------------------------- + +// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1 +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + .text-hide(); +} + + +// Hide from screenreaders and browsers +// +// Credit: HTML5 Boilerplate + +.hidden { + display: none !important; +} + + +// For Affix plugin +// ------------------------- + +.affix { + position: fixed; +} diff --git a/src/fmt/doc/bootstrap/variables.less b/src/fmt/doc/bootstrap/variables.less new file mode 100644 index 000000000..b5fb9d02c --- /dev/null +++ b/src/fmt/doc/bootstrap/variables.less @@ -0,0 +1,861 @@ +// +// Variables +// -------------------------------------------------- + + +//== Colors +// +//## Gray and brand colors for use across Bootstrap. + +@gray-base: #000; +@gray-darker: lighten(@gray-base, 13.5%); // #222 +@gray-dark: lighten(@gray-base, 20%); // #333 +@gray: lighten(@gray-base, 33.5%); // #555 +@gray-light: lighten(@gray-base, 46.7%); // #777 +@gray-lighter: lighten(@gray-base, 93.5%); // #eee + +@brand-primary: darken(#428bca, 6.5%); // #337ab7 +@brand-success: #5cb85c; +@brand-info: #5bc0de; +@brand-warning: #f0ad4e; +@brand-danger: #d9534f; + + +//== Scaffolding +// +//## Settings for some of the most global styles. + +//** Background color for `<body>`. +@body-bg: #fff; +//** Global text color on `<body>`. +@text-color: @gray-dark; + +//** Global textual link color. +@link-color: @brand-primary; +//** Link hover color set via `darken()` function. +@link-hover-color: darken(@link-color, 15%); +//** Link hover decoration. +@link-hover-decoration: underline; + + +//== Typography +// +//## Font, line-height, and color for body text, headings, and more. + +@font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif; +@font-family-serif: Georgia, "Times New Roman", Times, serif; +//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`. +@font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace; +@font-family-base: @font-family-sans-serif; + +@font-size-base: 14px; +@font-size-large: ceil((@font-size-base * 1.25)); // ~18px +@font-size-small: ceil((@font-size-base * 0.85)); // ~12px + +@font-size-h1: floor((@font-size-base * 2.6)); // ~36px +@font-size-h2: floor((@font-size-base * 2.15)); // ~30px +@font-size-h3: ceil((@font-size-base * 1.7)); // ~24px +@font-size-h4: ceil((@font-size-base * 1.25)); // ~18px +@font-size-h5: @font-size-base; +@font-size-h6: ceil((@font-size-base * 0.85)); // ~12px + +//** Unit-less `line-height` for use in components like buttons. +@line-height-base: 1.428571429; // 20/14 +//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc. +@line-height-computed: floor((@font-size-base * @line-height-base)); // ~20px + +//** By default, this inherits from the `<body>`. +@headings-font-family: inherit; +@headings-font-weight: 500; +@headings-line-height: 1.1; +@headings-color: inherit; + + +//== Iconography +// +//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower. + +//** Load fonts from this directory. +@icon-font-path: "../fonts/"; +//** File name for all font files. +@icon-font-name: "glyphicons-halflings-regular"; +//** Element ID within SVG icon file. +@icon-font-svg-id: "glyphicons_halflingsregular"; + + +//== Components +// +//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start). + +@padding-base-vertical: 6px; +@padding-base-horizontal: 12px; + +@padding-large-vertical: 10px; +@padding-large-horizontal: 16px; + +@padding-small-vertical: 5px; +@padding-small-horizontal: 10px; + +@padding-xs-vertical: 1px; +@padding-xs-horizontal: 5px; + +@line-height-large: 1.3333333; // extra decimals for Win 8.1 Chrome +@line-height-small: 1.5; + +@border-radius-base: 4px; +@border-radius-large: 6px; +@border-radius-small: 3px; + +//** Global color for active items (e.g., navs or dropdowns). +@component-active-color: #fff; +//** Global background color for active items (e.g., navs or dropdowns). +@component-active-bg: @brand-primary; + +//** Width of the `border` for generating carets that indicator dropdowns. +@caret-width-base: 4px; +//** Carets increase slightly in size for larger components. +@caret-width-large: 5px; + + +//== Tables +// +//## Customizes the `.table` component with basic values, each used across all table variations. + +//** Padding for `<th>`s and `<td>`s. +@table-cell-padding: 8px; +//** Padding for cells in `.table-condensed`. +@table-condensed-cell-padding: 5px; + +//** Default background color used for all tables. +@table-bg: transparent; +//** Background color used for `.table-striped`. +@table-bg-accent: #f9f9f9; +//** Background color used for `.table-hover`. +@table-bg-hover: #f5f5f5; +@table-bg-active: @table-bg-hover; + +//** Border color for table and cell borders. +@table-border-color: #ddd; + + +//== Buttons +// +//## For each of Bootstrap's buttons, define text, background and border color. + +@btn-font-weight: normal; + +@btn-default-color: #333; +@btn-default-bg: #fff; +@btn-default-border: #ccc; + +@btn-primary-color: #fff; +@btn-primary-bg: @brand-primary; +@btn-primary-border: darken(@btn-primary-bg, 5%); + +@btn-success-color: #fff; +@btn-success-bg: @brand-success; +@btn-success-border: darken(@btn-success-bg, 5%); + +@btn-info-color: #fff; +@btn-info-bg: @brand-info; +@btn-info-border: darken(@btn-info-bg, 5%); + +@btn-warning-color: #fff; +@btn-warning-bg: @brand-warning; +@btn-warning-border: darken(@btn-warning-bg, 5%); + +@btn-danger-color: #fff; +@btn-danger-bg: @brand-danger; +@btn-danger-border: darken(@btn-danger-bg, 5%); + +@btn-link-disabled-color: @gray-light; + + +//== Forms +// +//## + +//** `<input>` background color +@input-bg: #fff; +//** `<input disabled>` background color +@input-bg-disabled: @gray-lighter; + +//** Text color for `<input>`s +@input-color: @gray; +//** `<input>` border color +@input-border: #ccc; + +// TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4 +//** Default `.form-control` border radius +// This has no effect on `<select>`s in some browsers, due to the limited stylability of `<select>`s in CSS. +@input-border-radius: @border-radius-base; +//** Large `.form-control` border radius +@input-border-radius-large: @border-radius-large; +//** Small `.form-control` border radius +@input-border-radius-small: @border-radius-small; + +//** Border color for inputs on focus +@input-border-focus: #66afe9; + +//** Placeholder text color +@input-color-placeholder: #999; + +//** Default `.form-control` height +@input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2); +//** Large `.form-control` height +@input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2); +//** Small `.form-control` height +@input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2); + +//** `.form-group` margin +@form-group-margin-bottom: 15px; + +@legend-color: @gray-dark; +@legend-border-color: #e5e5e5; + +//** Background color for textual input addons +@input-group-addon-bg: @gray-lighter; +//** Border color for textual input addons +@input-group-addon-border-color: @input-border; + +//** Disabled cursor for form controls and buttons. +@cursor-disabled: not-allowed; + + +//== Dropdowns +// +//## Dropdown menu container and contents. + +//** Background for the dropdown menu. +@dropdown-bg: #fff; +//** Dropdown menu `border-color`. +@dropdown-border: rgba(0,0,0,.15); +//** Dropdown menu `border-color` **for IE8**. +@dropdown-fallback-border: #ccc; +//** Divider color for between dropdown items. +@dropdown-divider-bg: #e5e5e5; + +//** Dropdown link text color. +@dropdown-link-color: @gray-dark; +//** Hover color for dropdown links. +@dropdown-link-hover-color: darken(@gray-dark, 5%); +//** Hover background for dropdown links. +@dropdown-link-hover-bg: #f5f5f5; + +//** Active dropdown menu item text color. +@dropdown-link-active-color: @component-active-color; +//** Active dropdown menu item background color. +@dropdown-link-active-bg: @component-active-bg; + +//** Disabled dropdown menu item background color. +@dropdown-link-disabled-color: @gray-light; + +//** Text color for headers within dropdown menus. +@dropdown-header-color: @gray-light; + +//** Deprecated `@dropdown-caret-color` as of v3.1.0 +@dropdown-caret-color: #000; + + +//-- Z-index master list +// +// Warning: Avoid customizing these values. They're used for a bird's eye view +// of components dependent on the z-axis and are designed to all work together. +// +// Note: These variables are not generated into the Customizer. + +@zindex-navbar: 1000; +@zindex-dropdown: 1000; +@zindex-popover: 1060; +@zindex-tooltip: 1070; +@zindex-navbar-fixed: 1030; +@zindex-modal-background: 1040; +@zindex-modal: 1050; + + +//== Media queries breakpoints +// +//## Define the breakpoints at which your layout will change, adapting to different screen sizes. + +// Extra small screen / phone +//** Deprecated `@screen-xs` as of v3.0.1 +@screen-xs: 480px; +//** Deprecated `@screen-xs-min` as of v3.2.0 +@screen-xs-min: @screen-xs; +//** Deprecated `@screen-phone` as of v3.0.1 +@screen-phone: @screen-xs-min; + +// Small screen / tablet +//** Deprecated `@screen-sm` as of v3.0.1 +@screen-sm: 768px; +@screen-sm-min: @screen-sm; +//** Deprecated `@screen-tablet` as of v3.0.1 +@screen-tablet: @screen-sm-min; + +// Medium screen / desktop +//** Deprecated `@screen-md` as of v3.0.1 +@screen-md: 992px; +@screen-md-min: @screen-md; +//** Deprecated `@screen-desktop` as of v3.0.1 +@screen-desktop: @screen-md-min; + +// Large screen / wide desktop +//** Deprecated `@screen-lg` as of v3.0.1 +@screen-lg: 1200px; +@screen-lg-min: @screen-lg; +//** Deprecated `@screen-lg-desktop` as of v3.0.1 +@screen-lg-desktop: @screen-lg-min; + +// So media queries don't overlap when required, provide a maximum +@screen-xs-max: (@screen-sm-min - 1); +@screen-sm-max: (@screen-md-min - 1); +@screen-md-max: (@screen-lg-min - 1); + + +//== Grid system +// +//## Define your custom responsive grid. + +//** Number of columns in the grid. +@grid-columns: 12; +//** Padding between columns. Gets divided in half for the left and right. +@grid-gutter-width: 30px; +// Navbar collapse +//** Point at which the navbar becomes uncollapsed. +@grid-float-breakpoint: @screen-sm-min; +//** Point at which the navbar begins collapsing. +@grid-float-breakpoint-max: (@grid-float-breakpoint - 1); + + +//== Container sizes +// +//## Define the maximum width of `.container` for different screen sizes. + +// Small screen / tablet +@container-tablet: (720px + @grid-gutter-width); +//** For `@screen-sm-min` and up. +@container-sm: @container-tablet; + +// Medium screen / desktop +@container-desktop: (940px + @grid-gutter-width); +//** For `@screen-md-min` and up. +@container-md: @container-desktop; + +// Large screen / wide desktop +@container-large-desktop: (1140px + @grid-gutter-width); +//** For `@screen-lg-min` and up. +@container-lg: @container-large-desktop; + + +//== Navbar +// +//## + +// Basics of a navbar +@navbar-height: 50px; +@navbar-margin-bottom: @line-height-computed; +@navbar-border-radius: @border-radius-base; +@navbar-padding-horizontal: floor((@grid-gutter-width / 2)); +@navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2); +@navbar-collapse-max-height: 340px; + +@navbar-default-color: #777; +@navbar-default-bg: #f8f8f8; +@navbar-default-border: darken(@navbar-default-bg, 6.5%); + +// Navbar links +@navbar-default-link-color: #777; +@navbar-default-link-hover-color: #333; +@navbar-default-link-hover-bg: transparent; +@navbar-default-link-active-color: #555; +@navbar-default-link-active-bg: darken(@navbar-default-bg, 6.5%); +@navbar-default-link-disabled-color: #ccc; +@navbar-default-link-disabled-bg: transparent; + +// Navbar brand label +@navbar-default-brand-color: @navbar-default-link-color; +@navbar-default-brand-hover-color: darken(@navbar-default-brand-color, 10%); +@navbar-default-brand-hover-bg: transparent; + +// Navbar toggle +@navbar-default-toggle-hover-bg: #ddd; +@navbar-default-toggle-icon-bar-bg: #888; +@navbar-default-toggle-border-color: #ddd; + + +// Inverted navbar +// Reset inverted navbar basics +@navbar-inverse-color: lighten(@gray-light, 15%); +@navbar-inverse-bg: #222; +@navbar-inverse-border: darken(@navbar-inverse-bg, 10%); + +// Inverted navbar links +@navbar-inverse-link-color: lighten(@gray-light, 15%); +@navbar-inverse-link-hover-color: #fff; +@navbar-inverse-link-hover-bg: transparent; +@navbar-inverse-link-active-color: @navbar-inverse-link-hover-color; +@navbar-inverse-link-active-bg: darken(@navbar-inverse-bg, 10%); +@navbar-inverse-link-disabled-color: #444; +@navbar-inverse-link-disabled-bg: transparent; + +// Inverted navbar brand label +@navbar-inverse-brand-color: @navbar-inverse-link-color; +@navbar-inverse-brand-hover-color: #fff; +@navbar-inverse-brand-hover-bg: transparent; + +// Inverted navbar toggle +@navbar-inverse-toggle-hover-bg: #333; +@navbar-inverse-toggle-icon-bar-bg: #fff; +@navbar-inverse-toggle-border-color: #333; + + +//== Navs +// +//## + +//=== Shared nav styles +@nav-link-padding: 10px 15px; +@nav-link-hover-bg: @gray-lighter; + +@nav-disabled-link-color: @gray-light; +@nav-disabled-link-hover-color: @gray-light; + +//== Tabs +@nav-tabs-border-color: #ddd; + +@nav-tabs-link-hover-border-color: @gray-lighter; + +@nav-tabs-active-link-hover-bg: @body-bg; +@nav-tabs-active-link-hover-color: @gray; +@nav-tabs-active-link-hover-border-color: #ddd; + +@nav-tabs-justified-link-border-color: #ddd; +@nav-tabs-justified-active-link-border-color: @body-bg; + +//== Pills +@nav-pills-border-radius: @border-radius-base; +@nav-pills-active-link-hover-bg: @component-active-bg; +@nav-pills-active-link-hover-color: @component-active-color; + + +//== Pagination +// +//## + +@pagination-color: @link-color; +@pagination-bg: #fff; +@pagination-border: #ddd; + +@pagination-hover-color: @link-hover-color; +@pagination-hover-bg: @gray-lighter; +@pagination-hover-border: #ddd; + +@pagination-active-color: #fff; +@pagination-active-bg: @brand-primary; +@pagination-active-border: @brand-primary; + +@pagination-disabled-color: @gray-light; +@pagination-disabled-bg: #fff; +@pagination-disabled-border: #ddd; + + +//== Pager +// +//## + +@pager-bg: @pagination-bg; +@pager-border: @pagination-border; +@pager-border-radius: 15px; + +@pager-hover-bg: @pagination-hover-bg; + +@pager-active-bg: @pagination-active-bg; +@pager-active-color: @pagination-active-color; + +@pager-disabled-color: @pagination-disabled-color; + + +//== Jumbotron +// +//## + +@jumbotron-padding: 30px; +@jumbotron-color: inherit; +@jumbotron-bg: @gray-lighter; +@jumbotron-heading-color: inherit; +@jumbotron-font-size: ceil((@font-size-base * 1.5)); + + +//== Form states and alerts +// +//## Define colors for form feedback states and, by default, alerts. + +@state-success-text: #3c763d; +@state-success-bg: #dff0d8; +@state-success-border: darken(spin(@state-success-bg, -10), 5%); + +@state-info-text: #31708f; +@state-info-bg: #d9edf7; +@state-info-border: darken(spin(@state-info-bg, -10), 7%); + +@state-warning-text: #8a6d3b; +@state-warning-bg: #fcf8e3; +@state-warning-border: darken(spin(@state-warning-bg, -10), 5%); + +@state-danger-text: #a94442; +@state-danger-bg: #f2dede; +@state-danger-border: darken(spin(@state-danger-bg, -10), 5%); + + +//== Tooltips +// +//## + +//** Tooltip max width +@tooltip-max-width: 200px; +//** Tooltip text color +@tooltip-color: #fff; +//** Tooltip background color +@tooltip-bg: #000; +@tooltip-opacity: .9; + +//** Tooltip arrow width +@tooltip-arrow-width: 5px; +//** Tooltip arrow color +@tooltip-arrow-color: @tooltip-bg; + + +//== Popovers +// +//## + +//** Popover body background color +@popover-bg: #fff; +//** Popover maximum width +@popover-max-width: 276px; +//** Popover border color +@popover-border-color: rgba(0,0,0,.2); +//** Popover fallback border color +@popover-fallback-border-color: #ccc; + +//** Popover title background color +@popover-title-bg: darken(@popover-bg, 3%); + +//** Popover arrow width +@popover-arrow-width: 10px; +//** Popover arrow color +@popover-arrow-color: @popover-bg; + +//** Popover outer arrow width +@popover-arrow-outer-width: (@popover-arrow-width + 1); +//** Popover outer arrow color +@popover-arrow-outer-color: fadein(@popover-border-color, 5%); +//** Popover outer arrow fallback color +@popover-arrow-outer-fallback-color: darken(@popover-fallback-border-color, 20%); + + +//== Labels +// +//## + +//** Default label background color +@label-default-bg: @gray-light; +//** Primary label background color +@label-primary-bg: @brand-primary; +//** Success label background color +@label-success-bg: @brand-success; +//** Info label background color +@label-info-bg: @brand-info; +//** Warning label background color +@label-warning-bg: @brand-warning; +//** Danger label background color +@label-danger-bg: @brand-danger; + +//** Default label text color +@label-color: #fff; +//** Default text color of a linked label +@label-link-hover-color: #fff; + + +//== Modals +// +//## + +//** Padding applied to the modal body +@modal-inner-padding: 15px; + +//** Padding applied to the modal title +@modal-title-padding: 15px; +//** Modal title line-height +@modal-title-line-height: @line-height-base; + +//** Background color of modal content area +@modal-content-bg: #fff; +//** Modal content border color +@modal-content-border-color: rgba(0,0,0,.2); +//** Modal content border color **for IE8** +@modal-content-fallback-border-color: #999; + +//** Modal backdrop background color +@modal-backdrop-bg: #000; +//** Modal backdrop opacity +@modal-backdrop-opacity: .5; +//** Modal header border color +@modal-header-border-color: #e5e5e5; +//** Modal footer border color +@modal-footer-border-color: @modal-header-border-color; + +@modal-lg: 900px; +@modal-md: 600px; +@modal-sm: 300px; + + +//== Alerts +// +//## Define alert colors, border radius, and padding. + +@alert-padding: 15px; +@alert-border-radius: @border-radius-base; +@alert-link-font-weight: bold; + +@alert-success-bg: @state-success-bg; +@alert-success-text: @state-success-text; +@alert-success-border: @state-success-border; + +@alert-info-bg: @state-info-bg; +@alert-info-text: @state-info-text; +@alert-info-border: @state-info-border; + +@alert-warning-bg: @state-warning-bg; +@alert-warning-text: @state-warning-text; +@alert-warning-border: @state-warning-border; + +@alert-danger-bg: @state-danger-bg; +@alert-danger-text: @state-danger-text; +@alert-danger-border: @state-danger-border; + + +//== Progress bars +// +//## + +//** Background color of the whole progress component +@progress-bg: #f5f5f5; +//** Progress bar text color +@progress-bar-color: #fff; +//** Variable for setting rounded corners on progress bar. +@progress-border-radius: @border-radius-base; + +//** Default progress bar color +@progress-bar-bg: @brand-primary; +//** Success progress bar color +@progress-bar-success-bg: @brand-success; +//** Warning progress bar color +@progress-bar-warning-bg: @brand-warning; +//** Danger progress bar color +@progress-bar-danger-bg: @brand-danger; +//** Info progress bar color +@progress-bar-info-bg: @brand-info; + + +//== List group +// +//## + +//** Background color on `.list-group-item` +@list-group-bg: #fff; +//** `.list-group-item` border color +@list-group-border: #ddd; +//** List group border radius +@list-group-border-radius: @border-radius-base; + +//** Background color of single list items on hover +@list-group-hover-bg: #f5f5f5; +//** Text color of active list items +@list-group-active-color: @component-active-color; +//** Background color of active list items +@list-group-active-bg: @component-active-bg; +//** Border color of active list elements +@list-group-active-border: @list-group-active-bg; +//** Text color for content within active list items +@list-group-active-text-color: lighten(@list-group-active-bg, 40%); + +//** Text color of disabled list items +@list-group-disabled-color: @gray-light; +//** Background color of disabled list items +@list-group-disabled-bg: @gray-lighter; +//** Text color for content within disabled list items +@list-group-disabled-text-color: @list-group-disabled-color; + +@list-group-link-color: #555; +@list-group-link-hover-color: @list-group-link-color; +@list-group-link-heading-color: #333; + + +//== Panels +// +//## + +@panel-bg: #fff; +@panel-body-padding: 15px; +@panel-heading-padding: 10px 15px; +@panel-footer-padding: @panel-heading-padding; +@panel-border-radius: @border-radius-base; + +//** Border color for elements within panels +@panel-inner-border: #ddd; +@panel-footer-bg: #f5f5f5; + +@panel-default-text: @gray-dark; +@panel-default-border: #ddd; +@panel-default-heading-bg: #f5f5f5; + +@panel-primary-text: #fff; +@panel-primary-border: @brand-primary; +@panel-primary-heading-bg: @brand-primary; + +@panel-success-text: @state-success-text; +@panel-success-border: @state-success-border; +@panel-success-heading-bg: @state-success-bg; + +@panel-info-text: @state-info-text; +@panel-info-border: @state-info-border; +@panel-info-heading-bg: @state-info-bg; + +@panel-warning-text: @state-warning-text; +@panel-warning-border: @state-warning-border; +@panel-warning-heading-bg: @state-warning-bg; + +@panel-danger-text: @state-danger-text; +@panel-danger-border: @state-danger-border; +@panel-danger-heading-bg: @state-danger-bg; + + +//== Thumbnails +// +//## + +//** Padding around the thumbnail image +@thumbnail-padding: 4px; +//** Thumbnail background color +@thumbnail-bg: @body-bg; +//** Thumbnail border color +@thumbnail-border: #ddd; +//** Thumbnail border radius +@thumbnail-border-radius: @border-radius-base; + +//** Custom text color for thumbnail captions +@thumbnail-caption-color: @text-color; +//** Padding around the thumbnail caption +@thumbnail-caption-padding: 9px; + + +//== Wells +// +//## + +@well-bg: #f5f5f5; +@well-border: darken(@well-bg, 7%); + + +//== Badges +// +//## + +@badge-color: #fff; +//** Linked badge text color on hover +@badge-link-hover-color: #fff; +@badge-bg: @gray-light; + +//** Badge text color in active nav link +@badge-active-color: @link-color; +//** Badge background color in active nav link +@badge-active-bg: #fff; + +@badge-font-weight: bold; +@badge-line-height: 1; +@badge-border-radius: 10px; + + +//== Breadcrumbs +// +//## + +@breadcrumb-padding-vertical: 8px; +@breadcrumb-padding-horizontal: 15px; +//** Breadcrumb background color +@breadcrumb-bg: #f5f5f5; +//** Breadcrumb text color +@breadcrumb-color: #ccc; +//** Text color of current page in the breadcrumb +@breadcrumb-active-color: @gray-light; +//** Textual separator for between breadcrumb elements +@breadcrumb-separator: "/"; + + +//== Carousel +// +//## + +@carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6); + +@carousel-control-color: #fff; +@carousel-control-width: 15%; +@carousel-control-opacity: .5; +@carousel-control-font-size: 20px; + +@carousel-indicator-active-bg: #fff; +@carousel-indicator-border-color: #fff; + +@carousel-caption-color: #fff; + + +//== Close +// +//## + +@close-font-weight: bold; +@close-color: #000; +@close-text-shadow: 0 1px 0 #fff; + + +//== Code +// +//## + +@code-color: #c7254e; +@code-bg: #f9f2f4; + +@kbd-color: #fff; +@kbd-bg: #333; + +@pre-bg: #f5f5f5; +@pre-color: @gray-dark; +@pre-border-color: #ccc; +@pre-scrollable-max-height: 340px; + + +//== Type +// +//## + +//** Horizontal offset for forms and lists. +@component-offset-horizontal: 180px; +//** Text muted color +@text-muted: @gray-light; +//** Abbreviations and acronyms border color +@abbr-border-color: @gray-light; +//** Headings small color +@headings-small-color: @gray-light; +//** Blockquote small color +@blockquote-small-color: @gray-light; +//** Blockquote font size +@blockquote-font-size: (@font-size-base * 1.25); +//** Blockquote border color +@blockquote-border-color: @gray-lighter; +//** Page header border color +@page-header-border-color: @gray-lighter; +//** Width of horizontal description list titles +@dl-horizontal-offset: @component-offset-horizontal; +//** Horizontal line color. +@hr-border: @gray-lighter; diff --git a/src/fmt/doc/bootstrap/wells.less b/src/fmt/doc/bootstrap/wells.less new file mode 100644 index 000000000..15d072b0c --- /dev/null +++ b/src/fmt/doc/bootstrap/wells.less @@ -0,0 +1,29 @@ +// +// Wells +// -------------------------------------------------- + + +// Base class +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: @well-bg; + border: 1px solid @well-border; + border-radius: @border-radius-base; + .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); + blockquote { + border-color: #ddd; + border-color: rgba(0,0,0,.15); + } +} + +// Sizes +.well-lg { + padding: 24px; + border-radius: @border-radius-large; +} +.well-sm { + padding: 9px; + border-radius: @border-radius-small; +} diff --git a/src/fmt/doc/build.py b/src/fmt/doc/build.py new file mode 100755 index 000000000..56142da2e --- /dev/null +++ b/src/fmt/doc/build.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +# Build the documentation. + +import errno, os, re, sys +from subprocess import check_call, CalledProcessError, Popen, PIPE, STDOUT + +versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3', '7.1.0', '7.1.1', '7.1.2', '7.1.3', '8.0.0', '8.0.1', '8.1.0', '8.1.1', '9.0.0', '9.1.0'] + +class Pip: + def __init__(self, venv_dir): + self.path = os.path.join(venv_dir, 'bin', 'pip') + + def install(self, package, commit=None): + "Install package using pip." + if commit: + package = 'git+https://github.com/{0}.git@{1}'.format(package, commit) + print('Installing {0}'.format(package)) + check_call([self.path, 'install', package]) + +def create_build_env(venv_dir='virtualenv'): + # Create virtualenv. + if not os.path.exists(venv_dir): + check_call(['python3', '-m', 'venv', venv_dir]) + # Install Sphinx and Breathe. Require the exact version of Sphinx which is + # compatible with Breathe. + pip = Pip(venv_dir) + pip.install('wheel') + pip.install('six') + # See: https://github.com/sphinx-doc/sphinx/issues/9777 + pip.install('docutils==0.17.1') + # Jinja2 >= 3.1 incompatible with sphinx 3.3.0 + # See: https://github.com/sphinx-doc/sphinx/issues/10291 + pip.install('Jinja2<3.1') + pip.install('sphinx-doc/sphinx', 'v3.3.0') + pip.install('michaeljones/breathe', 'v4.25.0') + +def build_docs(version='dev', **kwargs): + doc_dir = kwargs.get('doc_dir', os.path.dirname(os.path.realpath(__file__))) + work_dir = kwargs.get('work_dir', '.') + include_dir = kwargs.get( + 'include_dir', os.path.join(os.path.dirname(doc_dir), 'include', 'fmt')) + # Build docs. + cmd = ['doxygen', '-'] + p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT) + doxyxml_dir = os.path.join(work_dir, 'doxyxml') + out, _ = p.communicate(input=r''' + PROJECT_NAME = fmt + GENERATE_LATEX = NO + GENERATE_MAN = NO + GENERATE_RTF = NO + CASE_SENSE_NAMES = NO + INPUT = {0}/chrono.h {0}/color.h {0}/core.h {0}/compile.h \ + {0}/format.h {0}/os.h {0}/ostream.h {0}/printf.h \ + {0}/xchar.h + QUIET = YES + JAVADOC_AUTOBRIEF = YES + AUTOLINK_SUPPORT = NO + GENERATE_HTML = NO + GENERATE_XML = YES + XML_OUTPUT = {1} + ALIASES = "rst=\verbatim embed:rst" + ALIASES += "endrst=\endverbatim" + MACRO_EXPANSION = YES + PREDEFINED = _WIN32=1 \ + __linux__=1 \ + FMT_ENABLE_IF(...)= \ + FMT_USE_VARIADIC_TEMPLATES=1 \ + FMT_USE_RVALUE_REFERENCES=1 \ + FMT_USE_USER_DEFINED_LITERALS=1 \ + FMT_USE_ALIAS_TEMPLATES=1 \ + FMT_USE_NONTYPE_TEMPLATE_ARGS=1 \ + FMT_API= \ + "FMT_BEGIN_NAMESPACE=namespace fmt {{" \ + "FMT_END_NAMESPACE=}}" \ + "FMT_STRING_ALIAS=1" \ + "FMT_VARIADIC(...)=" \ + "FMT_VARIADIC_W(...)=" \ + "FMT_DOC=1" + EXCLUDE_SYMBOLS = fmt::formatter fmt::printf_formatter fmt::arg_join \ + fmt::basic_format_arg::handle + '''.format(include_dir, doxyxml_dir).encode('UTF-8')) + out = out.decode('utf-8') + internal_symbols = [ + 'fmt::detail::.*', + 'basic_data<>', + 'fmt::type_identity', + 'fmt::dynamic_formatter' + ] + noisy_warnings = [ + 'warning: (Compound|Member .* of class) (' + '|'.join(internal_symbols) + \ + ') is not documented.', + 'warning: Internal inconsistency: .* does not belong to any container!' + ] + for w in noisy_warnings: + out = re.sub('.*' + w + '\n', '', out) + print(out) + if p.returncode != 0: + raise CalledProcessError(p.returncode, cmd) + + html_dir = os.path.join(work_dir, 'html') + main_versions = reversed(versions[-3:]) + check_call([os.path.join(work_dir, 'virtualenv', 'bin', 'sphinx-build'), + '-Dbreathe_projects.format=' + os.path.abspath(doxyxml_dir), + '-Dversion=' + version, '-Drelease=' + version, + '-Aversion=' + version, '-Aversions=' + ','.join(main_versions), + '-b', 'html', doc_dir, html_dir]) + try: + check_call(['lessc', '--verbose', '--clean-css', + '--include-path=' + os.path.join(doc_dir, 'bootstrap'), + os.path.join(doc_dir, 'fmt.less'), + os.path.join(html_dir, '_static', 'fmt.css')]) + except OSError as e: + if e.errno != errno.ENOENT: + raise + print('lessc not found; make sure that Less (http://lesscss.org/) ' + + 'is installed') + sys.exit(1) + return html_dir + +if __name__ == '__main__': + create_build_env() + build_docs(sys.argv[1]) diff --git a/src/fmt/doc/conf.py b/src/fmt/doc/conf.py new file mode 100644 index 000000000..0756192b6 --- /dev/null +++ b/src/fmt/doc/conf.py @@ -0,0 +1,256 @@ +# -*- coding: utf-8 -*- +# +# format documentation build configuration file, created by +# sphinx-quickstart on Tue Dec 18 06:46:16 2012. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os, re, subprocess + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +needs_sphinx = '1.2' + +if os.environ.get('READTHEDOCS', None) == 'True': + subprocess.call('doxygen') + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.ifconfig', 'breathe'] + +breathe_default_project = "format" +breathe_domain_by_extension = {"h" : "cpp"} + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +#master_doc = 'contents' + +# General information about the project. +project = u'fmt' +copyright = u'2012-present, Victor Zverovich' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. + +# Version and release are passed from CMake. +#version = None + +# The full version, including alpha/beta/rc tags. +#release = version + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['virtualenv'] + +# The reST default role (used for this markup: `text`) to use for all documents. +default_role = 'cpp:any' + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +highlight_language = 'c++' + +primary_domain = 'cpp' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'basic-bootstrap' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = ['.'] + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +html_sidebars = { + '**': ['localtoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html'] +} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a <link> tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'formatdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'format.tex', u'fmt documentation', + u'Victor Zverovich', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'fmt', u'fmt documentation', [u'Victor Zverovich'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'fmt', u'fmt documentation', + u'Victor Zverovich', 'fmt', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' diff --git a/src/fmt/doc/contents.rst b/src/fmt/doc/contents.rst new file mode 100644 index 000000000..d38691e41 --- /dev/null +++ b/src/fmt/doc/contents.rst @@ -0,0 +1,10 @@ +######## +Contents +######## + +.. toctree:: + :maxdepth: 2 + + usage + api + syntax diff --git a/src/fmt/doc/fmt.less b/src/fmt/doc/fmt.less new file mode 100644 index 000000000..3a97b9fd8 --- /dev/null +++ b/src/fmt/doc/fmt.less @@ -0,0 +1,71 @@ +@import 'bootstrap.less'; + +@header-bg: #094d75; +@icon-font-path: "fonts/"; + +html { + overflow-y: scroll; +} + +.navbar { + border-radius: 0; + margin-bottom: 0; + background-color: darken(@header-bg, 10%); +} + +.jumbotron { + #gradient > .vertical(@header-bg; darken(@header-bg, 2%); 50%; 50%); + background-size: 100% 4px; + background-color: @header-bg; + background-repeat: repeat-y; + color: white; + text-align: center; +} + +div.sphinxsidebar { + margin-left: 0; +} + +// Keep content not too wide for better readability. +.navbar-content, .content { + .make-md-column-offset(1); + .make-md-column(10); + .make-lg-column-offset(2); + .make-lg-column(8); +} + +.footer { + padding-top: 20px; + padding-bottom: 20px; + border-top: 1px solid @gray-lighter; + text-align: center; +} + +// Indent descriptions of classes, functions and macros. +.class dd, .function dd, .macro dd { + margin-left: 20px; +} + +// Remove Bootstrap padding for Sphinx containers. +.breathe-sectiondef.container { + padding: 0; +} + +// Remove Bootstrap padding for Sphinx code elements in API signatures. +.descclassname, .descname { + padding: 0; +} + +// Override center alignment in tables. +td { + text-align: left; +} + +p.rubric { + margin-top: 10px; +} + +.github-btn { + border: 0; + overflow: hidden; +} diff --git a/src/fmt/doc/index.rst b/src/fmt/doc/index.rst new file mode 100644 index 000000000..8d55c7a1c --- /dev/null +++ b/src/fmt/doc/index.rst @@ -0,0 +1,198 @@ +Overview +======== + +**{fmt}** is an open-source formatting library providing a fast and safe +alternative to C stdio and C++ iostreams. + +.. raw:: html + + <div class="panel panel-default"> + <div class="panel-heading">What users say:</div> + <div class="panel-body"> + Thanks for creating this library. It’s been a hole in C++ for + a long time. I’ve used both <code>boost::format</code> and + <code>loki::SPrintf</code>, and neither felt like the right answer. + This does. + </div> + </div> + +.. _format-api-intro: + +Format API +---------- + +The format API is similar in spirit to the C ``printf`` family of function but +is safer, simpler and several times `faster +<https://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_ +than common standard library implementations. +The `format string syntax <syntax.html>`_ is similar to the one used by +`str.format <https://docs.python.org/3/library/stdtypes.html#str.format>`_ in +Python: + +.. code:: c++ + + std::string s = fmt::format("The answer is {}.", 42); + +The ``fmt::format`` function returns a string "The answer is 42.". You can use +``fmt::memory_buffer`` to avoid constructing ``std::string``: + +.. code:: c++ + + auto out = fmt::memory_buffer(); + fmt::format_to(std::back_inserter(out), + "For a moment, {} happened.", "nothing"); + auto data = out.data(); // pointer to the formatted data + auto size = out.size(); // size of the formatted data + +The ``fmt::print`` function performs formatting and writes the result to a stream: + +.. code:: c++ + + fmt::print(stderr, "System error code = {}\n", errno); + +If you omit the file argument the function will print to ``stdout``: + +.. code:: c++ + + fmt::print("Don't {}\n", "panic"); + +The format API also supports positional arguments useful for localization: + +.. code:: c++ + + fmt::print("I'd rather be {1} than {0}.", "right", "happy"); + +You can pass named arguments with ``fmt::arg``: + +.. code:: c++ + + fmt::print("Hello, {name}! The answer is {number}. Goodbye, {name}.", + fmt::arg("name", "World"), fmt::arg("number", 42)); + +If your compiler supports C++11 user-defined literals, the suffix ``_a`` offers +an alternative, slightly terser syntax for named arguments: + +.. code:: c++ + + using namespace fmt::literals; + fmt::print("Hello, {name}! The answer is {number}. Goodbye, {name}.", + "name"_a="World", "number"_a=42); + +.. _safety: + +Safety +------ + +The library is fully type safe, automatic memory management prevents buffer +overflow, errors in format strings are reported using exceptions or at compile +time. For example, the code + +.. code:: c++ + + fmt::format("The answer is {:d}", "forty-two"); + +throws the ``format_error`` exception because the argument ``"forty-two"`` is a +string while the format code ``d`` only applies to integers. + +The code + +.. code:: c++ + + format(FMT_STRING("The answer is {:d}"), "forty-two"); + +reports a compile-time error on compilers that support relaxed ``constexpr``. +See `here <api.html#compile-time-format-string-checks>`_ for details. + +The following code + +.. code:: c++ + + fmt::format("Cyrillic letter {}", L'\x42e'); + +produces a compile-time error because wide character ``L'\x42e'`` cannot be +formatted into a narrow string. For comparison, writing a wide character to +``std::ostream`` results in its numeric value being written to the stream +(i.e. 1070 instead of letter 'ю' which is represented by ``L'\x42e'`` if we +use Unicode) which is rarely desirable. + +Compact Binary Code +------------------- + +The library produces compact per-call compiled code. For example +(`godbolt <https://godbolt.org/g/TZU4KF>`_), + +.. code:: c++ + + #include <fmt/core.h> + + int main() { + fmt::print("The answer is {}.", 42); + } + +compiles to just + +.. code:: asm + + main: # @main + sub rsp, 24 + mov qword ptr [rsp], 42 + mov rcx, rsp + mov edi, offset .L.str + mov esi, 17 + mov edx, 1 + call fmt::v7::vprint(fmt::v7::basic_string_view<char>, fmt::v7::format_args) + xor eax, eax + add rsp, 24 + ret + .L.str: + .asciz "The answer is {}." + +.. _portability: + +Portability +----------- + +The library is highly portable and relies only on a small set of C++11 features: + +* variadic templates +* type traits +* rvalue references +* decltype +* trailing return types +* deleted functions +* alias templates + +These are available in GCC 4.8, Clang 3.4, MSVC 19.0 (2015) and more recent +compiler version. For older compilers use {fmt} `version 4.x +<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which is maintained and +only requires C++98. + +The output of all formatting functions is consistent across platforms. +For example, + +.. code:: + + fmt::print("{}", std::numeric_limits<double>::infinity()); + +always prints ``inf`` while the output of ``printf`` is platform-dependent. + +.. _ease-of-use: + +Ease of Use +----------- + +{fmt} has a small self-contained code base with the core library consisting of +just three header files and no external dependencies. +A permissive MIT `license <https://github.com/fmtlib/fmt#license>`_ allows +using the library both in open-source and commercial projects. + +`Learn more... <contents.html>`_ + +.. raw:: html + + <a class="btn btn-success" href="https://github.com/fmtlib/fmt">GitHub Repository</a> + + <div class="section footer"> + <iframe src="https://ghbtns.com/github-btn.html?user=fmtlib&repo=fmt&type=watch&count=true" + class="github-btn" width="100" height="20"></iframe> + </div> diff --git a/src/fmt/doc/python-license.txt b/src/fmt/doc/python-license.txt new file mode 100644 index 000000000..88eed1f9c --- /dev/null +++ b/src/fmt/doc/python-license.txt @@ -0,0 +1,290 @@ +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 + 2.6.2 2.6.1 2009 PSF yes + 2.6.3 2.6.2 2009 PSF yes + 2.6.4 2.6.3 2009 PSF yes + 2.6.5 2.6.4 2010 PSF yes + 3.0 2.6 2008 PSF yes + 3.0.1 3.0 2009 PSF yes + 3.1 3.0.1 2009 PSF yes + 3.1.1 3.1 2009 PSF yes + 3.1.2 3.1.1 2010 PSF yes + 3.1.3 3.1.2 2010 PSF yes + 3.1.4 3.1.3 2011 PSF yes + 3.2 3.1 2011 PSF yes + 3.2.1 3.2 2011 PSF yes + 3.2.2 3.2.1 2011 PSF yes + 3.2.3 3.2.2 2012 PSF yes + 3.3.0 3.2 2012 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, 2010, +2011, 2012 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. diff --git a/src/fmt/doc/syntax.rst b/src/fmt/doc/syntax.rst new file mode 100644 index 000000000..1ec5c26f7 --- /dev/null +++ b/src/fmt/doc/syntax.rst @@ -0,0 +1,514 @@ +.. _syntax: + +******************** +Format String Syntax +******************** + +Formatting functions such as :ref:`fmt::format() <format>` and +:ref:`fmt::print() <print>` use the same format string syntax described in this +section. + +Format strings contain "replacement fields" surrounded by curly braces ``{}``. +Anything that is not contained in braces is considered literal text, which is +copied unchanged to the output. If you need to include a brace character in the +literal text, it can be escaped by doubling: ``{{`` and ``}}``. + +The grammar for a replacement field is as follows: + +.. productionlist:: sf + replacement_field: "{" [`arg_id`] [":" (`format_spec` | `chrono_format_spec`)] "}" + arg_id: `integer` | `identifier` + integer: `digit`+ + digit: "0"..."9" + identifier: `id_start` `id_continue`* + id_start: "a"..."z" | "A"..."Z" | "_" + id_continue: `id_start` | `digit` + +In less formal terms, the replacement field can start with an *arg_id* +that specifies the argument whose value is to be formatted and inserted into +the output instead of the replacement field. +The *arg_id* is optionally followed by a *format_spec*, which is preceded by a +colon ``':'``. These specify a non-default format for the replacement value. + +See also the :ref:`formatspec` section. + +If the numerical arg_ids in a format string are 0, 1, 2, ... in sequence, +they can all be omitted (not just some) and the numbers 0, 1, 2, ... will be +automatically inserted in that order. + +Named arguments can be referred to by their names or indices. + +Some simple format string examples:: + + "First, thou shalt count to {0}" // References the first argument + "Bring me a {}" // Implicitly references the first argument + "From {} to {}" // Same as "From {0} to {1}" + +The *format_spec* field contains a specification of how the value should be +presented, including such details as field width, alignment, padding, decimal +precision and so on. Each value type can define its own "formatting +mini-language" or interpretation of the *format_spec*. + +Most built-in types support a common formatting mini-language, which is +described in the next section. + +A *format_spec* field can also include nested replacement fields in certain +positions within it. These nested replacement fields can contain only an +argument id; format specifications are not allowed. This allows the formatting +of a value to be dynamically specified. + +See the :ref:`formatexamples` section for some examples. + +.. _formatspec: + +Format Specification Mini-Language +================================== + +"Format specifications" are used within replacement fields contained within a +format string to define how individual values are presented (see +:ref:`syntax`). Each formattable type may define how the format +specification is to be interpreted. + +Most built-in types implement the following options for format specifications, +although some of the formatting options are only supported by the numeric types. + +The general form of a *standard format specifier* is: + +.. productionlist:: sf + format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`]["L"][`type`] + fill: <a character other than '{' or '}'> + align: "<" | ">" | "^" + sign: "+" | "-" | " " + width: `integer` | "{" [`arg_id`] "}" + precision: `integer` | "{" [`arg_id`] "}" + type: "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | + : "o" | "p" | "s" | "x" | "X" + +The *fill* character can be any Unicode code point other than ``'{'`` or +``'}'``. The presence of a fill character is signaled by the character following +it, which must be one of the alignment options. If the second character of +*format_spec* is not a valid alignment option, then it is assumed that both the +fill character and the alignment option are absent. + +The meaning of the various alignment options is as follows: + ++---------+----------------------------------------------------------+ +| Option | Meaning | ++=========+==========================================================+ +| ``'<'`` | Forces the field to be left-aligned within the available | +| | space (this is the default for most objects). | ++---------+----------------------------------------------------------+ +| ``'>'`` | Forces the field to be right-aligned within the | +| | available space (this is the default for numbers). | ++---------+----------------------------------------------------------+ +| ``'^'`` | Forces the field to be centered within the available | +| | space. | ++---------+----------------------------------------------------------+ + +Note that unless a minimum field width is defined, the field width will always +be the same size as the data to fill it, so that the alignment option has no +meaning in this case. + +The *sign* option is only valid for number types, and can be one of the +following: + ++---------+------------------------------------------------------------+ +| Option | Meaning | ++=========+============================================================+ +| ``'+'`` | indicates that a sign should be used for both | +| | nonnegative as well as negative numbers. | ++---------+------------------------------------------------------------+ +| ``'-'`` | indicates that a sign should be used only for negative | +| | numbers (this is the default behavior). | ++---------+------------------------------------------------------------+ +| space | indicates that a leading space should be used on | +| | nonnegative numbers, and a minus sign on negative numbers. | ++---------+------------------------------------------------------------+ + +The ``'#'`` option causes the "alternate form" to be used for the +conversion. The alternate form is defined differently for different +types. This option is only valid for integer and floating-point types. +For integers, when binary, octal, or hexadecimal output is used, this +option adds the prefix respective ``"0b"`` (``"0B"``), ``"0"``, or +``"0x"`` (``"0X"``) to the output value. Whether the prefix is +lower-case or upper-case is determined by the case of the type +specifier, for example, the prefix ``"0x"`` is used for the type ``'x'`` +and ``"0X"`` is used for ``'X'``. For floating-point numbers the +alternate form causes the result of the conversion to always contain a +decimal-point character, even if no digits follow it. Normally, a +decimal-point character appears in the result of these conversions +only if a digit follows it. In addition, for ``'g'`` and ``'G'`` +conversions, trailing zeros are not removed from the result. + +.. ifconfig:: False + + The ``','`` option signals the use of a comma for a thousands separator. + For a locale aware separator, use the ``'L'`` integer presentation type + instead. + +*width* is a decimal integer defining the minimum field width. If not +specified, then the field width will be determined by the content. + +Preceding the *width* field by a zero (``'0'``) character enables sign-aware +zero-padding for numeric types. It forces the padding to be placed after the +sign or base (if any) but before the digits. This is used for printing fields in +the form '+000000120'. This option is only valid for numeric types and it has no +effect on formatting of infinity and NaN. + +The *precision* is a decimal number indicating how many digits should be +displayed after the decimal point for a floating-point value formatted with +``'f'`` and ``'F'``, or before and after the decimal point for a floating-point +value formatted with ``'g'`` or ``'G'``. For non-number types the field +indicates the maximum field size - in other words, how many characters will be +used from the field content. The *precision* is not allowed for integer, +character, Boolean, and pointer values. Note that a C string must be +null-terminated even if precision is specified. + +The ``'L'`` option uses the current locale setting to insert the appropriate +number separator characters. This option is only valid for numeric types. + +Finally, the *type* determines how the data should be presented. + +The available string presentation types are: + ++---------+----------------------------------------------------------+ +| Type | Meaning | ++=========+==========================================================+ +| ``'s'`` | String format. This is the default type for strings and | +| | may be omitted. | ++---------+----------------------------------------------------------+ +| none | The same as ``'s'``. | ++---------+----------------------------------------------------------+ + +The available character presentation types are: + ++---------+----------------------------------------------------------+ +| Type | Meaning | ++=========+==========================================================+ +| ``'c'`` | Character format. This is the default type for | +| | characters and may be omitted. | ++---------+----------------------------------------------------------+ +| none | The same as ``'c'``. | ++---------+----------------------------------------------------------+ + +The available integer presentation types are: + ++---------+----------------------------------------------------------+ +| Type | Meaning | ++=========+==========================================================+ +| ``'b'`` | Binary format. Outputs the number in base 2. Using the | +| | ``'#'`` option with this type adds the prefix ``"0b"`` | +| | to the output value. | ++---------+----------------------------------------------------------+ +| ``'B'`` | Binary format. Outputs the number in base 2. Using the | +| | ``'#'`` option with this type adds the prefix ``"0B"`` | +| | to the output value. | ++---------+----------------------------------------------------------+ +| ``'c'`` | Character format. Outputs the number as a character. | ++---------+----------------------------------------------------------+ +| ``'d'`` | Decimal integer. Outputs the number in base 10. | ++---------+----------------------------------------------------------+ +| ``'o'`` | Octal format. Outputs the number in base 8. | ++---------+----------------------------------------------------------+ +| ``'x'`` | Hex format. Outputs the number in base 16, using | +| | lower-case letters for the digits above 9. Using the | +| | ``'#'`` option with this type adds the prefix ``"0x"`` | +| | to the output value. | ++---------+----------------------------------------------------------+ +| ``'X'`` | Hex format. Outputs the number in base 16, using | +| | upper-case letters for the digits above 9. Using the | +| | ``'#'`` option with this type adds the prefix ``"0X"`` | +| | to the output value. | ++---------+----------------------------------------------------------+ +| none | The same as ``'d'``. | ++---------+----------------------------------------------------------+ + +Integer presentation types can also be used with character and Boolean values. +Boolean values are formatted using textual representation, either ``true`` or +``false``, if the presentation type is not specified. + +The available presentation types for floating-point values are: + ++---------+----------------------------------------------------------+ +| Type | Meaning | ++=========+==========================================================+ +| ``'a'`` | Hexadecimal floating point format. Prints the number in | +| | base 16 with prefix ``"0x"`` and lower-case letters for | +| | digits above 9. Uses ``'p'`` to indicate the exponent. | ++---------+----------------------------------------------------------+ +| ``'A'`` | Same as ``'a'`` except it uses upper-case letters for | +| | the prefix, digits above 9 and to indicate the exponent. | ++---------+----------------------------------------------------------+ +| ``'e'`` | Exponent notation. Prints the number in scientific | +| | notation using the letter 'e' to indicate the exponent. | ++---------+----------------------------------------------------------+ +| ``'E'`` | Exponent notation. Same as ``'e'`` except it uses an | +| | upper-case ``'E'`` as the separator character. | ++---------+----------------------------------------------------------+ +| ``'f'`` | Fixed point. Displays the number as a fixed-point | +| | number. | ++---------+----------------------------------------------------------+ +| ``'F'`` | Fixed point. Same as ``'f'``, but converts ``nan`` to | +| | ``NAN`` and ``inf`` to ``INF``. | ++---------+----------------------------------------------------------+ +| ``'g'`` | General format. For a given precision ``p >= 1``, | +| | this rounds the number to ``p`` significant digits and | +| | then formats the result in either fixed-point format | +| | or in scientific notation, depending on its magnitude. | +| | | +| | A precision of ``0`` is treated as equivalent to a | +| | precision of ``1``. | ++---------+----------------------------------------------------------+ +| ``'G'`` | General format. Same as ``'g'`` except switches to | +| | ``'E'`` if the number gets too large. The | +| | representations of infinity and NaN are uppercased, too. | ++---------+----------------------------------------------------------+ +| none | Similar to ``'g'``, except that the default precision is | +| | as high as needed to represent the particular value. | ++---------+----------------------------------------------------------+ + +.. ifconfig:: False + + +---------+----------------------------------------------------------+ + | | The precise rules are as follows: suppose that the | + | | result formatted with presentation type ``'e'`` and | + | | precision ``p-1`` would have exponent ``exp``. Then | + | | if ``-4 <= exp < p``, the number is formatted | + | | with presentation type ``'f'`` and precision | + | | ``p-1-exp``. Otherwise, the number is formatted | + | | with presentation type ``'e'`` and precision ``p-1``. | + | | In both cases insignificant trailing zeros are removed | + | | from the significand, and the decimal point is also | + | | removed if there are no remaining digits following it. | + | | | + | | Positive and negative infinity, positive and negative | + | | zero, and nans, are formatted as ``inf``, ``-inf``, | + | | ``0``, ``-0`` and ``nan`` respectively, regardless of | + | | the precision. | + | | | + +---------+----------------------------------------------------------+ + +The available presentation types for pointers are: + ++---------+----------------------------------------------------------+ +| Type | Meaning | ++=========+==========================================================+ +| ``'p'`` | Pointer format. This is the default type for | +| | pointers and may be omitted. | ++---------+----------------------------------------------------------+ +| none | The same as ``'p'``. | ++---------+----------------------------------------------------------+ + +.. _chrono-specs: + +Chrono Format Specifications +============================ + +Format specifications for chrono types and ``std::tm`` have the following +syntax: + +.. productionlist:: sf + chrono_format_spec: [[`fill`]`align`][`width`]["." `precision`][`chrono_specs`] + chrono_specs: [`chrono_specs`] `conversion_spec` | `chrono_specs` `literal_char` + conversion_spec: "%" [`modifier`] `chrono_type` + literal_char: <a character other than '{', '}' or '%'> + modifier: "E" | "O" + chrono_type: "a" | "A" | "b" | "B" | "c" | "C" | "d" | "D" | "e" | "F" | + : "g" | "G" | "h" | "H" | "I" | "j" | "m" | "M" | "n" | "p" | + : "q" | "Q" | "r" | "R" | "S" | "t" | "T" | "u" | "U" | "V" | + : "w" | "W" | "x" | "X" | "y" | "Y" | "z" | "Z" | "%" + +Literal chars are copied unchanged to the output. Precision is valid only for +``std::chrono::duration`` types with a floating-point representation type. + +The available presentation types (*chrono_type*) for chrono durations and time +points are: + ++---------+--------------------------------------------------------------------+ +| Type | Meaning | ++=========+====================================================================+ +| ``'H'`` | The hour (24-hour clock) as a decimal number. If the result is a | +| | single digit, it is prefixed with 0. The modified command ``%OH`` | +| | produces the locale's alternative representation. | ++---------+--------------------------------------------------------------------+ +| ``'M'`` | The minute as a decimal number. If the result is a single digit, | +| | it is prefixed with 0. The modified command ``%OM`` produces the | +| | locale's alternative representation. | ++---------+--------------------------------------------------------------------+ +| ``'S'`` | Seconds as a decimal number. If the number of seconds is less than | +| | 10, the result is prefixed with 0. If the precision of the input | +| | cannot be exactly represented with seconds, then the format is a | +| | decimal floating-point number with a fixed format and a precision | +| | matching that of the precision of the input (or to a microseconds | +| | precision if the conversion to floating-point decimal seconds | +| | cannot be made within 18 fractional digits). The character for the | +| | decimal point is localized according to the locale. The modified | +| | command ``%OS`` produces the locale's alternative representation. | ++---------+--------------------------------------------------------------------+ + +Specifiers that have a calendaric component such as ``'d'`` (the day of month) +are valid only for ``std::tm`` and not durations or time points. + +.. range-specs: + +Range Format Specifications +=========================== + +Format specifications for range types have the following syntax: + +.. productionlist:: sf + range_format_spec: [":" [`underlying_spec`]] + +The `underlying_spec` is parsed based on the formatter of the range's +reference type. + +By default, a range of characters or strings is printed escaped and quoted. But +if any `underlying_spec` is provided (even if it is empty), then the characters +or strings are printed according to the provided specification. + +Examples:: + + fmt::format("{}", std::vector{10, 20, 30}); + // Result: [10, 20, 30] + fmt::format("{::#x}", std::vector{10, 20, 30}); + // Result: [0xa, 0x14, 0x13] + fmt::format("{}", vector{'h', 'e', 'l', 'l', 'o'}); + // Result: ['h', 'e', 'l', 'l', 'o'] + fmt::format("{::}", vector{'h', 'e', 'l', 'l', 'o'}); + // Result: [h, e, l, l, o] + fmt::format("{::d}", vector{'h', 'e', 'l', 'l', 'o'}); + // Result: [104, 101, 108, 108, 111] + +.. _formatexamples: + +Format Examples +=============== + +This section contains examples of the format syntax and comparison with +the printf formatting. + +In most of the cases the syntax is similar to the printf formatting, with the +addition of the ``{}`` and with ``:`` used instead of ``%``. +For example, ``"%03.2f"`` can be translated to ``"{:03.2f}"``. + +The new format syntax also supports new and different options, shown in the +following examples. + +Accessing arguments by position:: + + fmt::format("{0}, {1}, {2}", 'a', 'b', 'c'); + // Result: "a, b, c" + fmt::format("{}, {}, {}", 'a', 'b', 'c'); + // Result: "a, b, c" + fmt::format("{2}, {1}, {0}", 'a', 'b', 'c'); + // Result: "c, b, a" + fmt::format("{0}{1}{0}", "abra", "cad"); // arguments' indices can be repeated + // Result: "abracadabra" + +Aligning the text and specifying a width:: + + fmt::format("{:<30}", "left aligned"); + // Result: "left aligned " + fmt::format("{:>30}", "right aligned"); + // Result: " right aligned" + fmt::format("{:^30}", "centered"); + // Result: " centered " + fmt::format("{:*^30}", "centered"); // use '*' as a fill char + // Result: "***********centered***********" + +Dynamic width:: + + fmt::format("{:<{}}", "left aligned", 30); + // Result: "left aligned " + +Dynamic precision:: + + fmt::format("{:.{}f}", 3.14, 1); + // Result: "3.1" + +Replacing ``%+f``, ``%-f``, and ``% f`` and specifying a sign:: + + fmt::format("{:+f}; {:+f}", 3.14, -3.14); // show it always + // Result: "+3.140000; -3.140000" + fmt::format("{: f}; {: f}", 3.14, -3.14); // show a space for positive numbers + // Result: " 3.140000; -3.140000" + fmt::format("{:-f}; {:-f}", 3.14, -3.14); // show only the minus -- same as '{:f}; {:f}' + // Result: "3.140000; -3.140000" + +Replacing ``%x`` and ``%o`` and converting the value to different bases:: + + fmt::format("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); + // Result: "int: 42; hex: 2a; oct: 52; bin: 101010" + // with 0x or 0 or 0b as prefix: + fmt::format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42); + // Result: "int: 42; hex: 0x2a; oct: 052; bin: 0b101010" + +Padded hex byte with prefix and always prints both hex characters:: + + fmt::format("{:#04x}", 0); + // Result: "0x00" + +Box drawing using Unicode fill:: + + fmt::print( + "┌{0:─^{2}}┐\n" + "│{1: ^{2}}│\n" + "└{0:─^{2}}┘\n", "", "Hello, world!", 20); + +prints:: + + ┌────────────────────┐ + │ Hello, world! │ + └────────────────────┘ + +Using type-specific formatting:: + + #include <fmt/chrono.h> + + auto t = tm(); + t.tm_year = 2010 - 1900; + t.tm_mon = 7; + t.tm_mday = 4; + t.tm_hour = 12; + t.tm_min = 15; + t.tm_sec = 58; + fmt::print("{:%Y-%m-%d %H:%M:%S}", t); + // Prints: 2010-08-04 12:15:58 + +Using the comma as a thousands separator:: + + #include <fmt/format.h> + + auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890); + // s == "1,234,567,890" + +.. ifconfig:: False + + Nesting arguments and more complex examples:: + + >>> for align, text in zip('<^>', ['left', 'center', 'right']): + ... '{0:{fill}{align}16}") << text, fill=align, align=align) + ... + 'left<<<<<<<<<<<<' + '^^^^^center^^^^^' + '>>>>>>>>>>>right' + >>> + >>> octets = [192, 168, 0, 1] + Format("{:02X}{:02X}{:02X}{:02X}") << *octets) + 'C0A80001' + >>> int(_, 16) + 3232235521 + >>> + >>> width = 5 + >>> for num in range(5,12): + ... for base in 'dXob': + ... print('{0:{width}{base}}") << num, base=base, width=width), end=' ') + ... print() + ... + 5 5 5 101 + 6 6 6 110 + 7 7 7 111 + 8 8 10 1000 + 9 9 11 1001 + 10 A 12 1010 + 11 B 13 1011 diff --git a/src/fmt/doc/usage.rst b/src/fmt/doc/usage.rst new file mode 100644 index 000000000..f9f818584 --- /dev/null +++ b/src/fmt/doc/usage.rst @@ -0,0 +1,212 @@ +***** +Usage +***** + +To use the {fmt} library, add :file:`fmt/core.h`, :file:`fmt/format.h`, +:file:`fmt/format-inl.h`, :file:`src/format.cc` and optionally other headers +from a `release archive <https://github.com/fmtlib/fmt/releases/latest>`_ or +the `Git repository <https://github.com/fmtlib/fmt>`_ to your project. +Alternatively, you can :ref:`build the library with CMake <building>`. + +.. _building: + +Building the Library +==================== + +The included `CMake build script`__ can be used to build the fmt +library on a wide range of platforms. CMake is freely available for +download from https://www.cmake.org/download/. + +__ https://github.com/fmtlib/fmt/blob/master/CMakeLists.txt + +CMake works by generating native makefiles or project files that can +be used in the compiler environment of your choice. The typical +workflow starts with:: + + mkdir build # Create a directory to hold the build output. + cd build + cmake .. # Generate native build scripts. + +where :file:`{<path/to/fmt>}` is a path to the ``fmt`` repository. + +If you are on a \*nix system, you should now see a Makefile in the +current directory. Now you can build the library by running :command:`make`. + +Once the library has been built you can invoke :command:`make test` to run +the tests. + +You can control generation of the make ``test`` target with the ``FMT_TEST`` +CMake option. This can be useful if you include fmt as a subdirectory in +your project but don't want to add fmt's tests to your ``test`` target. + +If you use Windows and have Visual Studio installed, a :file:`FMT.sln` +file and several :file:`.vcproj` files will be created. You can then build them +using Visual Studio or msbuild. + +On Mac OS X with Xcode installed, an :file:`.xcodeproj` file will be generated. + +To build a `shared library`__ set the ``BUILD_SHARED_LIBS`` CMake variable to +``TRUE``:: + + cmake -DBUILD_SHARED_LIBS=TRUE ... + +__ https://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries + + +To build a `static library` with position independent code (required if the main +consumer of the fmt library is a shared library i.e. a Python extension) set the +``CMAKE_POSITION_INDEPENDENT_CODE`` CMake variable to ``TRUE``:: + + cmake -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE ... + + +Installing the Library +====================== + +After building the library you can install it on a Unix-like system by running +:command:`sudo make install`. + +Usage with CMake +================ + +You can add the ``fmt`` library directory into your project and include it in +your ``CMakeLists.txt`` file:: + + add_subdirectory(fmt) + +or + +:: + + add_subdirectory(fmt EXCLUDE_FROM_ALL) + +to exclude it from ``make``, ``make all``, or ``cmake --build .``. + +You can detect and use an installed version of {fmt} as follows:: + + find_package(fmt) + target_link_libraries(<your-target> fmt::fmt) + +Setting up your target to use a header-only version of ``fmt`` is equally easy:: + + target_link_libraries(<your-target> PRIVATE fmt::fmt-header-only) + +Usage with build2 +================= + +You can use `build2 <https://build2.org>`_, a dependency manager and a +build-system combined, to use ``fmt``. + +Currently this package is available in these package repositories: + +- **https://cppget.org/fmt/** for released and published versions. +- `The git repository with the sources of the build2 package of fmt <https://github.com/build2-packaging/fmt.git>`_ + for unreleased or custom revisions of ``fmt``. + +**Usage:** + +- ``build2`` package name: ``fmt`` +- Library target name : ``lib{fmt}`` + +For example, to make your ``build2`` project depend on ``fmt``: + +- Add one of the repositories to your configurations, or in your + ``repositories.manifest``, if not already there:: + + : + role: prerequisite + location: https://pkg.cppget.org/1/stable + +- Add this package as a dependency to your ``./manifest`` file + (example for ``v7.0.x``):: + + depends: fmt ~7.0.0 + +- Import the target and use it as a prerequisite to your own target + using `fmt` in the appropriate ``buildfile``:: + + import fmt = fmt%lib{fmt} + lib{mylib} : cxx{**} ... $fmt + +Then build your project as usual with `b` or `bdep update`. + +For ``build2`` newcomers or to get more details and use cases, you can read the +``build2`` +`toolchain introduction <https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml>`_. + +Building the Documentation +========================== + +To build the documentation you need the following software installed on your +system: + +* `Python <https://www.python.org/>`_ with pip and virtualenv +* `Doxygen <http://www.stack.nl/~dimitri/doxygen/>`_ +* `Less <http://lesscss.org/>`_ with ``less-plugin-clean-css``. + Ubuntu doesn't package the ``clean-css`` plugin so you should use ``npm`` + instead of ``apt`` to install both ``less`` and the plugin:: + + sudo npm install -g less less-plugin-clean-css. + +First generate makefiles or project files using CMake as described in +the previous section. Then compile the ``doc`` target/project, for example:: + + make doc + +This will generate the HTML documentation in ``doc/html``. + +Conda +===== + +fmt can be installed on Linux, macOS and Windows with +`Conda <https://docs.conda.io/en/latest/>`__, using its +`conda-forge <https://conda-forge.org>`__ +`package <https://github.com/conda-forge/fmt-feedstock>`__, as follows:: + + conda install -c conda-forge fmt + +Vcpkg +===== + +You can download and install fmt using the `vcpkg +<https://github.com/Microsoft/vcpkg>`__ dependency manager:: + + git clone https://github.com/Microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.sh + ./vcpkg integrate install + ./vcpkg install fmt + +The fmt port in vcpkg is kept up to date by Microsoft team members and community +contributors. If the version is out of date, please `create an issue or pull +request <https://github.com/Microsoft/vcpkg>`__ on the vcpkg repository. + +LHelper +======= + +You can download and install fmt using +`lhelper <https://github.com/franko/lhelper>`__ dependency manager:: + + lhelper activate <some-environment> + lhelper install fmt + +All the recipes for lhelper are kept in the +`lhelper's recipe <https://github.com/franko/lhelper-recipes>`__ repository. + +Android NDK +=========== + +fmt provides `Android.mk file`__ that can be used to build the library +with `Android NDK <https://developer.android.com/tools/sdk/ndk/index.html>`_. +For an example of using fmt with Android NDK, see the +`android-ndk-example <https://github.com/fmtlib/android-ndk-example>`_ +repository. + +__ https://github.com/fmtlib/fmt/blob/master/support/Android.mk + +Homebrew +======== + +fmt can be installed on OS X using `Homebrew <https://brew.sh/>`_:: + + brew install fmt diff --git a/src/fmt/include/fmt/args.h b/src/fmt/include/fmt/args.h new file mode 100644 index 000000000..a3966d140 --- /dev/null +++ b/src/fmt/include/fmt/args.h @@ -0,0 +1,234 @@ +// Formatting library for C++ - dynamic format arguments +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_ARGS_H_ +#define FMT_ARGS_H_ + +#include <functional> // std::reference_wrapper +#include <memory> // std::unique_ptr +#include <vector> + +#include "core.h" + +FMT_BEGIN_NAMESPACE + +namespace detail { + +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 + +/** + \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< + std::is_convertible<T, std::basic_string<char_type>>::value && + !detail::is_reference_wrapper<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: + constexpr dynamic_format_arg_store() = default; + + /** + \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. + + **Example**:: + + fmt::dynamic_format_arg_store<fmt::format_context> store; + char band[] = "Rolling Stones"; + store.push_back(std::cref(band)); + band[9] = 'c'; // Changing str affects the output. + std::string result = fmt::vformat("{}", store); + // result == "Rolling Scones" + \endrst + */ + template <typename T> void push_back(std::reference_wrapper<T> arg) { + static_assert( + 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. The name is always copied into the store. + */ + 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); + } +}; + +FMT_END_NAMESPACE + +#endif // FMT_ARGS_H_ diff --git a/src/fmt/include/fmt/chrono.h b/src/fmt/include/fmt/chrono.h new file mode 100644 index 000000000..b112f76e9 --- /dev/null +++ b/src/fmt/include/fmt/chrono.h @@ -0,0 +1,2069 @@ +// Formatting library for C++ - chrono support +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_CHRONO_H_ +#define FMT_CHRONO_H_ + +#include <algorithm> +#include <chrono> +#include <cmath> // std::isfinite +#include <cstring> // std::memcpy +#include <ctime> +#include <iterator> +#include <locale> +#include <ostream> +#include <type_traits> + +#include "format.h" + +FMT_BEGIN_NAMESPACE + +// Enable tzset. +#ifndef FMT_USE_TZSET +// UWP doesn't provide _tzset. +# if FMT_HAS_INCLUDE("winapifamily.h") +# include <winapifamily.h> +# endif +# if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \ + (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) +# define FMT_USE_TZSET 1 +# else +# define FMT_USE_TZSET 0 +# endif +#endif + +// Enable safe chrono durations, unless explicitly disabled. +#ifndef FMT_SAFE_DURATION_CAST +# define FMT_SAFE_DURATION_CAST 1 +#endif +#if FMT_SAFE_DURATION_CAST + +// For conversion between std::chrono::durations without undefined +// behaviour or erroneous results. +// This is a stripped down version of duration_cast, for inclusion in fmt. +// See https://github.com/pauldreik/safe_duration_cast +// +// Copyright Paul Dreik 2019 +namespace safe_duration_cast { + +template <typename To, typename From, + FMT_ENABLE_IF(!std::is_same<From, To>::value && + std::numeric_limits<From>::is_signed == + std::numeric_limits<To>::is_signed)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + using F = std::numeric_limits<From>; + using T = std::numeric_limits<To>; + static_assert(F::is_integer, "From must be integral"); + static_assert(T::is_integer, "To must be integral"); + + // A and B are both signed, or both unsigned. + if (detail::const_check(F::digits <= T::digits)) { + // From fits in To without any problem. + } else { + // From does not always fit in To, resort to a dynamic check. + if (from < (T::min)() || from > (T::max)()) { + // outside range. + ec = 1; + return {}; + } + } + return static_cast<To>(from); +} + +/** + * converts From to To, without loss. If the dynamic value of from + * can't be converted to To without loss, ec is set. + */ +template <typename To, typename From, + FMT_ENABLE_IF(!std::is_same<From, To>::value && + std::numeric_limits<From>::is_signed != + std::numeric_limits<To>::is_signed)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + using F = std::numeric_limits<From>; + using T = std::numeric_limits<To>; + static_assert(F::is_integer, "From must be integral"); + static_assert(T::is_integer, "To must be integral"); + + if (detail::const_check(F::is_signed && !T::is_signed)) { + // From may be negative, not allowed! + if (fmt::detail::is_negative(from)) { + ec = 1; + return {}; + } + // From is positive. Can it always fit in To? + if (detail::const_check(F::digits > T::digits) && + from > static_cast<From>(detail::max_value<To>())) { + ec = 1; + return {}; + } + } + + if (detail::const_check(!F::is_signed && T::is_signed && + F::digits >= T::digits) && + from > static_cast<From>(detail::max_value<To>())) { + ec = 1; + return {}; + } + return static_cast<To>(from); // Lossless conversion. +} + +template <typename To, typename From, + FMT_ENABLE_IF(std::is_same<From, To>::value)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + return from; +} // function + +// clang-format off +/** + * converts From to To if possible, otherwise ec is set. + * + * input | output + * ---------------------------------|--------------- + * NaN | NaN + * Inf | Inf + * normal, fits in output | converted (possibly lossy) + * normal, does not fit in output | ec is set + * subnormal | best effort + * -Inf | -Inf + */ +// clang-format on +template <typename To, typename From, + FMT_ENABLE_IF(!std::is_same<From, To>::value)> +FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { + ec = 0; + using T = std::numeric_limits<To>; + static_assert(std::is_floating_point<From>::value, "From must be floating"); + static_assert(std::is_floating_point<To>::value, "To must be floating"); + + // catch the only happy case + if (std::isfinite(from)) { + if (from >= T::lowest() && from <= (T::max)()) { + return static_cast<To>(from); + } + // not within range. + ec = 1; + return {}; + } + + // nan and inf will be preserved + return static_cast<To>(from); +} // function + +template <typename To, typename From, + FMT_ENABLE_IF(std::is_same<From, To>::value)> +FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { + ec = 0; + static_assert(std::is_floating_point<From>::value, "From must be floating"); + return from; +} + +/** + * safe duration cast between integral durations + */ +template <typename To, typename FromRep, typename FromPeriod, + FMT_ENABLE_IF(std::is_integral<FromRep>::value), + FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)> +To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from, + int& ec) { + using From = std::chrono::duration<FromRep, FromPeriod>; + ec = 0; + // the basic idea is that we need to convert from count() in the from type + // to count() in the To type, by multiplying it with this: + struct Factor + : std::ratio_divide<typename From::period, typename To::period> {}; + + static_assert(Factor::num > 0, "num must be positive"); + static_assert(Factor::den > 0, "den must be positive"); + + // the conversion is like this: multiply from.count() with Factor::num + // /Factor::den and convert it to To::rep, all this without + // overflow/underflow. let's start by finding a suitable type that can hold + // both To, From and Factor::num + using IntermediateRep = + typename std::common_type<typename From::rep, typename To::rep, + decltype(Factor::num)>::type; + + // safe conversion to IntermediateRep + IntermediateRep count = + lossless_integral_conversion<IntermediateRep>(from.count(), ec); + if (ec) return {}; + // multiply with Factor::num without overflow or underflow + if (detail::const_check(Factor::num != 1)) { + const auto max1 = detail::max_value<IntermediateRep>() / Factor::num; + if (count > max1) { + ec = 1; + return {}; + } + const auto min1 = + (std::numeric_limits<IntermediateRep>::min)() / Factor::num; + if (!std::is_unsigned<IntermediateRep>::value && count < min1) { + ec = 1; + return {}; + } + count *= Factor::num; + } + + if (detail::const_check(Factor::den != 1)) count /= Factor::den; + auto tocount = lossless_integral_conversion<typename To::rep>(count, ec); + return ec ? To() : To(tocount); +} + +/** + * safe duration_cast between floating point durations + */ +template <typename To, typename FromRep, typename FromPeriod, + FMT_ENABLE_IF(std::is_floating_point<FromRep>::value), + FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)> +To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from, + int& ec) { + using From = std::chrono::duration<FromRep, FromPeriod>; + ec = 0; + if (std::isnan(from.count())) { + // nan in, gives nan out. easy. + return To{std::numeric_limits<typename To::rep>::quiet_NaN()}; + } + // maybe we should also check if from is denormal, and decide what to do about + // it. + + // +-inf should be preserved. + if (std::isinf(from.count())) { + return To{from.count()}; + } + + // the basic idea is that we need to convert from count() in the from type + // to count() in the To type, by multiplying it with this: + struct Factor + : std::ratio_divide<typename From::period, typename To::period> {}; + + static_assert(Factor::num > 0, "num must be positive"); + static_assert(Factor::den > 0, "den must be positive"); + + // the conversion is like this: multiply from.count() with Factor::num + // /Factor::den and convert it to To::rep, all this without + // overflow/underflow. let's start by finding a suitable type that can hold + // both To, From and Factor::num + using IntermediateRep = + typename std::common_type<typename From::rep, typename To::rep, + decltype(Factor::num)>::type; + + // force conversion of From::rep -> IntermediateRep to be safe, + // even if it will never happen be narrowing in this context. + IntermediateRep count = + safe_float_conversion<IntermediateRep>(from.count(), ec); + if (ec) { + return {}; + } + + // multiply with Factor::num without overflow or underflow + if (detail::const_check(Factor::num != 1)) { + constexpr auto max1 = detail::max_value<IntermediateRep>() / + static_cast<IntermediateRep>(Factor::num); + if (count > max1) { + ec = 1; + return {}; + } + constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() / + static_cast<IntermediateRep>(Factor::num); + if (count < min1) { + ec = 1; + return {}; + } + count *= static_cast<IntermediateRep>(Factor::num); + } + + // this can't go wrong, right? den>0 is checked earlier. + if (detail::const_check(Factor::den != 1)) { + using common_t = typename std::common_type<IntermediateRep, intmax_t>::type; + count /= static_cast<common_t>(Factor::den); + } + + // convert to the to type, safely + using ToRep = typename To::rep; + + const ToRep tocount = safe_float_conversion<ToRep>(count, ec); + if (ec) { + return {}; + } + return To{tocount}; +} +} // namespace safe_duration_cast +#endif + +// Prevents expansion of a preceding token as a function-style macro. +// Usage: f FMT_NOMACRO() +#define FMT_NOMACRO + +namespace detail { +template <typename T = void> struct null {}; +inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } +inline null<> localtime_s(...) { return null<>(); } +inline null<> gmtime_r(...) { return null<>(); } +inline null<> gmtime_s(...) { return null<>(); } + +inline const std::locale& get_classic_locale() { + static const auto& locale = std::locale::classic(); + return locale; +} + +template <typename CodeUnit> struct codecvt_result { + static constexpr const size_t max_size = 32; + CodeUnit buf[max_size]; + CodeUnit* end; +}; +template <typename CodeUnit> +constexpr const size_t codecvt_result<CodeUnit>::max_size; + +template <typename CodeUnit> +void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf, + const std::locale& loc) { +#if FMT_CLANG_VERSION +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated" + auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc); +# pragma clang diagnostic pop +#else + auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc); +#endif + auto mb = std::mbstate_t(); + const char* from_next = nullptr; + auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next, + std::begin(out.buf), std::end(out.buf), out.end); + if (result != std::codecvt_base::ok) + FMT_THROW(format_error("failed to format time")); +} + +template <typename OutputIt> +auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) + -> OutputIt { + if (detail::is_utf8() && loc != get_classic_locale()) { + // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and + // gcc-4. +#if FMT_MSC_VERSION != 0 || \ + (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) + // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 + // and newer. + using code_unit = wchar_t; +#else + using code_unit = char32_t; +#endif + + using unit_t = codecvt_result<code_unit>; + unit_t unit; + write_codecvt(unit, in, loc); + // In UTF-8 is used one to four one-byte code units. + auto&& buf = basic_memory_buffer<char, unit_t::max_size * 4>(); + for (code_unit* p = unit.buf; p != unit.end; ++p) { + uint32_t c = static_cast<uint32_t>(*p); + if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) { + // surrogate pair + ++p; + if (p == unit.end || (c & 0xfc00) != 0xd800 || + (*p & 0xfc00) != 0xdc00) { + FMT_THROW(format_error("failed to format time")); + } + c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00; + } + if (c < 0x80) { + buf.push_back(static_cast<char>(c)); + } else if (c < 0x800) { + buf.push_back(static_cast<char>(0xc0 | (c >> 6))); + buf.push_back(static_cast<char>(0x80 | (c & 0x3f))); + } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { + buf.push_back(static_cast<char>(0xe0 | (c >> 12))); + buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast<char>(0x80 | (c & 0x3f))); + } else if (c >= 0x10000 && c <= 0x10ffff) { + buf.push_back(static_cast<char>(0xf0 | (c >> 18))); + buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12))); + buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast<char>(0x80 | (c & 0x3f))); + } else { + FMT_THROW(format_error("failed to format time")); + } + } + return copy_str<char>(buf.data(), buf.data() + buf.size(), out); + } + return copy_str<char>(in.data(), in.data() + in.size(), out); +} + +template <typename Char, typename OutputIt, + FMT_ENABLE_IF(!std::is_same<Char, char>::value)> +auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) + -> OutputIt { + codecvt_result<Char> unit; + write_codecvt(unit, sv, loc); + return copy_str<Char>(unit.buf, unit.end, out); +} + +template <typename Char, typename OutputIt, + FMT_ENABLE_IF(std::is_same<Char, char>::value)> +auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) + -> OutputIt { + return write_encoded_tm_str(out, sv, loc); +} + +template <typename Char> +inline void do_write(buffer<Char>& buf, const std::tm& time, + const std::locale& loc, char format, char modifier) { + auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf); + auto&& os = std::basic_ostream<Char>(&format_buf); + os.imbue(loc); + using iterator = std::ostreambuf_iterator<Char>; + const auto& facet = std::use_facet<std::time_put<Char, iterator>>(loc); + auto end = facet.put(os, os, Char(' '), &time, format, modifier); + if (end.failed()) FMT_THROW(format_error("failed to format time")); +} + +template <typename Char, typename OutputIt, + FMT_ENABLE_IF(!std::is_same<Char, char>::value)> +auto write(OutputIt out, const std::tm& time, const std::locale& loc, + char format, char modifier = 0) -> OutputIt { + auto&& buf = get_buffer<Char>(out); + do_write<Char>(buf, time, loc, format, modifier); + return buf.out(); +} + +template <typename Char, typename OutputIt, + FMT_ENABLE_IF(std::is_same<Char, char>::value)> +auto write(OutputIt out, const std::tm& time, const std::locale& loc, + char format, char modifier = 0) -> OutputIt { + auto&& buf = basic_memory_buffer<Char>(); + do_write<char>(buf, time, loc, format, modifier); + return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); +} + +} // namespace detail + +FMT_MODULE_EXPORT_BEGIN + +/** + Converts given time since epoch as ``std::time_t`` value into calendar time, + expressed in local time. Unlike ``std::localtime``, this function is + thread-safe on most platforms. + */ +inline std::tm localtime(std::time_t time) { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + dispatcher(std::time_t t) : time_(t) {} + + bool run() { + using namespace fmt::detail; + return handle(localtime_r(&time_, &tm_)); + } + + bool handle(std::tm* tm) { return tm != nullptr; } + + bool handle(detail::null<>) { + using namespace fmt::detail; + return fallback(localtime_s(&tm_, &time_)); + } + + bool fallback(int res) { return res == 0; } + +#if !FMT_MSC_VERSION + bool fallback(detail::null<>) { + using namespace fmt::detail; + std::tm* tm = std::localtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } +#endif + }; + dispatcher lt(time); + // Too big time values may be unsupported. + if (!lt.run()) FMT_THROW(format_error("time_t value out of range")); + return lt.tm_; +} + +inline std::tm localtime( + std::chrono::time_point<std::chrono::system_clock> time_point) { + return localtime(std::chrono::system_clock::to_time_t(time_point)); +} + +/** + Converts given time since epoch as ``std::time_t`` value into calendar time, + expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this + function is thread-safe on most platforms. + */ +inline std::tm gmtime(std::time_t time) { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + dispatcher(std::time_t t) : time_(t) {} + + bool run() { + using namespace fmt::detail; + return handle(gmtime_r(&time_, &tm_)); + } + + bool handle(std::tm* tm) { return tm != nullptr; } + + bool handle(detail::null<>) { + using namespace fmt::detail; + return fallback(gmtime_s(&tm_, &time_)); + } + + bool fallback(int res) { return res == 0; } + +#if !FMT_MSC_VERSION + bool fallback(detail::null<>) { + std::tm* tm = std::gmtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } +#endif + }; + dispatcher gt(time); + // Too big time values may be unsupported. + if (!gt.run()) FMT_THROW(format_error("time_t value out of range")); + return gt.tm_; +} + +inline std::tm gmtime( + std::chrono::time_point<std::chrono::system_clock> time_point) { + return gmtime(std::chrono::system_clock::to_time_t(time_point)); +} + +FMT_BEGIN_DETAIL_NAMESPACE + +// Writes two-digit numbers a, b and c separated by sep to buf. +// The method by Pavel Novikov based on +// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/. +inline void write_digit2_separated(char* buf, unsigned a, unsigned b, + unsigned c, char sep) { + unsigned long long digits = + a | (b << 24) | (static_cast<unsigned long long>(c) << 48); + // Convert each value to BCD. + // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b. + // The difference is + // y - x = a * 6 + // a can be found from x: + // a = floor(x / 10) + // then + // y = x + a * 6 = x + floor(x / 10) * 6 + // floor(x / 10) is (x * 205) >> 11 (needs 16 bits). + digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6; + // Put low nibbles to high bytes and high nibbles to low bytes. + digits = ((digits & 0x00f00000f00000f0) >> 4) | + ((digits & 0x000f00000f00000f) << 8); + auto usep = static_cast<unsigned long long>(sep); + // Add ASCII '0' to each digit byte and insert separators. + digits |= 0x3030003030003030 | (usep << 16) | (usep << 40); + + constexpr const size_t len = 8; + if (const_check(is_big_endian())) { + char tmp[len]; + std::memcpy(tmp, &digits, len); + std::reverse_copy(tmp, tmp + len, buf); + } else { + std::memcpy(buf, &digits, len); + } +} + +template <typename Period> FMT_CONSTEXPR inline const char* get_units() { + if (std::is_same<Period, std::atto>::value) return "as"; + if (std::is_same<Period, std::femto>::value) return "fs"; + if (std::is_same<Period, std::pico>::value) return "ps"; + if (std::is_same<Period, std::nano>::value) return "ns"; + if (std::is_same<Period, std::micro>::value) return "µs"; + if (std::is_same<Period, std::milli>::value) return "ms"; + if (std::is_same<Period, std::centi>::value) return "cs"; + if (std::is_same<Period, std::deci>::value) return "ds"; + if (std::is_same<Period, std::ratio<1>>::value) return "s"; + if (std::is_same<Period, std::deca>::value) return "das"; + if (std::is_same<Period, std::hecto>::value) return "hs"; + if (std::is_same<Period, std::kilo>::value) return "ks"; + if (std::is_same<Period, std::mega>::value) return "Ms"; + if (std::is_same<Period, std::giga>::value) return "Gs"; + if (std::is_same<Period, std::tera>::value) return "Ts"; + if (std::is_same<Period, std::peta>::value) return "Ps"; + if (std::is_same<Period, std::exa>::value) return "Es"; + if (std::is_same<Period, std::ratio<60>>::value) return "m"; + if (std::is_same<Period, std::ratio<3600>>::value) return "h"; + return nullptr; +} + +enum class numeric_system { + standard, + // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale. + alternative +}; + +// Parses a put_time-like format string and invokes handler actions. +template <typename Char, typename Handler> +FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, + const Char* end, + Handler&& handler) { + auto ptr = begin; + while (ptr != end) { + auto c = *ptr; + if (c == '}') break; + if (c != '%') { + ++ptr; + continue; + } + if (begin != ptr) handler.on_text(begin, ptr); + ++ptr; // consume '%' + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case '%': + handler.on_text(ptr - 1, ptr); + break; + case 'n': { + const Char newline[] = {'\n'}; + handler.on_text(newline, newline + 1); + break; + } + case 't': { + const Char tab[] = {'\t'}; + handler.on_text(tab, tab + 1); + break; + } + // Year: + case 'Y': + handler.on_year(numeric_system::standard); + break; + case 'y': + handler.on_short_year(numeric_system::standard); + break; + case 'C': + handler.on_century(numeric_system::standard); + break; + case 'G': + handler.on_iso_week_based_year(); + break; + case 'g': + handler.on_iso_week_based_short_year(); + break; + // Day of the week: + case 'a': + handler.on_abbr_weekday(); + break; + case 'A': + handler.on_full_weekday(); + break; + case 'w': + handler.on_dec0_weekday(numeric_system::standard); + break; + case 'u': + handler.on_dec1_weekday(numeric_system::standard); + break; + // Month: + case 'b': + case 'h': + handler.on_abbr_month(); + break; + case 'B': + handler.on_full_month(); + break; + case 'm': + handler.on_dec_month(numeric_system::standard); + break; + // Day of the year/month: + case 'U': + handler.on_dec0_week_of_year(numeric_system::standard); + break; + case 'W': + handler.on_dec1_week_of_year(numeric_system::standard); + break; + case 'V': + handler.on_iso_week_of_year(numeric_system::standard); + break; + case 'j': + handler.on_day_of_year(); + break; + case 'd': + handler.on_day_of_month(numeric_system::standard); + break; + case 'e': + handler.on_day_of_month_space(numeric_system::standard); + break; + // Hour, minute, second: + case 'H': + handler.on_24_hour(numeric_system::standard); + break; + case 'I': + handler.on_12_hour(numeric_system::standard); + break; + case 'M': + handler.on_minute(numeric_system::standard); + break; + case 'S': + handler.on_second(numeric_system::standard); + break; + // Other: + case 'c': + handler.on_datetime(numeric_system::standard); + break; + case 'x': + handler.on_loc_date(numeric_system::standard); + break; + case 'X': + handler.on_loc_time(numeric_system::standard); + break; + case 'D': + handler.on_us_date(); + break; + case 'F': + handler.on_iso_date(); + break; + case 'r': + handler.on_12_hour_time(); + break; + case 'R': + handler.on_24_hour_time(); + break; + case 'T': + handler.on_iso_time(); + break; + case 'p': + handler.on_am_pm(); + break; + case 'Q': + handler.on_duration_value(); + break; + case 'q': + handler.on_duration_unit(); + break; + case 'z': + handler.on_utc_offset(); + break; + case 'Z': + handler.on_tz_name(); + break; + // Alternative representation: + case 'E': { + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case 'Y': + handler.on_year(numeric_system::alternative); + break; + case 'y': + handler.on_offset_year(); + break; + case 'C': + handler.on_century(numeric_system::alternative); + break; + case 'c': + handler.on_datetime(numeric_system::alternative); + break; + case 'x': + handler.on_loc_date(numeric_system::alternative); + break; + case 'X': + handler.on_loc_time(numeric_system::alternative); + break; + default: + FMT_THROW(format_error("invalid format")); + } + break; + } + case 'O': + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case 'y': + handler.on_short_year(numeric_system::alternative); + break; + case 'm': + handler.on_dec_month(numeric_system::alternative); + break; + case 'U': + handler.on_dec0_week_of_year(numeric_system::alternative); + break; + case 'W': + handler.on_dec1_week_of_year(numeric_system::alternative); + break; + case 'V': + handler.on_iso_week_of_year(numeric_system::alternative); + break; + case 'd': + handler.on_day_of_month(numeric_system::alternative); + break; + case 'e': + handler.on_day_of_month_space(numeric_system::alternative); + break; + case 'w': + handler.on_dec0_weekday(numeric_system::alternative); + break; + case 'u': + handler.on_dec1_weekday(numeric_system::alternative); + break; + case 'H': + handler.on_24_hour(numeric_system::alternative); + break; + case 'I': + handler.on_12_hour(numeric_system::alternative); + break; + case 'M': + handler.on_minute(numeric_system::alternative); + break; + case 'S': + handler.on_second(numeric_system::alternative); + break; + default: + FMT_THROW(format_error("invalid format")); + } + break; + default: + FMT_THROW(format_error("invalid format")); + } + begin = ptr; + } + if (begin != ptr) handler.on_text(begin, ptr); + return ptr; +} + +template <typename Derived> struct null_chrono_spec_handler { + FMT_CONSTEXPR void unsupported() { + static_cast<Derived*>(this)->unsupported(); + } + FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_offset_year() { unsupported(); } + FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); } + FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); } + FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); } + FMT_CONSTEXPR void on_full_weekday() { unsupported(); } + FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_abbr_month() { unsupported(); } + FMT_CONSTEXPR void on_full_month() { unsupported(); } + FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_day_of_year() { unsupported(); } + FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_us_date() { unsupported(); } + FMT_CONSTEXPR void on_iso_date() { unsupported(); } + FMT_CONSTEXPR void on_12_hour_time() { unsupported(); } + FMT_CONSTEXPR void on_24_hour_time() { unsupported(); } + FMT_CONSTEXPR void on_iso_time() { unsupported(); } + FMT_CONSTEXPR void on_am_pm() { unsupported(); } + FMT_CONSTEXPR void on_duration_value() { unsupported(); } + FMT_CONSTEXPR void on_duration_unit() { unsupported(); } + FMT_CONSTEXPR void on_utc_offset() { unsupported(); } + FMT_CONSTEXPR void on_tz_name() { unsupported(); } +}; + +struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> { + FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); } + + template <typename Char> + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + FMT_CONSTEXPR void on_year(numeric_system) {} + FMT_CONSTEXPR void on_short_year(numeric_system) {} + FMT_CONSTEXPR void on_offset_year() {} + FMT_CONSTEXPR void on_century(numeric_system) {} + FMT_CONSTEXPR void on_iso_week_based_year() {} + FMT_CONSTEXPR void on_iso_week_based_short_year() {} + FMT_CONSTEXPR void on_abbr_weekday() {} + FMT_CONSTEXPR void on_full_weekday() {} + FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {} + FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {} + FMT_CONSTEXPR void on_abbr_month() {} + FMT_CONSTEXPR void on_full_month() {} + FMT_CONSTEXPR void on_dec_month(numeric_system) {} + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_day_of_year() {} + FMT_CONSTEXPR void on_day_of_month(numeric_system) {} + FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {} + FMT_CONSTEXPR void on_24_hour(numeric_system) {} + FMT_CONSTEXPR void on_12_hour(numeric_system) {} + FMT_CONSTEXPR void on_minute(numeric_system) {} + FMT_CONSTEXPR void on_second(numeric_system) {} + FMT_CONSTEXPR void on_datetime(numeric_system) {} + FMT_CONSTEXPR void on_loc_date(numeric_system) {} + FMT_CONSTEXPR void on_loc_time(numeric_system) {} + FMT_CONSTEXPR void on_us_date() {} + FMT_CONSTEXPR void on_iso_date() {} + FMT_CONSTEXPR void on_12_hour_time() {} + FMT_CONSTEXPR void on_24_hour_time() {} + FMT_CONSTEXPR void on_iso_time() {} + FMT_CONSTEXPR void on_am_pm() {} + FMT_CONSTEXPR void on_utc_offset() {} + FMT_CONSTEXPR void on_tz_name() {} +}; + +inline const char* tm_wday_full_name(int wday) { + static constexpr const char* full_name_list[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday"}; + return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?"; +} +inline const char* tm_wday_short_name(int wday) { + static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"}; + return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???"; +} + +inline const char* tm_mon_full_name(int mon) { + static constexpr const char* full_name_list[] = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"}; + return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?"; +} +inline const char* tm_mon_short_name(int mon) { + static constexpr const char* short_name_list[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???"; +} + +template <typename T, typename = void> +struct has_member_data_tm_gmtoff : std::false_type {}; +template <typename T> +struct has_member_data_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>> + : std::true_type {}; + +template <typename T, typename = void> +struct has_member_data_tm_zone : std::false_type {}; +template <typename T> +struct has_member_data_tm_zone<T, void_t<decltype(T::tm_zone)>> + : std::true_type {}; + +#if FMT_USE_TZSET +inline void tzset_once() { + static bool init = []() -> bool { + _tzset(); + return true; + }(); + ignore_unused(init); +} +#endif + +template <typename OutputIt, typename Char> class tm_writer { + private: + static constexpr int days_per_week = 7; + + const std::locale& loc_; + const bool is_classic_; + OutputIt out_; + const std::tm& tm_; + + auto tm_sec() const noexcept -> int { + FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, ""); + return tm_.tm_sec; + } + auto tm_min() const noexcept -> int { + FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, ""); + return tm_.tm_min; + } + auto tm_hour() const noexcept -> int { + FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, ""); + return tm_.tm_hour; + } + auto tm_mday() const noexcept -> int { + FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, ""); + return tm_.tm_mday; + } + auto tm_mon() const noexcept -> int { + FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, ""); + return tm_.tm_mon; + } + auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; } + auto tm_wday() const noexcept -> int { + FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, ""); + return tm_.tm_wday; + } + auto tm_yday() const noexcept -> int { + FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, ""); + return tm_.tm_yday; + } + + auto tm_hour12() const noexcept -> int { + const auto h = tm_hour(); + const auto z = h < 12 ? h : h - 12; + return z == 0 ? 12 : z; + } + + // POSIX and the C Standard are unclear or inconsistent about what %C and %y + // do if the year is negative or exceeds 9999. Use the convention that %C + // concatenated with %y yields the same output as %Y, and that %Y contains at + // least 4 characters, with more only if necessary. + auto split_year_lower(long long year) const noexcept -> int { + auto l = year % 100; + if (l < 0) l = -l; // l in [0, 99] + return static_cast<int>(l); + } + + // Algorithm: + // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date + auto iso_year_weeks(long long curr_year) const noexcept -> int { + const auto prev_year = curr_year - 1; + const auto curr_p = + (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % + days_per_week; + const auto prev_p = + (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % + days_per_week; + return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0); + } + auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int { + return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) / + days_per_week; + } + auto tm_iso_week_year() const noexcept -> long long { + const auto year = tm_year(); + const auto w = iso_week_num(tm_yday(), tm_wday()); + if (w < 1) return year - 1; + if (w > iso_year_weeks(year)) return year + 1; + return year; + } + auto tm_iso_week_of_year() const noexcept -> int { + const auto year = tm_year(); + const auto w = iso_week_num(tm_yday(), tm_wday()); + if (w < 1) return iso_year_weeks(year - 1); + if (w > iso_year_weeks(year)) return 1; + return w; + } + + void write1(int value) { + *out_++ = static_cast<char>('0' + to_unsigned(value) % 10); + } + void write2(int value) { + const char* d = digits2(to_unsigned(value) % 100); + *out_++ = *d++; + *out_++ = *d; + } + + void write_year_extended(long long year) { + // At least 4 characters. + int width = 4; + if (year < 0) { + *out_++ = '-'; + year = 0 - year; + --width; + } + uint32_or_64_or_128_t<long long> n = to_unsigned(year); + const int num_digits = count_digits(n); + if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0'); + out_ = format_decimal<Char>(out_, n, num_digits).end; + } + void write_year(long long year) { + if (year >= 0 && year < 10000) { + write2(static_cast<int>(year / 100)); + write2(static_cast<int>(year % 100)); + } else { + write_year_extended(year); + } + } + + void write_utc_offset(long offset) { + if (offset < 0) { + *out_++ = '-'; + offset = -offset; + } else { + *out_++ = '+'; + } + offset /= 60; + write2(static_cast<int>(offset / 60)); + write2(static_cast<int>(offset % 60)); + } + template <typename T, FMT_ENABLE_IF(has_member_data_tm_gmtoff<T>::value)> + void format_utc_offset_impl(const T& tm) { + write_utc_offset(tm.tm_gmtoff); + } + template <typename T, FMT_ENABLE_IF(!has_member_data_tm_gmtoff<T>::value)> + void format_utc_offset_impl(const T& tm) { +#if defined(_WIN32) && defined(_UCRT) +# if FMT_USE_TZSET + tzset_once(); +# endif + long offset = 0; + _get_timezone(&offset); + if (tm.tm_isdst) { + long dstbias = 0; + _get_dstbias(&dstbias); + offset += dstbias; + } + write_utc_offset(-offset); +#else + ignore_unused(tm); + format_localized('z'); +#endif + } + + template <typename T, FMT_ENABLE_IF(has_member_data_tm_zone<T>::value)> + void format_tz_name_impl(const T& tm) { + if (is_classic_) + out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_); + else + format_localized('Z'); + } + template <typename T, FMT_ENABLE_IF(!has_member_data_tm_zone<T>::value)> + void format_tz_name_impl(const T&) { + format_localized('Z'); + } + + void format_localized(char format, char modifier = 0) { + out_ = write<Char>(out_, tm_, loc_, format, modifier); + } + + public: + tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm) + : loc_(loc), + is_classic_(loc_ == get_classic_locale()), + out_(out), + tm_(tm) {} + + OutputIt out() const { return out_; } + + FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { + out_ = copy_str<Char>(begin, end, out_); + } + + void on_abbr_weekday() { + if (is_classic_) + out_ = write(out_, tm_wday_short_name(tm_wday())); + else + format_localized('a'); + } + void on_full_weekday() { + if (is_classic_) + out_ = write(out_, tm_wday_full_name(tm_wday())); + else + format_localized('A'); + } + void on_dec0_weekday(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday()); + format_localized('w', 'O'); + } + void on_dec1_weekday(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto wday = tm_wday(); + write1(wday == 0 ? days_per_week : wday); + } else { + format_localized('u', 'O'); + } + } + + void on_abbr_month() { + if (is_classic_) + out_ = write(out_, tm_mon_short_name(tm_mon())); + else + format_localized('b'); + } + void on_full_month() { + if (is_classic_) + out_ = write(out_, tm_mon_full_name(tm_mon())); + else + format_localized('B'); + } + + void on_datetime(numeric_system ns) { + if (is_classic_) { + on_abbr_weekday(); + *out_++ = ' '; + on_abbr_month(); + *out_++ = ' '; + on_day_of_month_space(numeric_system::standard); + *out_++ = ' '; + on_iso_time(); + *out_++ = ' '; + on_year(numeric_system::standard); + } else { + format_localized('c', ns == numeric_system::standard ? '\0' : 'E'); + } + } + void on_loc_date(numeric_system ns) { + if (is_classic_) + on_us_date(); + else + format_localized('x', ns == numeric_system::standard ? '\0' : 'E'); + } + void on_loc_time(numeric_system ns) { + if (is_classic_) + on_iso_time(); + else + format_localized('X', ns == numeric_system::standard ? '\0' : 'E'); + } + void on_us_date() { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_mon() + 1), + to_unsigned(tm_mday()), + to_unsigned(split_year_lower(tm_year())), '/'); + out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_); + } + void on_iso_date() { + auto year = tm_year(); + char buf[10]; + size_t offset = 0; + if (year >= 0 && year < 10000) { + copy2(buf, digits2(static_cast<size_t>(year / 100))); + } else { + offset = 4; + write_year_extended(year); + year = 0; + } + write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100), + to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), + '-'); + out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_); + } + + void on_utc_offset() { format_utc_offset_impl(tm_); } + void on_tz_name() { format_tz_name_impl(tm_); } + + void on_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write_year(tm_year()); + format_localized('Y', 'E'); + } + void on_short_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(split_year_lower(tm_year())); + format_localized('y', 'O'); + } + void on_offset_year() { + if (is_classic_) return write2(split_year_lower(tm_year())); + format_localized('y', 'E'); + } + + void on_century(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto year = tm_year(); + auto upper = year / 100; + if (year >= -99 && year < 0) { + // Zero upper on negative year. + *out_++ = '-'; + *out_++ = '0'; + } else if (upper >= 0 && upper < 100) { + write2(static_cast<int>(upper)); + } else { + out_ = write<Char>(out_, upper); + } + } else { + format_localized('C', 'E'); + } + } + + void on_dec_month(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_mon() + 1); + format_localized('m', 'O'); + } + + void on_dec0_week_of_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week); + format_localized('U', 'O'); + } + void on_dec1_week_of_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto wday = tm_wday(); + write2((tm_yday() + days_per_week - + (wday == 0 ? (days_per_week - 1) : (wday - 1))) / + days_per_week); + } else { + format_localized('W', 'O'); + } + } + void on_iso_week_of_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_iso_week_of_year()); + format_localized('V', 'O'); + } + + void on_iso_week_based_year() { write_year(tm_iso_week_year()); } + void on_iso_week_based_short_year() { + write2(split_year_lower(tm_iso_week_year())); + } + + void on_day_of_year() { + auto yday = tm_yday() + 1; + write1(yday / 100); + write2(yday % 100); + } + void on_day_of_month(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday()); + format_localized('d', 'O'); + } + void on_day_of_month_space(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto mday = to_unsigned(tm_mday()) % 100; + const char* d2 = digits2(mday); + *out_++ = mday < 10 ? ' ' : d2[0]; + *out_++ = d2[1]; + } else { + format_localized('e', 'O'); + } + } + + void on_24_hour(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour()); + format_localized('H', 'O'); + } + void on_12_hour(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_hour12()); + format_localized('I', 'O'); + } + void on_minute(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_min()); + format_localized('M', 'O'); + } + void on_second(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec()); + format_localized('S', 'O'); + } + + void on_12_hour_time() { + if (is_classic_) { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_hour12()), + to_unsigned(tm_min()), to_unsigned(tm_sec()), ':'); + out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_); + *out_++ = ' '; + on_am_pm(); + } else { + format_localized('r'); + } + } + void on_24_hour_time() { + write2(tm_hour()); + *out_++ = ':'; + write2(tm_min()); + } + void on_iso_time() { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_hour()), to_unsigned(tm_min()), + to_unsigned(tm_sec()), ':'); + out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_); + } + + void on_am_pm() { + if (is_classic_) { + *out_++ = tm_hour() < 12 ? 'A' : 'P'; + *out_++ = 'M'; + } else { + format_localized('p'); + } + } + + // These apply to chrono durations but not tm. + void on_duration_value() {} + void on_duration_unit() {} +}; + +struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> { + FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); } + + template <typename Char> + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + FMT_CONSTEXPR void on_24_hour(numeric_system) {} + FMT_CONSTEXPR void on_12_hour(numeric_system) {} + FMT_CONSTEXPR void on_minute(numeric_system) {} + FMT_CONSTEXPR void on_second(numeric_system) {} + FMT_CONSTEXPR void on_12_hour_time() {} + FMT_CONSTEXPR void on_24_hour_time() {} + FMT_CONSTEXPR void on_iso_time() {} + FMT_CONSTEXPR void on_am_pm() {} + FMT_CONSTEXPR void on_duration_value() {} + FMT_CONSTEXPR void on_duration_unit() {} +}; + +template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> +inline bool isfinite(T) { + return true; +} + +// Converts value to Int and checks that it's in the range [0, upper). +template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)> +inline Int to_nonnegative_int(T value, Int upper) { + FMT_ASSERT(std::is_unsigned<Int>::value || + (value >= 0 && to_unsigned(value) <= to_unsigned(upper)), + "invalid value"); + (void)upper; + return static_cast<Int>(value); +} +template <typename T, typename Int, FMT_ENABLE_IF(!std::is_integral<T>::value)> +inline Int to_nonnegative_int(T value, Int upper) { + if (value < 0 || value > static_cast<T>(upper)) + FMT_THROW(format_error("invalid value")); + return static_cast<Int>(value); +} + +template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> +inline T mod(T x, int y) { + return x % static_cast<T>(y); +} +template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> +inline T mod(T x, int y) { + return std::fmod(x, static_cast<T>(y)); +} + +// If T is an integral type, maps T to its unsigned counterpart, otherwise +// leaves it unchanged (unlike std::make_unsigned). +template <typename T, bool INTEGRAL = std::is_integral<T>::value> +struct make_unsigned_or_unchanged { + using type = T; +}; + +template <typename T> struct make_unsigned_or_unchanged<T, true> { + using type = typename std::make_unsigned<T>::type; +}; + +#if FMT_SAFE_DURATION_CAST +// throwing version of safe_duration_cast +template <typename To, typename FromRep, typename FromPeriod> +To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) { + int ec; + To to = safe_duration_cast::safe_duration_cast<To>(from, ec); + if (ec) FMT_THROW(format_error("cannot format duration")); + return to; +} +#endif + +template <typename Rep, typename Period, + FMT_ENABLE_IF(std::is_integral<Rep>::value)> +inline std::chrono::duration<Rep, std::milli> get_milliseconds( + std::chrono::duration<Rep, Period> d) { + // this may overflow and/or the result may not fit in the + // target type. +#if FMT_SAFE_DURATION_CAST + using CommonSecondsType = + typename std::common_type<decltype(d), std::chrono::seconds>::type; + const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d); + const auto d_as_whole_seconds = + fmt_safe_duration_cast<std::chrono::seconds>(d_as_common); + // this conversion should be nonproblematic + const auto diff = d_as_common - d_as_whole_seconds; + const auto ms = + fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff); + return ms; +#else + auto s = std::chrono::duration_cast<std::chrono::seconds>(d); + return std::chrono::duration_cast<std::chrono::milliseconds>(d - s); +#endif +} + +// Counts the number of fractional digits in the range [0, 18] according to the +// C++20 spec. If more than 18 fractional digits are required then returns 6 for +// microseconds precision. +template <long long Num, long long Den, int N = 0, + bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)> +struct count_fractional_digits { + static constexpr int value = + Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value; +}; + +// Base case that doesn't instantiate any more templates +// in order to avoid overflow. +template <long long Num, long long Den, int N> +struct count_fractional_digits<Num, Den, N, false> { + static constexpr int value = (Num % Den == 0) ? N : 6; +}; + +constexpr long long pow10(std::uint32_t n) { + return n == 0 ? 1 : 10 * pow10(n - 1); +} + +template <class Rep, class Period, + FMT_ENABLE_IF(std::numeric_limits<Rep>::is_signed)> +constexpr std::chrono::duration<Rep, Period> abs( + std::chrono::duration<Rep, Period> d) { + // We need to compare the duration using the count() method directly + // due to a compiler bug in clang-11 regarding the spaceship operator, + // when -Wzero-as-null-pointer-constant is enabled. + // In clang-12 the bug has been fixed. See + // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example: + // https://www.godbolt.org/z/Knbb5joYx. + return d.count() >= d.zero().count() ? d : -d; +} + +template <class Rep, class Period, + FMT_ENABLE_IF(!std::numeric_limits<Rep>::is_signed)> +constexpr std::chrono::duration<Rep, Period> abs( + std::chrono::duration<Rep, Period> d) { + return d; +} + +template <typename Char, typename Rep, typename OutputIt, + FMT_ENABLE_IF(std::is_integral<Rep>::value)> +OutputIt format_duration_value(OutputIt out, Rep val, int) { + return write<Char>(out, val); +} + +template <typename Char, typename Rep, typename OutputIt, + FMT_ENABLE_IF(std::is_floating_point<Rep>::value)> +OutputIt format_duration_value(OutputIt out, Rep val, int precision) { + auto specs = basic_format_specs<Char>(); + specs.precision = precision; + specs.type = precision >= 0 ? presentation_type::fixed_lower + : presentation_type::general_lower; + return write<Char>(out, val, specs); +} + +template <typename Char, typename OutputIt> +OutputIt copy_unit(string_view unit, OutputIt out, Char) { + return std::copy(unit.begin(), unit.end(), out); +} + +template <typename OutputIt> +OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) { + // This works when wchar_t is UTF-32 because units only contain characters + // that have the same representation in UTF-16 and UTF-32. + utf8_to_utf16 u(unit); + return std::copy(u.c_str(), u.c_str() + u.size(), out); +} + +template <typename Char, typename Period, typename OutputIt> +OutputIt format_duration_unit(OutputIt out) { + if (const char* unit = get_units<Period>()) + return copy_unit(string_view(unit), out, Char()); + *out++ = '['; + out = write<Char>(out, Period::num); + if (const_check(Period::den != 1)) { + *out++ = '/'; + out = write<Char>(out, Period::den); + } + *out++ = ']'; + *out++ = 's'; + return out; +} + +class get_locale { + private: + union { + std::locale locale_; + }; + bool has_locale_ = false; + + public: + get_locale(bool localized, locale_ref loc) : has_locale_(localized) { + if (localized) + ::new (&locale_) std::locale(loc.template get<std::locale>()); + } + ~get_locale() { + if (has_locale_) locale_.~locale(); + } + operator const std::locale&() const { + return has_locale_ ? locale_ : get_classic_locale(); + } +}; + +template <typename FormatContext, typename OutputIt, typename Rep, + typename Period> +struct chrono_formatter { + FormatContext& context; + OutputIt out; + int precision; + bool localized = false; + // rep is unsigned to avoid overflow. + using rep = + conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int), + unsigned, typename make_unsigned_or_unchanged<Rep>::type>; + rep val; + using seconds = std::chrono::duration<rep>; + seconds s; + using milliseconds = std::chrono::duration<rep, std::milli>; + bool negative; + + using char_type = typename FormatContext::char_type; + using tm_writer_type = tm_writer<OutputIt, char_type>; + + chrono_formatter(FormatContext& ctx, OutputIt o, + std::chrono::duration<Rep, Period> d) + : context(ctx), + out(o), + val(static_cast<rep>(d.count())), + negative(false) { + if (d.count() < 0) { + val = 0 - val; + negative = true; + } + + // this may overflow and/or the result may not fit in the + // target type. +#if FMT_SAFE_DURATION_CAST + // might need checked conversion (rep!=Rep) + auto tmpval = std::chrono::duration<rep, Period>(val); + s = fmt_safe_duration_cast<seconds>(tmpval); +#else + s = std::chrono::duration_cast<seconds>( + std::chrono::duration<rep, Period>(val)); +#endif + } + + // returns true if nan or inf, writes to out. + bool handle_nan_inf() { + if (isfinite(val)) { + return false; + } + if (isnan(val)) { + write_nan(); + return true; + } + // must be +-inf + if (val > 0) { + write_pinf(); + } else { + write_ninf(); + } + return true; + } + + Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); } + + Rep hour12() const { + Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12)); + return hour <= 0 ? 12 : hour; + } + + Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); } + Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); } + + std::tm time() const { + auto time = std::tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + time.tm_min = to_nonnegative_int(minute(), 60); + time.tm_sec = to_nonnegative_int(second(), 60); + return time; + } + + void write_sign() { + if (negative) { + *out++ = '-'; + negative = false; + } + } + + void write(Rep value, int width) { + write_sign(); + if (isnan(value)) return write_nan(); + uint32_or_64_or_128_t<int> n = + to_unsigned(to_nonnegative_int(value, max_value<int>())); + int num_digits = detail::count_digits(n); + if (width > num_digits) out = std::fill_n(out, width - num_digits, '0'); + out = format_decimal<char_type>(out, n, num_digits).end; + } + + template <typename Duration> void write_fractional_seconds(Duration d) { + FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, ""); + constexpr auto num_fractional_digits = + count_fractional_digits<Duration::period::num, + Duration::period::den>::value; + + using subsecond_precision = std::chrono::duration< + typename std::common_type<typename Duration::rep, + std::chrono::seconds::rep>::type, + std::ratio<1, detail::pow10(num_fractional_digits)>>; + if (std::ratio_less<typename subsecond_precision::period, + std::chrono::seconds::period>::value) { + *out++ = '.'; + auto fractional = + detail::abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d); + auto subseconds = + std::chrono::treat_as_floating_point< + typename subsecond_precision::rep>::value + ? fractional.count() + : std::chrono::duration_cast<subsecond_precision>(fractional) + .count(); + uint32_or_64_or_128_t<long long> n = + to_unsigned(to_nonnegative_int(subseconds, max_value<long long>())); + int num_digits = detail::count_digits(n); + if (num_fractional_digits > num_digits) + out = std::fill_n(out, num_fractional_digits - num_digits, '0'); + out = format_decimal<char_type>(out, n, num_digits).end; + } + } + + void write_nan() { std::copy_n("nan", 3, out); } + void write_pinf() { std::copy_n("inf", 3, out); } + void write_ninf() { std::copy_n("-inf", 4, out); } + + template <typename Callback, typename... Args> + void format_tm(const tm& time, Callback cb, Args... args) { + if (isnan(val)) return write_nan(); + get_locale loc(localized, context.locale()); + auto w = tm_writer_type(loc, out, time); + (w.*cb)(args...); + out = w.out(); + } + + void on_text(const char_type* begin, const char_type* end) { + std::copy(begin, end, out); + } + + // These are not implemented because durations don't have date information. + void on_abbr_weekday() {} + void on_full_weekday() {} + void on_dec0_weekday(numeric_system) {} + void on_dec1_weekday(numeric_system) {} + void on_abbr_month() {} + void on_full_month() {} + void on_datetime(numeric_system) {} + void on_loc_date(numeric_system) {} + void on_loc_time(numeric_system) {} + void on_us_date() {} + void on_iso_date() {} + void on_utc_offset() {} + void on_tz_name() {} + void on_year(numeric_system) {} + void on_short_year(numeric_system) {} + void on_offset_year() {} + void on_century(numeric_system) {} + void on_iso_week_based_year() {} + void on_iso_week_based_short_year() {} + void on_dec_month(numeric_system) {} + void on_dec0_week_of_year(numeric_system) {} + void on_dec1_week_of_year(numeric_system) {} + void on_iso_week_of_year(numeric_system) {} + void on_day_of_year() {} + void on_day_of_month(numeric_system) {} + void on_day_of_month_space(numeric_system) {} + + void on_24_hour(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour(), 2); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + format_tm(time, &tm_writer_type::on_24_hour, ns); + } + + void on_12_hour(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour12(), 2); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour12(), 12); + format_tm(time, &tm_writer_type::on_12_hour, ns); + } + + void on_minute(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(minute(), 2); + auto time = tm(); + time.tm_min = to_nonnegative_int(minute(), 60); + format_tm(time, &tm_writer_type::on_minute, ns); + } + + void on_second(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) { + if (std::is_floating_point<rep>::value) { + constexpr auto num_fractional_digits = + count_fractional_digits<Period::num, Period::den>::value; + auto buf = memory_buffer(); + format_to(std::back_inserter(buf), runtime("{:.{}f}"), + std::fmod(val * static_cast<rep>(Period::num) / + static_cast<rep>(Period::den), + static_cast<rep>(60)), + num_fractional_digits); + if (negative) *out++ = '-'; + if (buf.size() < 2 || buf[1] == '.') *out++ = '0'; + out = std::copy(buf.begin(), buf.end(), out); + } else { + write(second(), 2); + write_fractional_seconds(std::chrono::duration<rep, Period>(val)); + } + return; + } + auto time = tm(); + time.tm_sec = to_nonnegative_int(second(), 60); + format_tm(time, &tm_writer_type::on_second, ns); + } + + void on_12_hour_time() { + if (handle_nan_inf()) return; + format_tm(time(), &tm_writer_type::on_12_hour_time); + } + + void on_24_hour_time() { + if (handle_nan_inf()) { + *out++ = ':'; + handle_nan_inf(); + return; + } + + write(hour(), 2); + *out++ = ':'; + write(minute(), 2); + } + + void on_iso_time() { + on_24_hour_time(); + *out++ = ':'; + if (handle_nan_inf()) return; + on_second(numeric_system::standard); + } + + void on_am_pm() { + if (handle_nan_inf()) return; + format_tm(time(), &tm_writer_type::on_am_pm); + } + + void on_duration_value() { + if (handle_nan_inf()) return; + write_sign(); + out = format_duration_value<char_type>(out, val, precision); + } + + void on_duration_unit() { + out = format_duration_unit<char_type, Period>(out); + } +}; + +FMT_END_DETAIL_NAMESPACE + +#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 +using weekday = std::chrono::weekday; +#else +// A fallback version of weekday. +class weekday { + private: + unsigned char value; + + public: + weekday() = default; + explicit constexpr weekday(unsigned wd) noexcept + : value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {} + constexpr unsigned c_encoding() const noexcept { return value; } +}; + +class year_month_day {}; +#endif + +// A rudimentary weekday formatter. +template <typename Char> struct formatter<weekday, Char> { + private: + bool localized = false; + + public: + FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) + -> decltype(ctx.begin()) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin != end && *begin == 'L') { + ++begin; + localized = true; + } + return begin; + } + + template <typename FormatContext> + auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_wday = static_cast<int>(wd.c_encoding()); + detail::get_locale loc(localized, ctx.locale()); + auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time); + w.on_abbr_weekday(); + return w.out(); + } +}; + +template <typename Rep, typename Period, typename Char> +struct formatter<std::chrono::duration<Rep, Period>, Char> { + private: + basic_format_specs<Char> specs; + int precision = -1; + using arg_ref_type = detail::arg_ref<Char>; + arg_ref_type width_ref; + arg_ref_type precision_ref; + bool localized = false; + basic_string_view<Char> format_str; + using duration = std::chrono::duration<Rep, Period>; + + struct spec_handler { + formatter& f; + basic_format_parse_context<Char>& context; + basic_string_view<Char> format_str; + + template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) { + context.check_arg_id(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) { + context.check_arg_id(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) { + return arg_ref_type(context.next_arg_id()); + } + + void on_error(const char* msg) { FMT_THROW(format_error(msg)); } + FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) { + f.specs.fill = fill; + } + FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; } + FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; } + FMT_CONSTEXPR void on_precision(int _precision) { + f.precision = _precision; + } + FMT_CONSTEXPR void end_precision() {} + + template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { + f.width_ref = make_arg_ref(arg_id); + } + + template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { + f.precision_ref = make_arg_ref(arg_id); + } + }; + + using iterator = typename basic_format_parse_context<Char>::iterator; + struct parse_range { + iterator begin; + iterator end; + }; + + FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin == end || *begin == '}') return {begin, begin}; + spec_handler handler{*this, ctx, format_str}; + begin = detail::parse_align(begin, end, handler); + if (begin == end) return {begin, begin}; + begin = detail::parse_width(begin, end, handler); + if (begin == end) return {begin, begin}; + if (*begin == '.') { + if (std::is_floating_point<Rep>::value) + begin = detail::parse_precision(begin, end, handler); + else + handler.on_error("precision not allowed for this argument type"); + } + if (begin != end && *begin == 'L') { + ++begin; + localized = true; + } + end = detail::parse_chrono_format(begin, end, + detail::chrono_format_checker()); + return {begin, end}; + } + + public: + FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) + -> decltype(ctx.begin()) { + auto range = do_parse(ctx); + format_str = basic_string_view<Char>( + &*range.begin, detail::to_unsigned(range.end - range.begin)); + return range.end; + } + + template <typename FormatContext> + auto format(const duration& d, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto specs_copy = specs; + auto precision_copy = precision; + auto begin = format_str.begin(), end = format_str.end(); + // As a possible future optimization, we could avoid extra copying if width + // is not specified. + basic_memory_buffer<Char> buf; + auto out = std::back_inserter(buf); + detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width, + width_ref, ctx); + detail::handle_dynamic_spec<detail::precision_checker>(precision_copy, + precision_ref, ctx); + if (begin == end || *begin == '}') { + out = detail::format_duration_value<Char>(out, d.count(), precision_copy); + detail::format_duration_unit<Char, Period>(out); + } else { + detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f( + ctx, out, d); + f.precision = precision_copy; + f.localized = localized; + detail::parse_chrono_format(begin, end, f); + } + return detail::write( + ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy); + } +}; + +template <typename Char, typename Duration> +struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>, + Char> : formatter<std::tm, Char> { + FMT_CONSTEXPR formatter() { + basic_string_view<Char> default_specs = + detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{}; + this->do_parse(default_specs.begin(), default_specs.end()); + } + + template <typename FormatContext> + auto format(std::chrono::time_point<std::chrono::system_clock> val, + FormatContext& ctx) const -> decltype(ctx.out()) { + return formatter<std::tm, Char>::format(localtime(val), ctx); + } +}; + +template <typename Char> struct formatter<std::tm, Char> { + private: + enum class spec { + unknown, + year_month_day, + hh_mm_ss, + }; + spec spec_ = spec::unknown; + basic_string_view<Char> specs; + + protected: + template <typename It> FMT_CONSTEXPR auto do_parse(It begin, It end) -> It { + if (begin != end && *begin == ':') ++begin; + end = detail::parse_chrono_format(begin, end, detail::tm_format_checker()); + // Replace default spec only if the new spec is not empty. + if (end != begin) specs = {begin, detail::to_unsigned(end - begin)}; + return end; + } + + public: + FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) + -> decltype(ctx.begin()) { + auto end = this->do_parse(ctx.begin(), ctx.end()); + // basic_string_view<>::compare isn't constexpr before C++17. + if (specs.size() == 2 && specs[0] == Char('%')) { + if (specs[1] == Char('F')) + spec_ = spec::year_month_day; + else if (specs[1] == Char('T')) + spec_ = spec::hh_mm_ss; + } + return end; + } + + template <typename FormatContext> + auto format(const std::tm& tm, FormatContext& ctx) const + -> decltype(ctx.out()) { + const auto loc_ref = ctx.locale(); + detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref); + auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), tm); + if (spec_ == spec::year_month_day) + w.on_iso_date(); + else if (spec_ == spec::hh_mm_ss) + w.on_iso_time(); + else + detail::parse_chrono_format(specs.begin(), specs.end(), w); + return w.out(); + } +}; + +FMT_MODULE_EXPORT_END +FMT_END_NAMESPACE + +#endif // FMT_CHRONO_H_ diff --git a/src/fmt/include/fmt/color.h b/src/fmt/include/fmt/color.h new file mode 100644 index 000000000..4c163277e --- /dev/null +++ b/src/fmt/include/fmt/color.h @@ -0,0 +1,651 @@ +// Formatting library for C++ - color support +// +// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_COLOR_H_ +#define FMT_COLOR_H_ + +#include "format.h" + +FMT_BEGIN_NAMESPACE +FMT_MODULE_EXPORT_BEGIN + +enum class color : uint32_t { + alice_blue = 0xF0F8FF, // rgb(240,248,255) + antique_white = 0xFAEBD7, // rgb(250,235,215) + aqua = 0x00FFFF, // rgb(0,255,255) + aquamarine = 0x7FFFD4, // rgb(127,255,212) + azure = 0xF0FFFF, // rgb(240,255,255) + beige = 0xF5F5DC, // rgb(245,245,220) + bisque = 0xFFE4C4, // rgb(255,228,196) + black = 0x000000, // rgb(0,0,0) + blanched_almond = 0xFFEBCD, // rgb(255,235,205) + blue = 0x0000FF, // rgb(0,0,255) + blue_violet = 0x8A2BE2, // rgb(138,43,226) + brown = 0xA52A2A, // rgb(165,42,42) + burly_wood = 0xDEB887, // rgb(222,184,135) + cadet_blue = 0x5F9EA0, // rgb(95,158,160) + chartreuse = 0x7FFF00, // rgb(127,255,0) + chocolate = 0xD2691E, // rgb(210,105,30) + coral = 0xFF7F50, // rgb(255,127,80) + cornflower_blue = 0x6495ED, // rgb(100,149,237) + cornsilk = 0xFFF8DC, // rgb(255,248,220) + crimson = 0xDC143C, // rgb(220,20,60) + cyan = 0x00FFFF, // rgb(0,255,255) + dark_blue = 0x00008B, // rgb(0,0,139) + dark_cyan = 0x008B8B, // rgb(0,139,139) + dark_golden_rod = 0xB8860B, // rgb(184,134,11) + dark_gray = 0xA9A9A9, // rgb(169,169,169) + dark_green = 0x006400, // rgb(0,100,0) + dark_khaki = 0xBDB76B, // rgb(189,183,107) + dark_magenta = 0x8B008B, // rgb(139,0,139) + dark_olive_green = 0x556B2F, // rgb(85,107,47) + dark_orange = 0xFF8C00, // rgb(255,140,0) + dark_orchid = 0x9932CC, // rgb(153,50,204) + dark_red = 0x8B0000, // rgb(139,0,0) + dark_salmon = 0xE9967A, // rgb(233,150,122) + dark_sea_green = 0x8FBC8F, // rgb(143,188,143) + dark_slate_blue = 0x483D8B, // rgb(72,61,139) + dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) + dark_turquoise = 0x00CED1, // rgb(0,206,209) + dark_violet = 0x9400D3, // rgb(148,0,211) + deep_pink = 0xFF1493, // rgb(255,20,147) + deep_sky_blue = 0x00BFFF, // rgb(0,191,255) + dim_gray = 0x696969, // rgb(105,105,105) + dodger_blue = 0x1E90FF, // rgb(30,144,255) + fire_brick = 0xB22222, // rgb(178,34,34) + floral_white = 0xFFFAF0, // rgb(255,250,240) + forest_green = 0x228B22, // rgb(34,139,34) + fuchsia = 0xFF00FF, // rgb(255,0,255) + gainsboro = 0xDCDCDC, // rgb(220,220,220) + ghost_white = 0xF8F8FF, // rgb(248,248,255) + gold = 0xFFD700, // rgb(255,215,0) + golden_rod = 0xDAA520, // rgb(218,165,32) + gray = 0x808080, // rgb(128,128,128) + green = 0x008000, // rgb(0,128,0) + green_yellow = 0xADFF2F, // rgb(173,255,47) + honey_dew = 0xF0FFF0, // rgb(240,255,240) + hot_pink = 0xFF69B4, // rgb(255,105,180) + indian_red = 0xCD5C5C, // rgb(205,92,92) + indigo = 0x4B0082, // rgb(75,0,130) + ivory = 0xFFFFF0, // rgb(255,255,240) + khaki = 0xF0E68C, // rgb(240,230,140) + lavender = 0xE6E6FA, // rgb(230,230,250) + lavender_blush = 0xFFF0F5, // rgb(255,240,245) + lawn_green = 0x7CFC00, // rgb(124,252,0) + lemon_chiffon = 0xFFFACD, // rgb(255,250,205) + light_blue = 0xADD8E6, // rgb(173,216,230) + light_coral = 0xF08080, // rgb(240,128,128) + light_cyan = 0xE0FFFF, // rgb(224,255,255) + light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) + light_gray = 0xD3D3D3, // rgb(211,211,211) + light_green = 0x90EE90, // rgb(144,238,144) + light_pink = 0xFFB6C1, // rgb(255,182,193) + light_salmon = 0xFFA07A, // rgb(255,160,122) + light_sea_green = 0x20B2AA, // rgb(32,178,170) + light_sky_blue = 0x87CEFA, // rgb(135,206,250) + light_slate_gray = 0x778899, // rgb(119,136,153) + light_steel_blue = 0xB0C4DE, // rgb(176,196,222) + light_yellow = 0xFFFFE0, // rgb(255,255,224) + lime = 0x00FF00, // rgb(0,255,0) + lime_green = 0x32CD32, // rgb(50,205,50) + linen = 0xFAF0E6, // rgb(250,240,230) + magenta = 0xFF00FF, // rgb(255,0,255) + maroon = 0x800000, // rgb(128,0,0) + medium_aquamarine = 0x66CDAA, // rgb(102,205,170) + medium_blue = 0x0000CD, // rgb(0,0,205) + medium_orchid = 0xBA55D3, // rgb(186,85,211) + medium_purple = 0x9370DB, // rgb(147,112,219) + medium_sea_green = 0x3CB371, // rgb(60,179,113) + medium_slate_blue = 0x7B68EE, // rgb(123,104,238) + medium_spring_green = 0x00FA9A, // rgb(0,250,154) + medium_turquoise = 0x48D1CC, // rgb(72,209,204) + medium_violet_red = 0xC71585, // rgb(199,21,133) + midnight_blue = 0x191970, // rgb(25,25,112) + mint_cream = 0xF5FFFA, // rgb(245,255,250) + misty_rose = 0xFFE4E1, // rgb(255,228,225) + moccasin = 0xFFE4B5, // rgb(255,228,181) + navajo_white = 0xFFDEAD, // rgb(255,222,173) + navy = 0x000080, // rgb(0,0,128) + old_lace = 0xFDF5E6, // rgb(253,245,230) + olive = 0x808000, // rgb(128,128,0) + olive_drab = 0x6B8E23, // rgb(107,142,35) + orange = 0xFFA500, // rgb(255,165,0) + orange_red = 0xFF4500, // rgb(255,69,0) + orchid = 0xDA70D6, // rgb(218,112,214) + pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) + pale_green = 0x98FB98, // rgb(152,251,152) + pale_turquoise = 0xAFEEEE, // rgb(175,238,238) + pale_violet_red = 0xDB7093, // rgb(219,112,147) + papaya_whip = 0xFFEFD5, // rgb(255,239,213) + peach_puff = 0xFFDAB9, // rgb(255,218,185) + peru = 0xCD853F, // rgb(205,133,63) + pink = 0xFFC0CB, // rgb(255,192,203) + plum = 0xDDA0DD, // rgb(221,160,221) + powder_blue = 0xB0E0E6, // rgb(176,224,230) + purple = 0x800080, // rgb(128,0,128) + rebecca_purple = 0x663399, // rgb(102,51,153) + red = 0xFF0000, // rgb(255,0,0) + rosy_brown = 0xBC8F8F, // rgb(188,143,143) + royal_blue = 0x4169E1, // rgb(65,105,225) + saddle_brown = 0x8B4513, // rgb(139,69,19) + salmon = 0xFA8072, // rgb(250,128,114) + sandy_brown = 0xF4A460, // rgb(244,164,96) + sea_green = 0x2E8B57, // rgb(46,139,87) + sea_shell = 0xFFF5EE, // rgb(255,245,238) + sienna = 0xA0522D, // rgb(160,82,45) + silver = 0xC0C0C0, // rgb(192,192,192) + sky_blue = 0x87CEEB, // rgb(135,206,235) + slate_blue = 0x6A5ACD, // rgb(106,90,205) + slate_gray = 0x708090, // rgb(112,128,144) + snow = 0xFFFAFA, // rgb(255,250,250) + spring_green = 0x00FF7F, // rgb(0,255,127) + steel_blue = 0x4682B4, // rgb(70,130,180) + tan = 0xD2B48C, // rgb(210,180,140) + teal = 0x008080, // rgb(0,128,128) + thistle = 0xD8BFD8, // rgb(216,191,216) + tomato = 0xFF6347, // rgb(255,99,71) + turquoise = 0x40E0D0, // rgb(64,224,208) + violet = 0xEE82EE, // rgb(238,130,238) + wheat = 0xF5DEB3, // rgb(245,222,179) + white = 0xFFFFFF, // rgb(255,255,255) + white_smoke = 0xF5F5F5, // rgb(245,245,245) + yellow = 0xFFFF00, // rgb(255,255,0) + yellow_green = 0x9ACD32 // rgb(154,205,50) +}; // enum class color + +enum class terminal_color : uint8_t { + black = 30, + red, + green, + yellow, + blue, + magenta, + cyan, + white, + bright_black = 90, + bright_red, + bright_green, + bright_yellow, + bright_blue, + bright_magenta, + bright_cyan, + bright_white +}; + +enum class emphasis : uint8_t { + bold = 1, + faint = 1 << 1, + italic = 1 << 2, + underline = 1 << 3, + blink = 1 << 4, + reverse = 1 << 5, + conceal = 1 << 6, + strikethrough = 1 << 7, +}; + +// rgb is a struct for red, green and blue colors. +// Using the name "rgb" makes some editors show the color in a tooltip. +struct rgb { + FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} + FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} + FMT_CONSTEXPR rgb(uint32_t hex) + : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} + FMT_CONSTEXPR rgb(color hex) + : r((uint32_t(hex) >> 16) & 0xFF), + g((uint32_t(hex) >> 8) & 0xFF), + b(uint32_t(hex) & 0xFF) {} + uint8_t r; + uint8_t g; + uint8_t b; +}; + +FMT_BEGIN_DETAIL_NAMESPACE + +// color is a struct of either a rgb color or a terminal color. +struct color_type { + FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {} + FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} { + value.rgb_color = static_cast<uint32_t>(rgb_color); + } + FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} { + value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) | + (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b; + } + FMT_CONSTEXPR color_type(terminal_color term_color) noexcept + : is_rgb(), value{} { + value.term_color = static_cast<uint8_t>(term_color); + } + bool is_rgb; + union color_union { + uint8_t term_color; + uint32_t rgb_color; + } value; +}; + +FMT_END_DETAIL_NAMESPACE + +/** A text style consisting of foreground and background colors and emphasis. */ +class text_style { + public: + FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept + : set_foreground_color(), set_background_color(), ems(em) {} + + FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { + if (!set_foreground_color) { + set_foreground_color = rhs.set_foreground_color; + foreground_color = rhs.foreground_color; + } else if (rhs.set_foreground_color) { + if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) + FMT_THROW(format_error("can't OR a terminal color")); + foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; + } + + if (!set_background_color) { + set_background_color = rhs.set_background_color; + background_color = rhs.background_color; + } else if (rhs.set_background_color) { + if (!background_color.is_rgb || !rhs.background_color.is_rgb) + FMT_THROW(format_error("can't OR a terminal color")); + background_color.value.rgb_color |= rhs.background_color.value.rgb_color; + } + + ems = static_cast<emphasis>(static_cast<uint8_t>(ems) | + static_cast<uint8_t>(rhs.ems)); + return *this; + } + + friend FMT_CONSTEXPR text_style operator|(text_style lhs, + const text_style& rhs) { + return lhs |= rhs; + } + + FMT_CONSTEXPR bool has_foreground() const noexcept { + return set_foreground_color; + } + FMT_CONSTEXPR bool has_background() const noexcept { + return set_background_color; + } + FMT_CONSTEXPR bool has_emphasis() const noexcept { + return static_cast<uint8_t>(ems) != 0; + } + FMT_CONSTEXPR detail::color_type get_foreground() const noexcept { + FMT_ASSERT(has_foreground(), "no foreground specified for this style"); + return foreground_color; + } + FMT_CONSTEXPR detail::color_type get_background() const noexcept { + FMT_ASSERT(has_background(), "no background specified for this style"); + return background_color; + } + FMT_CONSTEXPR emphasis get_emphasis() const noexcept { + FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); + return ems; + } + + private: + FMT_CONSTEXPR text_style(bool is_foreground, + detail::color_type text_color) noexcept + : set_foreground_color(), set_background_color(), ems() { + if (is_foreground) { + foreground_color = text_color; + set_foreground_color = true; + } else { + background_color = text_color; + set_background_color = true; + } + } + + friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept; + + friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept; + + detail::color_type foreground_color; + detail::color_type background_color; + bool set_foreground_color; + bool set_background_color; + emphasis ems; +}; + +/** Creates a text style from the foreground (text) color. */ +FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept { + return text_style(true, foreground); +} + +/** Creates a text style from the background color. */ +FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept { + return text_style(false, background); +} + +FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept { + return text_style(lhs) | rhs; +} + +FMT_BEGIN_DETAIL_NAMESPACE + +template <typename Char> struct ansi_color_escape { + FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, + const char* esc) noexcept { + // If we have a terminal color, we need to output another escape code + // sequence. + if (!text_color.is_rgb) { + bool is_background = esc == string_view("\x1b[48;2;"); + uint32_t value = text_color.value.term_color; + // Background ASCII codes are the same as the foreground ones but with + // 10 more. + if (is_background) value += 10u; + + size_t index = 0; + buffer[index++] = static_cast<Char>('\x1b'); + buffer[index++] = static_cast<Char>('['); + + if (value >= 100u) { + buffer[index++] = static_cast<Char>('1'); + value %= 100u; + } + buffer[index++] = static_cast<Char>('0' + value / 10u); + buffer[index++] = static_cast<Char>('0' + value % 10u); + + buffer[index++] = static_cast<Char>('m'); + buffer[index++] = static_cast<Char>('\0'); + return; + } + + for (int i = 0; i < 7; i++) { + buffer[i] = static_cast<Char>(esc[i]); + } + rgb color(text_color.value.rgb_color); + to_esc(color.r, buffer + 7, ';'); + to_esc(color.g, buffer + 11, ';'); + to_esc(color.b, buffer + 15, 'm'); + buffer[19] = static_cast<Char>(0); + } + FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept { + uint8_t em_codes[num_emphases] = {}; + if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; + if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2; + if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3; + if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4; + if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5; + if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7; + if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; + if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; + + size_t index = 0; + for (size_t i = 0; i < num_emphases; ++i) { + if (!em_codes[i]) continue; + buffer[index++] = static_cast<Char>('\x1b'); + buffer[index++] = static_cast<Char>('['); + buffer[index++] = static_cast<Char>('0' + em_codes[i]); + buffer[index++] = static_cast<Char>('m'); + } + buffer[index++] = static_cast<Char>(0); + } + FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } + + FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; } + FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept { + return buffer + std::char_traits<Char>::length(buffer); + } + + private: + static constexpr size_t num_emphases = 8; + Char buffer[7u + 3u * num_emphases + 1u]; + + static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, + char delimiter) noexcept { + out[0] = static_cast<Char>('0' + c / 100); + out[1] = static_cast<Char>('0' + c / 10 % 10); + out[2] = static_cast<Char>('0' + c % 10); + out[3] = static_cast<Char>(delimiter); + } + static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept { + return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask); + } +}; + +template <typename Char> +FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color( + detail::color_type foreground) noexcept { + return ansi_color_escape<Char>(foreground, "\x1b[38;2;"); +} + +template <typename Char> +FMT_CONSTEXPR ansi_color_escape<Char> make_background_color( + detail::color_type background) noexcept { + return ansi_color_escape<Char>(background, "\x1b[48;2;"); +} + +template <typename Char> +FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept { + return ansi_color_escape<Char>(em); +} + +template <typename Char> inline void fputs(const Char* chars, FILE* stream) { + int result = std::fputs(chars, stream); + if (result < 0) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); +} + +template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) { + int result = std::fputws(chars, stream); + if (result < 0) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); +} + +template <typename Char> inline void reset_color(FILE* stream) { + fputs("\x1b[0m", stream); +} + +template <> inline void reset_color<wchar_t>(FILE* stream) { + fputs(L"\x1b[0m", stream); +} + +template <typename Char> inline void reset_color(buffer<Char>& buffer) { + auto reset_color = string_view("\x1b[0m"); + buffer.append(reset_color.begin(), reset_color.end()); +} + +template <typename T> struct styled_arg { + const T& value; + text_style style; +}; + +template <typename Char> +void vformat_to(buffer<Char>& buf, const text_style& ts, + basic_string_view<Char> format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) { + bool has_style = false; + if (ts.has_emphasis()) { + has_style = true; + auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis()); + buf.append(emphasis.begin(), emphasis.end()); + } + if (ts.has_foreground()) { + has_style = true; + auto foreground = detail::make_foreground_color<Char>(ts.get_foreground()); + buf.append(foreground.begin(), foreground.end()); + } + if (ts.has_background()) { + has_style = true; + auto background = detail::make_background_color<Char>(ts.get_background()); + buf.append(background.begin(), background.end()); + } + detail::vformat_to(buf, format_str, args, {}); + if (has_style) detail::reset_color<Char>(buf); +} + +FMT_END_DETAIL_NAMESPACE + +template <typename S, typename Char = char_t<S>> +void vprint(std::FILE* f, const text_style& ts, const S& format, + basic_format_args<buffer_context<type_identity_t<Char>>> args) { + basic_memory_buffer<Char> buf; + detail::vformat_to(buf, ts, detail::to_string_view(format), args); + if (detail::is_utf8()) { + detail::print(f, basic_string_view<Char>(buf.begin(), buf.size())); + } else { + buf.push_back(Char(0)); + detail::fputs(buf.data(), f); + } +} + +/** + \rst + Formats a string and prints it to the specified file stream using ANSI + escape sequences to specify text formatting. + + **Example**:: + + fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + "Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template <typename S, typename... Args, + FMT_ENABLE_IF(detail::is_string<S>::value)> +void print(std::FILE* f, const text_style& ts, const S& format_str, + const Args&... args) { + vprint(f, ts, format_str, + fmt::make_format_args<buffer_context<char_t<S>>>(args...)); +} + +/** + \rst + Formats a string and prints it to stdout using ANSI escape sequences to + specify text formatting. + + **Example**:: + + fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + "Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template <typename S, typename... Args, + FMT_ENABLE_IF(detail::is_string<S>::value)> +void print(const text_style& ts, const S& format_str, const Args&... args) { + return print(stdout, ts, format_str, args...); +} + +template <typename S, typename Char = char_t<S>> +inline std::basic_string<Char> vformat( + const text_style& ts, const S& format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) { + basic_memory_buffer<Char> buf; + detail::vformat_to(buf, ts, detail::to_string_view(format_str), args); + return fmt::to_string(buf); +} + +/** + \rst + Formats arguments and returns the result as a string using ANSI + escape sequences to specify text formatting. + + **Example**:: + + #include <fmt/color.h> + std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), + "The answer is {}", 42); + \endrst +*/ +template <typename S, typename... Args, typename Char = char_t<S>> +inline std::basic_string<Char> format(const text_style& ts, const S& format_str, + const Args&... args) { + return fmt::vformat(ts, detail::to_string_view(format_str), + fmt::make_format_args<buffer_context<Char>>(args...)); +} + +/** + Formats a string with the given text_style and writes the output to ``out``. + */ +template <typename OutputIt, typename Char, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)> +OutputIt vformat_to( + OutputIt out, const text_style& ts, basic_string_view<Char> format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) { + auto&& buf = detail::get_buffer<Char>(out); + detail::vformat_to(buf, ts, format_str, args); + return detail::get_iterator(buf); +} + +/** + \rst + Formats arguments with the given text_style, 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), + fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); + \endrst +*/ +template <typename OutputIt, typename S, typename... Args, + bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&& + detail::is_string<S>::value> +inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, + Args&&... args) -> + typename std::enable_if<enable, OutputIt>::type { + return vformat_to(out, ts, detail::to_string_view(format_str), + fmt::make_format_args<buffer_context<char_t<S>>>(args...)); +} + +template <typename T, typename Char> +struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> { + template <typename FormatContext> + auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const + -> decltype(ctx.out()) { + const auto& ts = arg.style; + const auto& value = arg.value; + auto out = ctx.out(); + + bool has_style = false; + if (ts.has_emphasis()) { + has_style = true; + auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis()); + out = std::copy(emphasis.begin(), emphasis.end(), out); + } + if (ts.has_foreground()) { + has_style = true; + auto foreground = + detail::make_foreground_color<Char>(ts.get_foreground()); + out = std::copy(foreground.begin(), foreground.end(), out); + } + if (ts.has_background()) { + has_style = true; + auto background = + detail::make_background_color<Char>(ts.get_background()); + out = std::copy(background.begin(), background.end(), out); + } + out = formatter<T, Char>::format(value, ctx); + if (has_style) { + auto reset_color = string_view("\x1b[0m"); + out = std::copy(reset_color.begin(), reset_color.end(), out); + } + return out; + } +}; + +/** + \rst + Returns an argument that will be formatted using ANSI escape sequences, + to be used in a formatting function. + + **Example**:: + + fmt::print("Elapsed time: {0:.2f} seconds", + fmt::styled(1.23, fmt::fg(fmt::color::green) | + fmt::bg(fmt::color::blue))); + \endrst + */ +template <typename T> +FMT_CONSTEXPR auto styled(const T& value, text_style ts) + -> detail::styled_arg<remove_cvref_t<T>> { + return detail::styled_arg<remove_cvref_t<T>>{value, ts}; +} + +FMT_MODULE_EXPORT_END +FMT_END_NAMESPACE + +#endif // FMT_COLOR_H_ diff --git a/src/fmt/include/fmt/compile.h b/src/fmt/include/fmt/compile.h new file mode 100644 index 000000000..933668c41 --- /dev/null +++ b/src/fmt/include/fmt/compile.h @@ -0,0 +1,611 @@ +// Formatting library for C++ - experimental format string compilation +// +// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_COMPILE_H_ +#define FMT_COMPILE_H_ + +#include "format.h" + +FMT_BEGIN_NAMESPACE +namespace detail { + +template <typename Char, typename InputIt> +FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end, + counting_iterator it) { + return it + (end - begin); +} + +template <typename OutputIt> class truncating_iterator_base { + protected: + OutputIt out_; + size_t limit_; + size_t count_ = 0; + + truncating_iterator_base() : out_(), limit_(0) {} + + truncating_iterator_base(OutputIt out, size_t limit) + : out_(out), limit_(limit) {} + + public: + using iterator_category = std::output_iterator_tag; + using value_type = typename std::iterator_traits<OutputIt>::value_type; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = void; + FMT_UNCHECKED_ITERATOR(truncating_iterator_base); + + 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() = default; + + 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() = default; + + 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; } +}; + +// A compile-time string which is compiled into fast formatting code. +class compiled_string {}; + +template <typename S> +struct is_compiled_string : std::is_base_of<compiled_string, S> {}; + +/** + \rst + Converts a string literal *s* into a format string that will be parsed at + compile time and converted into efficient formatting code. Requires C++17 + ``constexpr if`` compiler support. + + **Example**:: + + // Converts 42 into std::string using the most efficient method and no + // runtime format string processing. + std::string s = fmt::format(FMT_COMPILE("{}"), 42); + \endrst + */ +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +# define FMT_COMPILE(s) \ + FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) +#else +# define FMT_COMPILE(s) FMT_STRING(s) +#endif + +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +template <typename Char, size_t N, + fmt::detail_exported::fixed_string<Char, N> Str> +struct udl_compiled_string : compiled_string { + using char_type = Char; + explicit constexpr operator basic_string_view<char_type>() const { + return {Str.data, N - 1}; + } +}; +#endif + +template <typename T, typename... Tail> +const T& first(const T& value, const Tail&...) { + return value; +} + +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +template <typename... Args> struct type_list {}; + +// Returns a reference to the argument at index N from [first, rest...]. +template <int N, typename T, typename... Args> +constexpr const auto& get([[maybe_unused]] const T& first, + [[maybe_unused]] const Args&... rest) { + static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); + if constexpr (N == 0) + return first; + else + return detail::get<N - 1>(rest...); +} + +template <typename Char, typename... Args> +constexpr int get_arg_index_by_name(basic_string_view<Char> name, + type_list<Args...>) { + return get_arg_index_by_name<Args...>(name); +} + +template <int N, typename> struct get_type_impl; + +template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> { + using type = + remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>; +}; + +template <int N, typename T> +using get_type = typename get_type_impl<N, T>::type; + +template <typename T> struct is_compiled_format : std::false_type {}; + +template <typename Char> struct text { + basic_string_view<Char> data; + using char_type = Char; + + template <typename OutputIt, typename... Args> + constexpr OutputIt format(OutputIt out, const Args&...) const { + return write<Char>(out, data); + } +}; + +template <typename Char> +struct is_compiled_format<text<Char>> : std::true_type {}; + +template <typename Char> +constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos, + size_t size) { + return {{&s[pos], size}}; +} + +template <typename Char> struct code_unit { + Char value; + using char_type = Char; + + template <typename OutputIt, typename... Args> + constexpr OutputIt format(OutputIt out, const Args&...) const { + return write<Char>(out, value); + } +}; + +// This ensures that the argument type is convertible to `const T&`. +template <typename T, int N, typename... Args> +constexpr const T& get_arg_checked(const Args&... args) { + const auto& arg = detail::get<N>(args...); + if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) { + return arg.value; + } else { + return arg; + } +} + +template <typename Char> +struct is_compiled_format<code_unit<Char>> : std::true_type {}; + +// A replacement field that refers to argument N. +template <typename Char, typename T, int N> struct field { + using char_type = Char; + + template <typename OutputIt, typename... Args> + constexpr OutputIt format(OutputIt out, const Args&... args) const { + return write<Char>(out, get_arg_checked<T, N>(args...)); + } +}; + +template <typename Char, typename T, int N> +struct is_compiled_format<field<Char, T, N>> : std::true_type {}; + +// A replacement field that refers to argument with name. +template <typename Char> struct runtime_named_field { + using char_type = Char; + basic_string_view<Char> name; + + template <typename OutputIt, typename T> + constexpr static bool try_format_argument( + OutputIt& out, + // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9 + [[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) { + if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) { + if (arg_name == arg.name) { + out = write<Char>(out, arg.value); + return true; + } + } + return false; + } + + template <typename OutputIt, typename... Args> + constexpr OutputIt format(OutputIt out, const Args&... args) const { + bool found = (try_format_argument(out, name, args) || ...); + if (!found) { + FMT_THROW(format_error("argument with specified name is not found")); + } + return out; + } +}; + +template <typename Char> +struct is_compiled_format<runtime_named_field<Char>> : std::true_type {}; + +// A replacement field that refers to argument N and has format specifiers. +template <typename Char, typename T, int N> struct spec_field { + using char_type = Char; + formatter<T, Char> fmt; + + template <typename OutputIt, typename... Args> + constexpr FMT_INLINE OutputIt format(OutputIt out, + const Args&... args) const { + const auto& vargs = + fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...); + basic_format_context<OutputIt, Char> ctx(out, vargs); + return fmt.format(get_arg_checked<T, N>(args...), ctx); + } +}; + +template <typename Char, typename T, int N> +struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {}; + +template <typename L, typename R> struct concat { + L lhs; + R rhs; + using char_type = typename L::char_type; + + template <typename OutputIt, typename... Args> + constexpr OutputIt format(OutputIt out, const Args&... args) const { + out = lhs.format(out, args...); + return rhs.format(out, args...); + } +}; + +template <typename L, typename R> +struct is_compiled_format<concat<L, R>> : std::true_type {}; + +template <typename L, typename R> +constexpr concat<L, R> make_concat(L lhs, R rhs) { + return {lhs, rhs}; +} + +struct unknown_format {}; + +template <typename Char> +constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) { + for (size_t size = str.size(); pos != size; ++pos) { + if (str[pos] == '{' || str[pos] == '}') break; + } + return pos; +} + +template <typename Args, size_t POS, int ID, typename S> +constexpr auto compile_format_string(S format_str); + +template <typename Args, size_t POS, int ID, typename T, typename S> +constexpr auto parse_tail(T head, S format_str) { + if constexpr (POS != + basic_string_view<typename S::char_type>(format_str).size()) { + constexpr auto tail = compile_format_string<Args, POS, ID>(format_str); + if constexpr (std::is_same<remove_cvref_t<decltype(tail)>, + unknown_format>()) + return tail; + else + return make_concat(head, tail); + } else { + return head; + } +} + +template <typename T, typename Char> struct parse_specs_result { + formatter<T, Char> fmt; + size_t end; + int next_arg_id; +}; + +constexpr int manual_indexing_id = -1; + +template <typename T, typename Char> +constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, + size_t pos, int next_arg_id) { + str.remove_prefix(pos); + auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, {}, + next_arg_id); + auto f = formatter<T, Char>(); + auto end = f.parse(ctx); + return {f, pos + fmt::detail::to_unsigned(end - str.data()), + next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; +} + +template <typename Char> struct arg_id_handler { + arg_ref<Char> arg_id; + + constexpr int operator()() { + FMT_ASSERT(false, "handler cannot be used with automatic indexing"); + return 0; + } + constexpr int operator()(int id) { + arg_id = arg_ref<Char>(id); + return 0; + } + constexpr int operator()(basic_string_view<Char> id) { + arg_id = arg_ref<Char>(id); + return 0; + } + + constexpr void on_error(const char* message) { + FMT_THROW(format_error(message)); + } +}; + +template <typename Char> struct parse_arg_id_result { + arg_ref<Char> arg_id; + const Char* arg_id_end; +}; + +template <int ID, typename Char> +constexpr auto parse_arg_id(const Char* begin, const Char* end) { + auto handler = arg_id_handler<Char>{arg_ref<Char>{}}; + auto arg_id_end = parse_arg_id(begin, end, handler); + return parse_arg_id_result<Char>{handler.arg_id, arg_id_end}; +} + +template <typename T, typename Enable = void> struct field_type { + using type = remove_cvref_t<T>; +}; + +template <typename T> +struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> { + using type = remove_cvref_t<decltype(T::value)>; +}; + +template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID, + typename S> +constexpr auto parse_replacement_field_then_tail(S format_str) { + using char_type = typename S::char_type; + constexpr auto str = basic_string_view<char_type>(format_str); + constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); + if constexpr (c == '}') { + return parse_tail<Args, END_POS + 1, NEXT_ID>( + field<char_type, typename field_type<T>::type, ARG_INDEX>(), + format_str); + } else if constexpr (c != ':') { + FMT_THROW(format_error("expected ':'")); + } else { + constexpr auto result = parse_specs<typename field_type<T>::type>( + str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); + if constexpr (result.end >= str.size() || str[result.end] != '}') { + FMT_THROW(format_error("expected '}'")); + return 0; + } else { + return parse_tail<Args, result.end + 1, result.next_arg_id>( + spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{ + result.fmt}, + format_str); + } + } +} + +// Compiles a non-empty format string and returns the compiled representation +// or unknown_format() on unrecognized input. +template <typename Args, size_t POS, int ID, typename S> +constexpr auto compile_format_string(S format_str) { + using char_type = typename S::char_type; + constexpr auto str = basic_string_view<char_type>(format_str); + if constexpr (str[POS] == '{') { + if constexpr (POS + 1 == str.size()) + FMT_THROW(format_error("unmatched '{' in format string")); + if constexpr (str[POS + 1] == '{') { + return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); + } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { + static_assert(ID != manual_indexing_id, + "cannot switch from manual to automatic argument indexing"); + constexpr auto next_id = + ID != manual_indexing_id ? ID + 1 : manual_indexing_id; + return parse_replacement_field_then_tail<get_type<ID, Args>, Args, + POS + 1, ID, next_id>( + format_str); + } else { + constexpr auto arg_id_result = + parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size()); + constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data(); + constexpr char_type c = + arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); + static_assert(c == '}' || c == ':', "missing '}' in format string"); + if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) { + static_assert( + ID == manual_indexing_id || ID == 0, + "cannot switch from automatic to manual argument indexing"); + constexpr auto arg_index = arg_id_result.arg_id.val.index; + return parse_replacement_field_then_tail<get_type<arg_index, Args>, + Args, arg_id_end_pos, + arg_index, manual_indexing_id>( + format_str); + } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { + constexpr auto arg_index = + get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); + if constexpr (arg_index != invalid_arg_index) { + constexpr auto next_id = + ID != manual_indexing_id ? ID + 1 : manual_indexing_id; + return parse_replacement_field_then_tail< + decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos, + arg_index, next_id>(format_str); + } else { + if constexpr (c == '}') { + return parse_tail<Args, arg_id_end_pos + 1, ID>( + runtime_named_field<char_type>{arg_id_result.arg_id.val.name}, + format_str); + } else if constexpr (c == ':') { + return unknown_format(); // no type info for specs parsing + } + } + } + } + } else if constexpr (str[POS] == '}') { + if constexpr (POS + 1 == str.size()) + FMT_THROW(format_error("unmatched '}' in format string")); + return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); + } else { + constexpr auto end = parse_text(str, POS + 1); + if constexpr (end - POS > 1) { + return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), + format_str); + } else { + return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, + format_str); + } + } +} + +template <typename... Args, typename S, + FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> +constexpr auto compile(S format_str) { + constexpr auto str = basic_string_view<typename S::char_type>(format_str); + if constexpr (str.size() == 0) { + return detail::make_text(str, 0, 0); + } else { + constexpr auto result = + detail::compile_format_string<detail::type_list<Args...>, 0, 0>( + format_str); + return result; + } +} +#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +} // namespace detail + +FMT_MODULE_EXPORT_BEGIN + +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) + +template <typename CompiledFormat, typename... Args, + typename Char = typename CompiledFormat::char_type, + FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> +FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf, + const Args&... args) { + auto s = std::basic_string<Char>(); + cf.format(std::back_inserter(s), args...); + return s; +} + +template <typename OutputIt, typename CompiledFormat, typename... Args, + FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> +constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, + const Args&... args) { + return cf.format(out, args...); +} + +template <typename S, typename... Args, + FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> +FMT_INLINE std::basic_string<typename S::char_type> format(const S&, + Args&&... args) { + if constexpr (std::is_same<typename S::char_type, char>::value) { + constexpr auto str = basic_string_view<typename S::char_type>(S()); + if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { + const auto& first = detail::first(args...); + if constexpr (detail::is_named_arg< + remove_cvref_t<decltype(first)>>::value) { + return fmt::to_string(first.value); + } else { + return fmt::to_string(first); + } + } + } + constexpr auto compiled = detail::compile<Args...>(S()); + if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, + detail::unknown_format>()) { + return fmt::format( + static_cast<basic_string_view<typename S::char_type>>(S()), + std::forward<Args>(args)...); + } else { + return fmt::format(compiled, std::forward<Args>(args)...); + } +} + +template <typename OutputIt, typename S, typename... Args, + FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> +FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { + constexpr auto compiled = detail::compile<Args...>(S()); + if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, + detail::unknown_format>()) { + return fmt::format_to( + out, static_cast<basic_string_view<typename S::char_type>>(S()), + std::forward<Args>(args)...); + } else { + return fmt::format_to(out, compiled, std::forward<Args>(args)...); + } +} +#endif + +template <typename OutputIt, typename S, typename... Args, + FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> +format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, + const S& format_str, Args&&... args) { + auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n), + format_str, std::forward<Args>(args)...); + return {it.base(), it.count()}; +} + +template <typename S, typename... Args, + FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> +FMT_CONSTEXPR20 size_t formatted_size(const S& format_str, + const Args&... args) { + return fmt::format_to(detail::counting_iterator(), format_str, args...) + .count(); +} + +template <typename S, typename... Args, + FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> +void print(std::FILE* f, const S& format_str, const Args&... args) { + memory_buffer buffer; + fmt::format_to(std::back_inserter(buffer), format_str, args...); + detail::print(f, {buffer.data(), buffer.size()}); +} + +template <typename S, typename... Args, + FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> +void print(const S& format_str, const Args&... args) { + print(stdout, format_str, args...); +} + +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +inline namespace literals { +template <detail_exported::fixed_string Str> constexpr auto operator""_cf() { + using char_t = remove_cvref_t<decltype(Str.data[0])>; + return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t), + Str>(); +} +} // namespace literals +#endif + +FMT_MODULE_EXPORT_END +FMT_END_NAMESPACE + +#endif // FMT_COMPILE_H_ diff --git a/src/fmt/include/fmt/core.h b/src/fmt/include/fmt/core.h new file mode 100644 index 000000000..f6a37af9e --- /dev/null +++ b/src/fmt/include/fmt/core.h @@ -0,0 +1,3323 @@ +// Formatting library for C++ - the core API for char/UTF-8 +// +// 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 <cstddef> // std::byte +#include <cstdio> // std::FILE +#include <cstring> // std::strlen +#include <iterator> +#include <limits> +#include <string> +#include <type_traits> + +// The fmt library version in the form major * 10000 + minor * 100 + patch. +#define FMT_VERSION 90100 + +#if defined(__clang__) && !defined(__ibmxl__) +# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) +#else +# define FMT_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \ + !defined(__NVCOMPILER) +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#else +# define FMT_GCC_VERSION 0 +#endif + +#ifndef FMT_GCC_PRAGMA +// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884. +# if FMT_GCC_VERSION >= 504 +# define FMT_GCC_PRAGMA(arg) _Pragma(arg) +# else +# define FMT_GCC_PRAGMA(arg) +# endif +#endif + +#ifdef __ICL +# define FMT_ICC_VERSION __ICL +#elif defined(__INTEL_COMPILER) +# define FMT_ICC_VERSION __INTEL_COMPILER +#else +# define FMT_ICC_VERSION 0 +#endif + +#ifdef _MSC_VER +# define FMT_MSC_VERSION _MSC_VER +# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) +#else +# define FMT_MSC_VERSION 0 +# define FMT_MSC_WARNING(...) +#endif + +#ifdef _MSVC_LANG +# define FMT_CPLUSPLUS _MSVC_LANG +#else +# define FMT_CPLUSPLUS __cplusplus +#endif + +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif + +#if (defined(__has_include) || FMT_ICC_VERSION >= 1600 || \ + FMT_MSC_VERSION > 1900) && \ + !defined(__INTELLISENSE__) +# 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) \ + (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ + (FMT_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 +# if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \ + (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \ + !FMT_ICC_VERSION && !defined(__NVCC__) +# define FMT_USE_CONSTEXPR 1 +# else +# define FMT_USE_CONSTEXPR 0 +# endif +#endif +#if FMT_USE_CONSTEXPR +# define FMT_CONSTEXPR constexpr +#else +# define FMT_CONSTEXPR +#endif + +#if ((FMT_CPLUSPLUS >= 202002L) && \ + (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \ + (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002) +# define FMT_CONSTEXPR20 constexpr +#else +# define FMT_CONSTEXPR20 +#endif + +// Check if constexpr std::char_traits<>::{compare,length} are supported. +#if defined(__GLIBCXX__) +# if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \ + _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE. +# define FMT_CONSTEXPR_CHAR_TRAITS constexpr +# endif +#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \ + _LIBCPP_VERSION >= 4000 +# define FMT_CONSTEXPR_CHAR_TRAITS constexpr +#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L +# define FMT_CONSTEXPR_CHAR_TRAITS constexpr +#endif +#ifndef FMT_CONSTEXPR_CHAR_TRAITS +# define FMT_CONSTEXPR_CHAR_TRAITS +#endif + +// Check if exceptions are disabled. +#ifndef FMT_EXCEPTIONS +# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ + (FMT_MSC_VERSION && !_HAS_EXCEPTIONS) +# define FMT_EXCEPTIONS 0 +# else +# define FMT_EXCEPTIONS 1 +# endif +#endif + +#ifndef FMT_DEPRECATED +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900 +# define FMT_DEPRECATED [[deprecated]] +# else +# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) +# define FMT_DEPRECATED __attribute__((deprecated)) +# elif FMT_MSC_VERSION +# define FMT_DEPRECATED __declspec(deprecated) +# else +# define FMT_DEPRECATED /* deprecated */ +# endif +# 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_VERSION && \ + !defined(__NVCC__) +# define FMT_NORETURN [[noreturn]] +#else +# define FMT_NORETURN +#endif + +#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) +# define FMT_FALLTHROUGH [[fallthrough]] +#elif defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +#elif FMT_GCC_VERSION >= 700 && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] +#else +# define FMT_FALLTHROUGH +#endif + +#ifndef FMT_NODISCARD +# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard) +# define FMT_NODISCARD [[nodiscard]] +# else +# define FMT_NODISCARD +# 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 + +#ifndef FMT_INLINE +# if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_INLINE inline __attribute__((always_inline)) +# else +# define FMT_INLINE inline +# endif +#endif + +// An inline std::forward replacement. +#define FMT_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__) + +#ifdef _MSC_VER +# define FMT_UNCHECKED_ITERATOR(It) \ + using _Unchecked_type = It // Mark iterator as checked. +#else +# define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It +#endif + +#ifndef FMT_BEGIN_NAMESPACE +# define FMT_BEGIN_NAMESPACE \ + namespace fmt { \ + inline namespace v9 { +# define FMT_END_NAMESPACE \ + } \ + } +#endif + +#ifndef FMT_MODULE_EXPORT +# define FMT_MODULE_EXPORT +# define FMT_MODULE_EXPORT_BEGIN +# define FMT_MODULE_EXPORT_END +# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail { +# define FMT_END_DETAIL_NAMESPACE } +#endif + +#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) +# define FMT_CLASS_API FMT_MSC_WARNING(suppress : 4275) +# ifdef FMT_EXPORT +# define FMT_API __declspec(dllexport) +# elif defined(FMT_SHARED) +# define FMT_API __declspec(dllimport) +# endif +#else +# define FMT_CLASS_API +# if defined(FMT_EXPORT) || defined(FMT_SHARED) +# if defined(__GNUC__) || defined(__clang__) +# define FMT_API __attribute__((visibility("default"))) +# endif +# endif +#endif +#ifndef FMT_API +# define FMT_API +#endif + +// libc++ supports string_view in pre-c++17. +#if FMT_HAS_INCLUDE(<string_view>) && \ + (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) +# include <string_view> +# define FMT_USE_STRING_VIEW +#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L +# include <experimental/string_view> +# define FMT_USE_EXPERIMENTAL_STRING_VIEW +#endif + +#ifndef FMT_UNICODE +# define FMT_UNICODE !FMT_MSC_VERSION +#endif + +#ifndef FMT_CONSTEVAL +# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ + FMT_CPLUSPLUS >= 202002L && !defined(__apple_build_version__)) || \ + (defined(__cpp_consteval) && \ + (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704)) +// consteval is broken in MSVC before VS2022 and Apple clang 13. +# define FMT_CONSTEVAL consteval +# define FMT_HAS_CONSTEVAL +# else +# define FMT_CONSTEVAL +# endif +#endif + +#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS +# if defined(__cpp_nontype_template_args) && \ + ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \ + __cpp_nontype_template_args >= 201911L) && \ + !defined(__NVCOMPILER) +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +# else +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 +# endif +#endif + +// Enable minimal optimizations for more compact code in debug mode. +FMT_GCC_PRAGMA("GCC push_options") +#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) +FMT_GCC_PRAGMA("GCC optimize(\"Og\")") +#endif + +FMT_BEGIN_NAMESPACE +FMT_MODULE_EXPORT_BEGIN + +// Implementations of enable_if_t and other metafunctions for older systems. +template <bool B, typename T = void> +using enable_if_t = typename std::enable_if<B, T>::type; +template <bool B, typename T, typename 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; +template <typename T> +using underlying_t = typename std::underlying_type<T>::type; + +template <typename...> struct disjunction : std::false_type {}; +template <typename P> struct disjunction<P> : P {}; +template <typename P1, typename... Pn> +struct disjunction<P1, Pn...> + : conditional_t<bool(P1::value), P1, disjunction<Pn...>> {}; + +template <typename...> struct conjunction : std::true_type {}; +template <typename P> struct conjunction<P> : P {}; +template <typename P1, typename... Pn> +struct conjunction<P1, Pn...> + : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {}; + +struct monostate { + constexpr 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). +#ifdef FMT_DOC +# define FMT_ENABLE_IF(...) +#else +# define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0 +#endif + +FMT_BEGIN_DETAIL_NAMESPACE + +// Suppresses "unused variable" warnings with the method described in +// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. +// (void)var does not work on many Intel compilers. +template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {} + +constexpr FMT_INLINE auto is_constant_evaluated( + bool default_value = false) noexcept -> bool { +#ifdef __cpp_lib_is_constant_evaluated + ignore_unused(default_value); + return std::is_constant_evaluated(); +#else + return default_value; +#endif +} + +// Suppresses "conditional expression is constant" warnings. +template <typename T> constexpr FMT_INLINE auto const_check(T value) -> T { + 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 -Wempty-body. +# define FMT_ASSERT(condition, message) \ + ::fmt::detail::ignore_unused((condition), (message)) +# 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__) && !defined(__NVCC__) && \ + !(FMT_CLANG_VERSION && FMT_MSC_VERSION) +# define FMT_USE_INT128 1 +using int128_opt = __int128_t; // An optional native 128-bit integer. +using uint128_opt = __uint128_t; +template <typename T> inline auto convert_for_visit(T value) -> T { + return value; +} +#else +# define FMT_USE_INT128 0 +#endif +#if !FMT_USE_INT128 +enum class int128_opt {}; +enum class uint128_opt {}; +// Reduce template instantiations. +template <typename T> auto convert_for_visit(T) -> monostate { return {}; } +#endif + +// Casts a nonnegative integer to unsigned. +template <typename Int> +FMT_CONSTEXPR auto to_unsigned(Int value) -> + typename std::make_unsigned<Int>::type { + FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value"); + return static_cast<typename std::make_unsigned<Int>::type>(value); +} + +FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5"; + +constexpr auto is_utf8() -> bool { + // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297). + using uchar = unsigned char; + return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 && + uchar(micro[1]) == 0xB5); +} +FMT_END_DETAIL_NAMESPACE + +/** + 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() 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) 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 + */ + FMT_CONSTEXPR_CHAR_TRAITS + FMT_INLINE + basic_string_view(const Char* s) + : data_(s), + size_(detail::const_check(std::is_same<Char, char>::value && + !detail::is_constant_evaluated(true)) + ? std::strlen(reinterpret_cast<const char*>(s)) + : 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) 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) noexcept + : data_(s.data()), size_(s.size()) {} + + /** Returns a pointer to the string data. */ + constexpr auto data() const noexcept -> const Char* { return data_; } + + /** Returns the string size. */ + constexpr auto size() const noexcept -> size_t { return size_; } + + constexpr auto begin() const noexcept -> iterator { return data_; } + constexpr auto end() const noexcept -> iterator { return data_ + size_; } + + constexpr auto operator[](size_t pos) const noexcept -> const Char& { + return data_[pos]; + } + + FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { + data_ += n; + size_ -= n; + } + + // Lexicographically compare this string reference to other. + FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int { + 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; + } + + FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs, + basic_string_view rhs) + -> bool { + return lhs.compare(rhs) == 0; + } + friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) != 0; + } + friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) < 0; + } + friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) <= 0; + } + friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) > 0; + } + friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) >= 0; + } +}; + +using string_view = basic_string_view<char>; + +/** 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 {}; + +FMT_BEGIN_DETAIL_NAMESPACE + +// A base class for compile-time strings. +struct compile_string {}; + +template <typename S> +struct is_compile_string : std::is_base_of<compile_string, S> {}; + +// Returns a string view of `s`. +template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)> +FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view<Char> { + return s; +} +template <typename Char, typename Traits, typename Alloc> +inline auto to_string_view(const std::basic_string<Char, Traits, Alloc>& s) + -> basic_string_view<Char> { + return s; +} +template <typename Char> +constexpr auto to_string_view(basic_string_view<Char> s) + -> basic_string_view<Char> { + return s; +} +template <typename Char, + FMT_ENABLE_IF(!std::is_empty<std_string_view<Char>>::value)> +inline auto to_string_view(std_string_view<Char> s) -> basic_string_view<Char> { + return s; +} +template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)> +constexpr auto to_string_view(const S& s) + -> basic_string_view<typename S::char_type> { + return basic_string_view<typename S::char_type>(s); +} +void 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. +// ADL invocation of to_string_view is DEPRECATED! +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; +}; + +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_opt, int128_type); +FMT_TYPE_CONSTANT(uint128_opt, 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; +} + +FMT_NORETURN FMT_API void throw_format_error(const char* message); + +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 void on_error(const char* message) { + throw_format_error(message); + } +}; +FMT_END_DETAIL_NAMESPACE + +/** 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 the ``format_parse_context`` type alias for ``char`` instead. + \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_; + + FMT_CONSTEXPR void do_check_arg_id(int 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 = {}, + int next_arg_id = 0) + : ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {} + + /** + Returns an iterator to the beginning of the format string range being + parsed. + */ + constexpr auto begin() const noexcept -> iterator { + return format_str_.begin(); + } + + /** + Returns an iterator past the end of the format string range being parsed. + */ + constexpr auto end() const noexcept -> iterator { 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 auto next_arg_id() -> int { + if (next_arg_id_ < 0) { + on_error("cannot switch from manual to automatic argument indexing"); + return 0; + } + int id = next_arg_id_++; + do_check_arg_id(id); + return id; + } + + /** + Reports an error if using the automatic argument indexing; otherwise + switches to the manual indexing. + */ + FMT_CONSTEXPR void check_arg_id(int id) { + if (next_arg_id_ > 0) { + on_error("cannot switch from automatic to manual argument indexing"); + return; + } + next_arg_id_ = -1; + do_check_arg_id(id); + } + FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {} + FMT_CONSTEXPR void check_dynamic_spec(int arg_id); + + FMT_CONSTEXPR void on_error(const char* message) { + ErrorHandler::on_error(message); + } + + constexpr auto error_handler() const -> ErrorHandler { return *this; } +}; + +using format_parse_context = basic_format_parse_context<char>; + +FMT_BEGIN_DETAIL_NAMESPACE +// A parse context with extra data used only in compile-time checks. +template <typename Char, typename ErrorHandler = detail::error_handler> +class compile_parse_context + : public basic_format_parse_context<Char, ErrorHandler> { + private: + int num_args_; + const type* types_; + using base = basic_format_parse_context<Char, ErrorHandler>; + + public: + explicit FMT_CONSTEXPR compile_parse_context( + basic_string_view<Char> format_str, int num_args, const type* types, + ErrorHandler eh = {}, int next_arg_id = 0) + : base(format_str, eh, next_arg_id), num_args_(num_args), types_(types) {} + + constexpr auto num_args() const -> int { return num_args_; } + constexpr auto arg_type(int id) const -> type { return types_[id]; } + + FMT_CONSTEXPR auto next_arg_id() -> int { + 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; + + FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { + if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) + this->on_error("width/precision is not integer"); + } +}; +FMT_END_DETAIL_NAMESPACE + +template <typename Char, typename ErrorHandler> +FMT_CONSTEXPR void +basic_format_parse_context<Char, ErrorHandler>::do_check_arg_id(int id) { + // Argument id is only checked at compile-time during parsing because + // formatting has its own validation. + if (detail::is_constant_evaluated() && FMT_GCC_VERSION >= 1200) { + using context = detail::compile_parse_context<Char, ErrorHandler>; + if (id >= static_cast<context*>(this)->num_args()) + on_error("argument not found"); + } +} + +template <typename Char, typename ErrorHandler> +FMT_CONSTEXPR void +basic_format_parse_context<Char, ErrorHandler>::check_dynamic_spec(int arg_id) { + if (detail::is_constant_evaluated()) { + using context = detail::compile_parse_context<Char, ErrorHandler>; + static_cast<context*>(this)->check_dynamic_spec(arg_id); + } +} + +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>>; + +// 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 {}; + +class appender; + +FMT_BEGIN_DETAIL_NAMESPACE + +template <typename Context, typename T> +constexpr auto has_const_formatter_impl(T*) + -> decltype(typename Context::template formatter_type<T>().format( + std::declval<const T&>(), std::declval<Context&>()), + true) { + return true; +} +template <typename Context> +constexpr auto has_const_formatter_impl(...) -> bool { + return false; +} +template <typename T, typename Context> +constexpr auto has_const_formatter() -> bool { + return has_const_formatter_impl<Context>(static_cast<T*>(nullptr)); +} + +// Extracts a reference to the container from back_insert_iterator. +template <typename Container> +inline auto get_container(std::back_insert_iterator<Container> it) + -> Container& { + using base = std::back_insert_iterator<Container>; + struct accessor : base { + accessor(base b) : base(b) {} + using base::container; + }; + return *accessor(it).container; +} + +template <typename Char, typename InputIt, typename OutputIt> +FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + while (begin != end) *out++ = static_cast<Char>(*begin++); + return out; +} + +template <typename Char, typename T, typename U, + FMT_ENABLE_IF( + std::is_same<remove_const_t<T>, U>::value&& is_char<U>::value)> +FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* { + if (is_constant_evaluated()) return copy_str<Char, T*, U*>(begin, end, out); + auto size = to_unsigned(end - begin); + memcpy(out, begin, size * sizeof(U)); + return out + size; +} + +/** + \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_MSC_WARNING(suppress : 26495) + buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {} + + FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept + : ptr_(p), size_(sz), capacity_(cap) {} + + FMT_CONSTEXPR20 ~buffer() = default; + buffer(buffer&&) = default; + + /** Sets the buffer data and capacity. */ + FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { + ptr_ = buf_data; + capacity_ = buf_capacity; + } + + /** Increases the buffer capacity to hold at least *capacity* elements. */ + virtual FMT_CONSTEXPR20 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; + + auto begin() noexcept -> T* { return ptr_; } + auto end() noexcept -> T* { return ptr_ + size_; } + + auto begin() const noexcept -> const T* { return ptr_; } + auto end() const noexcept -> const T* { return ptr_ + size_; } + + /** Returns the size of this buffer. */ + constexpr auto size() const noexcept -> size_t { return size_; } + + /** Returns the capacity of this buffer. */ + constexpr auto capacity() const noexcept -> size_t { return capacity_; } + + /** Returns a pointer to the buffer data. */ + FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } + + /** Returns a pointer to the buffer data. */ + FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } + + /** Clears this buffer. */ + void clear() { size_ = 0; } + + // Tries resizing the buffer to contain *count* elements. If T is a POD type + // the new elements may not be initialized. + FMT_CONSTEXPR20 void try_resize(size_t count) { + try_reserve(count); + size_ = count <= capacity_ ? count : capacity_; + } + + // Tries increasing the buffer capacity to *new_capacity*. It can increase the + // capacity by a smaller amount than requested but guarantees there is space + // for at least one additional element either by increasing the capacity or by + // flushing the buffer if it is full. + FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) { + if (new_capacity > capacity_) grow(new_capacity); + } + + FMT_CONSTEXPR20 void push_back(const T& value) { + try_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 Idx> FMT_CONSTEXPR auto operator[](Idx index) -> T& { + return ptr_[index]; + } + template <typename Idx> + FMT_CONSTEXPR auto operator[](Idx index) const -> const T& { + return ptr_[index]; + } +}; + +struct buffer_traits { + explicit buffer_traits(size_t) {} + auto count() const -> size_t { return 0; } + auto limit(size_t size) -> size_t { return size; } +}; + +class fixed_buffer_traits { + private: + size_t count_ = 0; + size_t limit_; + + public: + explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} + auto count() const -> size_t { return count_; } + auto limit(size_t size) -> size_t { + size_t n = limit_ > count_ ? limit_ - count_ : 0; + count_ += size; + return size < n ? size : n; + } +}; + +// A buffer that writes to an output iterator when flushed. +template <typename OutputIt, typename T, typename Traits = buffer_traits> +class iterator_buffer final : public Traits, public buffer<T> { + private: + OutputIt out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + protected: + FMT_CONSTEXPR20 void grow(size_t) override { + if (this->size() == buffer_size) flush(); + } + + void flush() { + auto size = this->size(); + this->clear(); + out_ = copy_str<T>(data_, data_ + this->limit(size), out_); + } + + public: + explicit iterator_buffer(OutputIt out, size_t n = buffer_size) + : Traits(n), buffer<T>(data_, 0, buffer_size), out_(out) {} + iterator_buffer(iterator_buffer&& other) + : Traits(other), buffer<T>(data_, 0, buffer_size), out_(other.out_) {} + ~iterator_buffer() { flush(); } + + auto out() -> OutputIt { + flush(); + return out_; + } + auto count() const -> size_t { return Traits::count() + this->size(); } +}; + +template <typename T> +class iterator_buffer<T*, T, fixed_buffer_traits> final + : public fixed_buffer_traits, + public buffer<T> { + private: + T* out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + protected: + FMT_CONSTEXPR20 void grow(size_t) override { + if (this->size() == this->capacity()) flush(); + } + + void flush() { + size_t n = this->limit(this->size()); + if (this->data() == out_) { + out_ += n; + this->set(data_, buffer_size); + } + this->clear(); + } + + public: + explicit iterator_buffer(T* out, size_t n = buffer_size) + : fixed_buffer_traits(n), buffer<T>(out, 0, n), out_(out) {} + iterator_buffer(iterator_buffer&& other) + : fixed_buffer_traits(other), + buffer<T>(std::move(other)), + out_(other.out_) { + if (this->data() != out_) { + this->set(data_, buffer_size); + this->clear(); + } + } + ~iterator_buffer() { flush(); } + + auto out() -> T* { + flush(); + return out_; + } + auto count() const -> size_t { + return fixed_buffer_traits::count() + this->size(); + } +}; + +template <typename T> class iterator_buffer<T*, T> final : public buffer<T> { + protected: + FMT_CONSTEXPR20 void grow(size_t) override {} + + public: + explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {} + + auto out() -> T* { return &*this->end(); } +}; + +// A buffer that writes to a container with the contiguous storage. +template <typename Container> +class iterator_buffer<std::back_insert_iterator<Container>, + enable_if_t<is_contiguous<Container>::value, + typename Container::value_type>> + final : public buffer<typename Container::value_type> { + private: + Container& container_; + + protected: + FMT_CONSTEXPR20 void grow(size_t capacity) override { + container_.resize(capacity); + this->set(&container_[0], capacity); + } + + public: + explicit iterator_buffer(Container& c) + : buffer<typename Container::value_type>(c.size()), container_(c) {} + explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0) + : iterator_buffer(get_container(out)) {} + + auto out() -> std::back_insert_iterator<Container> { + return std::back_inserter(container_); + } +}; + +// A buffer that counts the number of code units written discarding the output. +template <typename T = char> class counting_buffer final : public buffer<T> { + private: + enum { buffer_size = 256 }; + T data_[buffer_size]; + size_t count_ = 0; + + protected: + FMT_CONSTEXPR20 void grow(size_t) override { + if (this->size() != buffer_size) return; + count_ += this->size(); + this->clear(); + } + + public: + counting_buffer() : buffer<T>(data_, 0, buffer_size) {} + + auto count() -> size_t { return count_ + this->size(); } +}; + +template <typename T> +using buffer_appender = conditional_t<std::is_same<T, char>::value, appender, + std::back_insert_iterator<buffer<T>>>; + +// Maps an output iterator to a buffer. +template <typename T, typename OutputIt> +auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> { + return iterator_buffer<OutputIt, T>(out); +} + +template <typename Buffer> +auto get_iterator(Buffer& buf) -> decltype(buf.out()) { + return buf.out(); +} +template <typename T> auto get_iterator(buffer<T>& buf) -> buffer_appender<T> { + return buffer_appender<T>(buf); +} + +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 Char> +using has_fallback_formatter = +#ifdef FMT_DEPRECATED_OSTREAM + std::is_constructible<fallback_formatter<T, Char>>; +#else + std::false_type; +#endif + +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. + // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. + 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; + auto args() const -> const T* { return args_ + 1; } + auto named_args() -> named_arg_info<Char>* { return named_args_; } +}; + +template <typename T, typename Char, size_t NUM_ARGS> +struct arg_data<T, Char, NUM_ARGS, 0> { + // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. + T args_[NUM_ARGS != 0 ? NUM_ARGS : +1]; + + template <typename... U> + FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {} + FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; } + FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t { + return nullptr; + } +}; + +template <typename Char> +inline void init_named_args(named_arg_info<Char>*, int, int) {} + +template <typename T> struct is_named_arg : std::false_type {}; +template <typename T> struct is_statically_named_arg : std::false_type {}; + +template <typename T, typename Char> +struct is_named_arg<named_arg<Char, T>> : std::true_type {}; + +template <typename Char, typename T, typename... Tail, + FMT_ENABLE_IF(!is_named_arg<T>::value)> +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, + FMT_ENABLE_IF(is_named_arg<T>::value)> +void init_named_args(named_arg_info<Char>* named_args, int arg_count, + int named_arg_count, const 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_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, + const Args&...) {} + +template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; } +template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t { + return (B1 ? 1 : 0) + count<B2, Tail...>(); +} + +template <typename... Args> constexpr auto count_named_args() -> size_t { + return count<is_named_arg<Args>::value...>(); +} + +template <typename... Args> +constexpr auto count_statically_named_args() -> size_t { + return count<is_statically_named_arg<Args>::value...>(); +} + +struct unformattable {}; +struct unformattable_char : unformattable {}; +struct unformattable_const : unformattable {}; +struct unformattable_pointer : unformattable {}; + +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; + void* value; + void (*format)(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 { + monostate no_value; + int int_value; + unsigned uint_value; + long long long_long_value; + unsigned long long ulong_long_value; + int128_opt int128_value; + uint128_opt 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() : no_value() {} + constexpr FMT_INLINE value(int val) : int_value(val) {} + constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} + constexpr FMT_INLINE value(long long val) : long_long_value(val) {} + constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} + FMT_INLINE value(int128_opt val) : int128_value(val) {} + FMT_INLINE value(uint128_opt val) : uint128_value(val) {} + constexpr FMT_INLINE value(float val) : float_value(val) {} + constexpr FMT_INLINE value(double val) : double_value(val) {} + FMT_INLINE value(long double val) : long_double_value(val) {} + constexpr FMT_INLINE value(bool val) : bool_value(val) {} + constexpr FMT_INLINE value(char_type val) : char_value(val) {} + FMT_CONSTEXPR FMT_INLINE value(const char_type* val) { + string.data = val; + if (is_constant_evaluated()) string.size = {}; + } + FMT_CONSTEXPR 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_CONSTEXPR FMT_INLINE value(T& val) { + using value_type = remove_cvref_t<T>; + custom.value = const_cast<value_type*>(&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< + value_type, + conditional_t<has_formatter<value_type, Context>::value, + typename Context::template formatter_type<value_type>, + fallback_formatter<value_type, char_type>>>; + } + value(unformattable); + value(unformattable_char); + value(unformattable_const); + value(unformattable_pointer); + + private: + // Formats an argument of a custom type, such as a user-defined class. + template <typename T, typename Formatter> + static void format_custom_arg(void* arg, + typename Context::parse_context_type& parse_ctx, + Context& ctx) { + auto f = Formatter(); + parse_ctx.advance_to(f.parse(parse_ctx)); + using qualified_type = + conditional_t<has_const_formatter<T, Context>(), const T, T>; + ctx.advance_to(f.format(*static_cast<qualified_type*>(arg), ctx)); + } +}; + +template <typename Context, typename T> +FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context>; + +// 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>; + +#ifdef __cpp_lib_byte +inline auto format_as(std::byte b) -> unsigned char { + return static_cast<unsigned char>(b); +} +#endif + +template <typename T> struct has_format_as { + template <typename U, typename V = decltype(format_as(U())), + FMT_ENABLE_IF(std::is_enum<U>::value&& std::is_integral<V>::value)> + static auto check(U*) -> std::true_type; + static auto check(...) -> std::false_type; + + enum { value = decltype(check(static_cast<T*>(nullptr)))::value }; +}; + +// Maps formatting arguments to core types. +// arg_mapper reports errors by returning unformattable instead of using +// static_assert because it's used in the is_formattable trait. +template <typename Context> struct arg_mapper { + using char_type = typename Context::char_type; + + FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val) + -> unsigned long long { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; } + + template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value || + std::is_same<T, char_type>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type { + return val; + } + template <typename T, enable_if_t<(std::is_same<T, wchar_t>::value || +#ifdef __cpp_char8_t + std::is_same<T, char8_t>::value || +#endif + std::is_same<T, char16_t>::value || + std::is_same<T, char32_t>::value) && + !std::is_same<T, char_type>::value, + int> = 0> + FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char { + return {}; + } + + FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double { + return val; + } + + FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* { + return val; + } + template <typename T, + FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value && + std::is_same<char_type, char_t<T>>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view<char_type> { + return to_string_view(val); + } + template <typename T, + FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value && + !std::is_same<char_type, char_t<T>>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char { + return {}; + } + template <typename T, + FMT_ENABLE_IF( + std::is_convertible<T, basic_string_view<char_type>>::value && + !is_string<T>::value && !has_formatter<T, Context>::value && + !has_fallback_formatter<T, char_type>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view<char_type> { + return basic_string_view<char_type>(val); + } + template <typename T, + FMT_ENABLE_IF( + std::is_convertible<T, std_string_view<char_type>>::value && + !std::is_convertible<T, basic_string_view<char_type>>::value && + !is_string<T>::value && !has_formatter<T, Context>::value && + !has_fallback_formatter<T, char_type>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view<char_type> { + return std_string_view<char_type>(val); + } + + FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* { + return val; + } + + // We use SFINAE instead of a const T* parameter to avoid conflicting with + // the C array overload. + template < + typename T, + FMT_ENABLE_IF( + std::is_pointer<T>::value || std::is_member_pointer<T>::value || + std::is_function<typename std::remove_pointer<T>::type>::value || + (std::is_convertible<const T&, const void*>::value && + !std::is_convertible<const T&, const char_type*>::value && + !has_formatter<T, Context>::value))> + FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { + return {}; + } + + template <typename T, std::size_t N, + FMT_ENABLE_IF(!std::is_same<T, wchar_t>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] { + return values; + } + + template <typename T, + FMT_ENABLE_IF( + std::is_enum<T>::value&& std::is_convertible<T, int>::value && + !has_format_as<T>::value && !has_formatter<T, Context>::value && + !has_fallback_formatter<T, char_type>::value)> + FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> decltype(std::declval<arg_mapper>().map( + static_cast<underlying_t<T>>(val))) { + return map(static_cast<underlying_t<T>>(val)); + } + + template <typename T, FMT_ENABLE_IF(has_format_as<T>::value && + !has_formatter<T, Context>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> decltype(std::declval<arg_mapper>().map(format_as(T()))) { + return map(format_as(val)); + } + + template <typename T, typename U = remove_cvref_t<T>> + struct formattable + : bool_constant<has_const_formatter<U, Context>() || + !std::is_const<remove_reference_t<T>>::value || + has_fallback_formatter<U, char_type>::value> {}; + +#if (FMT_MSC_VERSION != 0 && FMT_MSC_VERSION < 1910) || \ + FMT_ICC_VERSION != 0 || defined(__NVCC__) + // Workaround a bug in MSVC and Intel (Issue 2746). + template <typename T> FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { + return val; + } +#else + template <typename T, FMT_ENABLE_IF(formattable<T>::value)> + FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { + return val; + } + template <typename T, FMT_ENABLE_IF(!formattable<T>::value)> + FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable_const { + return {}; + } +#endif + + template <typename T, typename U = remove_cvref_t<T>, + FMT_ENABLE_IF(!is_string<U>::value && !is_char<U>::value && + !std::is_array<U>::value && + !std::is_pointer<U>::value && + !has_format_as<U>::value && + (has_formatter<U, Context>::value || + has_fallback_formatter<U, char_type>::value))> + FMT_CONSTEXPR FMT_INLINE auto map(T&& val) + -> decltype(this->do_map(std::forward<T>(val))) { + return do_map(std::forward<T>(val)); + } + + template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg) + -> decltype(std::declval<arg_mapper>().map(named_arg.value)) { + return map(named_arg.value); + } + + auto map(...) -> unformattable { return {}; } +}; + +// 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 }; + +FMT_END_DETAIL_NAMESPACE + +// An output iterator that appends to a buffer. +// It is used to reduce symbol sizes for the common case. +class appender : public std::back_insert_iterator<detail::buffer<char>> { + using base = std::back_insert_iterator<detail::buffer<char>>; + + template <typename T> + friend auto get_buffer(appender out) -> detail::buffer<char>& { + return detail::get_container(out); + } + + public: + using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator; + appender(base it) noexcept : base(it) {} + FMT_UNCHECKED_ITERATOR(appender); + + auto operator++() noexcept -> appender& { return *this; } + auto operator++(int) noexcept -> appender { return *this; } +}; + +// 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 auto detail::make_arg(T&& value) + -> basic_format_arg<ContextType>; + + 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 noexcept { + return type_ != detail::type::none_type; + } + + auto type() const -> detail::type { return type_; } + + auto is_integral() const -> bool { return detail::is_integral_type(type_); } + auto is_arithmetic() const -> bool { + 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 FMT_INLINE auto visit_format_arg( + Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) { + 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); + case detail::type::int128_type: + return vis(detail::convert_for_visit(arg.value_.int128_value)); + case detail::type::uint128_type: + return vis(detail::convert_for_visit(arg.value_.uint128_value)); + 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: + using sv = basic_string_view<typename Context::char_type>; + return vis(sv(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()); +} + +FMT_BEGIN_DETAIL_NAMESPACE + +template <typename Char, typename InputIt> +auto copy_str(InputIt begin, InputIt end, appender out) -> appender { + get_container(out).append(begin, end); + return out; +} + +template <typename Char, typename R, typename OutputIt> +FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt { + return detail::copy_str<Char>(rng.begin(), rng.end(), out); +} + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 +// 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; +#else +template <typename...> using void_t = void; +#endif + +template <typename It, typename T, typename Enable = void> +struct is_output_iterator : std::false_type {}; + +template <typename It, typename T> +struct is_output_iterator< + It, T, + void_t<typename std::iterator_traits<It>::iterator_category, + decltype(*std::declval<It>() = std::declval<T>())>> + : std::true_type {}; + +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> {}; +template <> +struct is_contiguous_back_insert_iterator<appender> : std::true_type {}; + +// A type-erased reference to an std::locale to avoid a heavy <locale> include. +class locale_ref { + private: + const void* locale_; // A type-erased pointer to std::locale. + + public: + constexpr locale_ref() : locale_(nullptr) {} + template <typename Locale> explicit locale_ref(const Locale& loc); + + explicit operator bool() const noexcept { return locale_ != nullptr; } + + template <typename Locale> auto get() const -> Locale; +}; + +template <typename> constexpr auto encode_types() -> unsigned long long { + return 0; +} + +template <typename Context, typename Arg, typename... Args> +constexpr auto encode_types() -> unsigned long long { + return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) | + (encode_types<Context, Args...>() << packed_arg_bits); +} + +template <typename Context, typename T> +FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value<Context> { + const auto& arg = arg_mapper<Context>().map(FMT_FORWARD(val)); + + constexpr bool formattable_char = + !std::is_same<decltype(arg), const unformattable_char&>::value; + static_assert(formattable_char, "Mixing character types is disallowed."); + + constexpr bool formattable_const = + !std::is_same<decltype(arg), const unformattable_const&>::value; + static_assert(formattable_const, "Cannot format a const argument."); + + // 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. + constexpr bool formattable_pointer = + !std::is_same<decltype(arg), const unformattable_pointer&>::value; + static_assert(formattable_pointer, + "Formatting of non-void pointers is disallowed."); + + constexpr bool formattable = + !std::is_same<decltype(arg), const unformattable&>::value; + static_assert( + formattable, + "Cannot format an argument. To make type T formattable provide a " + "formatter<T> specialization: https://fmt.dev/latest/api.html#udt"); + return {arg}; +} + +template <typename Context, typename T> +FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context> { + basic_format_arg<Context> arg; + arg.type_ = mapped_type_constant<T, Context>::value; + arg.value_ = make_value<Context>(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)> +FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> { + return make_value<Context>(val); +} + +template <bool IS_PACKED, typename Context, type, typename T, + FMT_ENABLE_IF(!IS_PACKED)> +FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg<Context> { + return make_arg<Context>(value); +} +FMT_END_DETAIL_NAMESPACE + +// 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(basic_format_context&&) = default; + 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. + */ + constexpr 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) {} + + constexpr auto arg(int id) const -> format_arg { return args_.get(id); } + FMT_CONSTEXPR auto arg(basic_string_view<char_type> name) -> format_arg { + return args_.get(name); + } + FMT_CONSTEXPR auto arg_id(basic_string_view<char_type> name) -> int { + return args_.get_id(name); + } + auto args() const -> const basic_format_args<basic_format_context>& { + return args_; + } + + FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; } + void on_error(const char* message) { error_handler().on_error(message); } + + // Returns an iterator to the beginning of the output range. + FMT_CONSTEXPR auto out() -> iterator { return out_; } + + // Advances the begin iterator to ``it``. + void advance_to(iterator it) { + if (!detail::is_back_insert_iterator<iterator>()) out_ = it; + } + + FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } +}; + +template <typename Char> +using buffer_context = + basic_format_context<detail::buffer_appender<Char>, Char>; +using format_context = buffer_context<char>; + +// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164. +#define FMT_BUFFER_CONTEXT(Char) \ + basic_format_context<detail::buffer_appender<Char>, Char> + +template <typename T, typename Char = char> +using is_formattable = bool_constant< + !std::is_base_of<detail::unformattable, + decltype(detail::arg_mapper<buffer_context<Char>>().map( + std::declval<T>()))>::value && + !detail::has_fallback_formatter<T, Char>::value>; + +/** + \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: + template <typename... T> + FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... 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<remove_cvref_t<T>, Context>::value>( + FMT_FORWARD(args))...} { + detail::init_named_args(data_.named_args(), 0, 0, args...); + } +}; + +/** + \rst + Constructs a `~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> +constexpr auto make_format_args(Args&&... args) + -> format_arg_store<Context, remove_cvref_t<Args>...> { + return {FMT_FORWARD(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 or + `dynamic_format_arg_store::push_back`. + + **Example**:: + + fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); + \endrst + */ +template <typename Char, typename T> +inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> { + static_assert(!detail::is_named_arg<T>(), "nested named arguments"); + return {name, arg}; +} + +/** + \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_; + }; + + constexpr auto is_packed() const -> bool { + return (desc_ & detail::is_unpacked_bit) == 0; + } + auto has_named_args() const -> bool { + return (desc_ & detail::has_named_args_bit) != 0; + } + + FMT_CONSTEXPR auto type(int index) const -> detail::type { + int shift = index * detail::packed_arg_bits; + unsigned int mask = (1 << detail::packed_arg_bits) - 1; + return static_cast<detail::type>((desc_ >> shift) & mask); + } + + constexpr FMT_INLINE basic_format_args(unsigned long long desc, + const detail::value<Context>* values) + : desc_(desc), values_(values) {} + constexpr basic_format_args(unsigned long long desc, const format_arg* args) + : desc_(desc), args_(args) {} + + public: + constexpr basic_format_args() : desc_(0), args_(nullptr) {} + + /** + \rst + Constructs a `basic_format_args` object from `~fmt::format_arg_store`. + \endrst + */ + template <typename... Args> + constexpr FMT_INLINE basic_format_args( + const format_arg_store<Context, Args...>& store) + : basic_format_args(format_arg_store<Context, Args...>::desc, + store.data_.args()) {} + + /** + \rst + Constructs a `basic_format_args` object from + `~fmt::dynamic_format_arg_store`. + \endrst + */ + constexpr 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 + */ + constexpr 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. */ + FMT_CONSTEXPR auto get(int id) const -> format_arg { + 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> + auto get(basic_string_view<Char> name) const -> format_arg { + int id = get_id(name); + return id >= 0 ? get(id) : format_arg(); + } + + template <typename Char> + auto get_id(basic_string_view<Char> name) const -> int { + 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; + } + + auto max_size() const -> int { + 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<format_context>``. */ +// A separate type would result in shorter symbols but break ABI compatibility +// between clang and gcc on ARM (#1919). +using format_args = basic_format_args<format_context>; + +// We cannot use enum classes as bit fields because of a gcc bug, so we put them +// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414). +// Additionally, if an underlying type is specified, older gcc incorrectly warns +// that the type is too small. Both bugs are fixed in gcc 9.3. +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903 +# define FMT_ENUM_UNDERLYING_TYPE(type) +#else +# define FMT_ENUM_UNDERLYING_TYPE(type) : type +#endif +namespace align { +enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center, + numeric}; +} +using align_t = align::type; +namespace sign { +enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space}; +} +using sign_t = sign::type; + +FMT_BEGIN_DETAIL_NAMESPACE + +// Workaround an array initialization issue in gcc 4.8. +template <typename Char> struct fill_t { + private: + enum { max_size = 4 }; + Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)}; + unsigned char size_ = 1; + + public: + FMT_CONSTEXPR void operator=(basic_string_view<Char> s) { + auto size = s.size(); + if (size > max_size) return throw_format_error("invalid fill"); + for (size_t i = 0; i < size; ++i) data_[i] = s[i]; + size_ = static_cast<unsigned char>(size); + } + + constexpr auto size() const -> size_t { return size_; } + constexpr auto data() const -> const Char* { return data_; } + + FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; } + FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& { + return data_[index]; + } +}; +FMT_END_DETAIL_NAMESPACE + +enum class presentation_type : unsigned char { + none, + // Integer types should go first, + dec, // 'd' + oct, // 'o' + hex_lower, // 'x' + hex_upper, // 'X' + bin_lower, // 'b' + bin_upper, // 'B' + hexfloat_lower, // 'a' + hexfloat_upper, // 'A' + exp_lower, // 'e' + exp_upper, // 'E' + fixed_lower, // 'f' + fixed_upper, // 'F' + general_lower, // 'g' + general_upper, // 'G' + chr, // 'c' + string, // 's' + pointer, // 'p' + debug // '?' +}; + +// Format specifiers for built-in and string types. +template <typename Char> struct basic_format_specs { + int width; + int precision; + presentation_type type; + align_t align : 4; + sign_t sign : 3; + bool alt : 1; // Alternate form ('#'). + bool localized : 1; + detail::fill_t<Char> fill; + + constexpr basic_format_specs() + : width(0), + precision(-1), + type(presentation_type::none), + align(align::none), + sign(sign::none), + alt(false), + localized(false) {} +}; + +using format_specs = basic_format_specs<char>; + +FMT_BEGIN_DETAIL_NAMESPACE + +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 auto operator=(int idx) -> arg_ref& { + 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; +}; + +struct auto_id {}; + +// A format specifier handler that sets fields in basic_format_specs. +template <typename Char> class specs_setter { + protected: + basic_format_specs<Char>& specs_; + + 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_sign(sign_t s) { specs_.sign = s; } + FMT_CONSTEXPR void on_hash() { specs_.alt = true; } + FMT_CONSTEXPR void on_localized() { specs_.localized = true; } + + FMT_CONSTEXPR void on_zero() { + if (specs_.align == align::none) 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(presentation_type type) { specs_.type = type; } +}; + +// 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: + dynamic_format_specs<char_type>& specs_; + ParseContext& context_; + + using arg_ref_type = arg_ref<char_type>; + + FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type { + context_.check_arg_id(arg_id); + context_.check_dynamic_spec(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type { + int arg_id = context_.next_arg_id(); + context_.check_dynamic_spec(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR auto make_arg_ref(basic_string_view<char_type> arg_id) + -> arg_ref_type { + 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); + } +}; + +template <typename Char> constexpr bool is_ascii_letter(Char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +// Converts a character to ASCII. Returns a number > 127 on conversion failure. +template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)> +constexpr auto to_ascii(Char c) -> Char { + return c; +} +template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)> +constexpr auto to_ascii(Char c) -> underlying_t<Char> { + return c; +} + +FMT_CONSTEXPR inline auto code_point_length_impl(char c) -> int { + return "\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" + [static_cast<unsigned char>(c) >> 3]; +} + +template <typename Char> +FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { + if (const_check(sizeof(Char) != 1)) return 1; + int len = code_point_length_impl(static_cast<char>(*begin)); + + // 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. + return len + !len; +} + +// Return the result via the out param to workaround gcc bug 77539. +template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*> +FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { + for (out = first; out != last; ++out) { + if (*out == value) return true; + } + return false; +} + +template <> +inline auto find<false, char>(const char* first, const char* last, char value, + const char*& out) -> bool { + out = static_cast<const char*>( + std::memchr(first, value, to_unsigned(last - first))); + return out != nullptr; +} + +// 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> +FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, + int error_value) noexcept -> int { + FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); + unsigned value = 0, prev = 0; + auto p = begin; + do { + prev = value; + value = value * 10 + unsigned(*p - '0'); + ++p; + } while (p != end && '0' <= *p && *p <= '9'); + auto num_digits = p - begin; + begin = p; + if (num_digits <= std::numeric_limits<int>::digits10) + return static_cast<int>(value); + // Check for overflow. + const unsigned max = to_unsigned((std::numeric_limits<int>::max)()); + return num_digits == std::numeric_limits<int>::digits10 + 1 && + prev * 10ull + unsigned(p[-1] - '0') <= max + ? static_cast<int>(value) + : error_value; +} + +// Parses fill and alignment. +template <typename Char, typename Handler> +FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + FMT_ASSERT(begin != end, ""); + auto align = align::none; + auto p = begin + code_point_length(begin); + if (end - p <= 0) p = begin; + for (;;) { + switch (to_ascii(*p)) { + case '<': + align = align::left; + break; + case '>': + align = align::right; + break; + case '^': + align = align::center; + break; + default: + 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> FMT_CONSTEXPR bool is_name_start(Char c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; +} + +template <typename Char, typename IDHandler> +FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, + IDHandler&& handler) -> const Char* { + FMT_ASSERT(begin != end, ""); + Char c = *begin; + if (c >= '0' && c <= '9') { + int index = 0; + if (c != '0') + index = + parse_nonnegative_int(begin, end, (std::numeric_limits<int>::max)()); + 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; +} + +template <typename Char, typename IDHandler> +FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end, + IDHandler&& handler) -> const Char* { + Char c = *begin; + if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler); + handler(); + return begin; +} + +template <typename Char, typename Handler> +FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + using detail::auto_id; + struct width_adapter { + Handler& handler; + + 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) { + if (message) handler.on_error(message); + } + }; + + FMT_ASSERT(begin != end, ""); + if ('0' <= *begin && *begin <= '9') { + int width = parse_nonnegative_int(begin, end, -1); + if (width != -1) + handler.on_width(width); + else + handler.on_error("number is too big"); + } else if (*begin == '{') { + ++begin; + if (begin != end) begin = parse_arg_id(begin, end, width_adapter{handler}); + if (begin == end || *begin != '}') + return handler.on_error("invalid format string"), begin; + ++begin; + } + return begin; +} + +template <typename Char, typename Handler> +FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + using detail::auto_id; + struct precision_adapter { + Handler& handler; + + 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) { + if (message) handler.on_error(message); + } + }; + + ++begin; + auto c = begin != end ? *begin : Char(); + if ('0' <= c && c <= '9') { + auto precision = parse_nonnegative_int(begin, end, -1); + if (precision != -1) + handler.on_precision(precision); + else + handler.on_error("number is too big"); + } else if (c == '{') { + ++begin; + if (begin != end) + begin = parse_arg_id(begin, end, precision_adapter{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; +} + +template <typename Char> +FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type { + switch (to_ascii(type)) { + case 'd': + return presentation_type::dec; + case 'o': + return presentation_type::oct; + case 'x': + return presentation_type::hex_lower; + case 'X': + return presentation_type::hex_upper; + case 'b': + return presentation_type::bin_lower; + case 'B': + return presentation_type::bin_upper; + case 'a': + return presentation_type::hexfloat_lower; + case 'A': + return presentation_type::hexfloat_upper; + case 'e': + return presentation_type::exp_lower; + case 'E': + return presentation_type::exp_upper; + case 'f': + return presentation_type::fixed_lower; + case 'F': + return presentation_type::fixed_upper; + case 'g': + return presentation_type::general_lower; + case 'G': + return presentation_type::general_upper; + case 'c': + return presentation_type::chr; + case 's': + return presentation_type::string; + case 'p': + return presentation_type::pointer; + case '?': + return presentation_type::debug; + default: + return presentation_type::none; + } +} + +// Parses standard format specifiers and sends notifications about parsed +// components to handler. +template <typename Char, typename SpecHandler> +FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin, + const Char* end, + SpecHandler&& handler) + -> const Char* { + if (1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) && + *begin != 'L') { + presentation_type type = parse_presentation_type(*begin++); + if (type == presentation_type::none) + handler.on_error("invalid type specifier"); + handler.on_type(type); + return begin; + } + + if (begin == end) return begin; + + begin = parse_align(begin, end, handler); + if (begin == end) return begin; + + // Parse sign. + switch (to_ascii(*begin)) { + case '+': + handler.on_sign(sign::plus); + ++begin; + break; + case '-': + handler.on_sign(sign::minus); + ++begin; + break; + case ' ': + handler.on_sign(sign::space); + ++begin; + break; + default: + 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); + if (begin == end) return begin; + } + + if (*begin == 'L') { + handler.on_localized(); + ++begin; + } + + // Parse type. + if (begin != end && *begin != '}') { + presentation_type type = parse_presentation_type(*begin++); + if (type == presentation_type::none) + handler.on_error("invalid type specifier"); + handler.on_type(type); + } + return begin; +} + +template <typename Char, typename Handler> +FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end, + Handler&& handler) -> const 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) { + if (message) handler.on_error(message); + } + }; + + ++begin; + if (begin == end) return handler.on_error("invalid format string"), end; + if (*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, 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 FMT_INLINE void parse_format_string( + basic_string_view<Char> format_str, Handler&& handler) { + // Workaround a name-lookup bug in MSVC's modules implementation. + using detail::find; + + 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* from, const Char* to) { + if (from == to) return; + for (;;) { + const Char* p = nullptr; + if (!find<IS_CONSTEXPR>(from, to, Char('}'), p)) + return handler_.on_text(from, to); + ++p; + if (p == to || *p != '}') + return handler_.on_error("unmatched '}' in format string"); + handler_.on_text(from, p); + from = 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, Char('{'), p)) + return write(begin, end); + write(begin, p); + begin = parse_replacement_field(p, end, handler); + } +} + +template <typename T, bool = is_named_arg<T>::value> struct strip_named_arg { + using type = T; +}; +template <typename T> struct strip_named_arg<T, true> { + using type = remove_cvref_t<decltype(T::value)>; +}; + +template <typename T, typename ParseContext> +FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) + -> decltype(ctx.begin()) { + using char_type = typename ParseContext::char_type; + using context = buffer_context<char_type>; + using stripped_type = typename strip_named_arg<T>::type; + using mapped_type = conditional_t< + mapped_type_constant<T, context>::value != type::custom_type, + decltype(arg_mapper<context>().map(std::declval<const T&>())), + stripped_type>; + auto f = conditional_t<has_formatter<mapped_type, context>::value, + formatter<mapped_type, char_type>, + fallback_formatter<stripped_type, char_type>>(); + return f.parse(ctx); +} + +template <typename ErrorHandler> +FMT_CONSTEXPR void check_int_type_spec(presentation_type type, + ErrorHandler&& eh) { + if (type > presentation_type::bin_upper && type != presentation_type::chr) + eh.on_error("invalid type specifier"); +} + +// Checks char specs and returns true if the type spec is char (and not int). +template <typename Char, typename ErrorHandler = error_handler> +FMT_CONSTEXPR auto check_char_specs(const basic_format_specs<Char>& specs, + ErrorHandler&& eh = {}) -> bool { + if (specs.type != presentation_type::none && + specs.type != presentation_type::chr && + specs.type != presentation_type::debug) { + check_int_type_spec(specs.type, eh); + return false; + } + if (specs.align == align::numeric || specs.sign != sign::none || specs.alt) + eh.on_error("invalid format specifier for char"); + return true; +} + +// 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 showpoint : 1; +}; + +template <typename ErrorHandler = error_handler, typename Char> +FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs<Char>& specs, + ErrorHandler&& eh = {}) + -> float_specs { + auto result = float_specs(); + result.showpoint = specs.alt; + result.locale = specs.localized; + switch (specs.type) { + case presentation_type::none: + result.format = float_format::general; + break; + case presentation_type::general_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::general_lower: + result.format = float_format::general; + break; + case presentation_type::exp_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::exp_lower: + result.format = float_format::exp; + result.showpoint |= specs.precision != 0; + break; + case presentation_type::fixed_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::fixed_lower: + result.format = float_format::fixed; + result.showpoint |= specs.precision != 0; + break; + case presentation_type::hexfloat_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::hexfloat_lower: + result.format = float_format::hex; + break; + default: + eh.on_error("invalid type specifier"); + break; + } + return result; +} + +template <typename ErrorHandler = error_handler> +FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type, + ErrorHandler&& eh = {}) -> bool { + if (type == presentation_type::none || type == presentation_type::string || + type == presentation_type::debug) + return true; + if (type != presentation_type::pointer) eh.on_error("invalid type specifier"); + return false; +} + +template <typename ErrorHandler = error_handler> +FMT_CONSTEXPR void check_string_type_spec(presentation_type type, + ErrorHandler&& eh = {}) { + if (type != presentation_type::none && type != presentation_type::string && + type != presentation_type::debug) + eh.on_error("invalid type specifier"); +} + +template <typename ErrorHandler> +FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type, + ErrorHandler&& eh) { + if (type != presentation_type::none && type != presentation_type::pointer) + eh.on_error("invalid type specifier"); +} + +// A parse_format_specs handler that checks if specifiers are consistent with +// the argument type. +template <typename Handler> class specs_checker : public Handler { + private: + detail::type arg_type_; + + FMT_CONSTEXPR void require_numeric_argument() { + if (!is_arithmetic_type(arg_type_)) + this->on_error("format specifier requires numeric argument"); + } + + public: + FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type) + : Handler(handler), arg_type_(arg_type) {} + + FMT_CONSTEXPR void on_align(align_t align) { + if (align == align::numeric) require_numeric_argument(); + Handler::on_align(align); + } + + FMT_CONSTEXPR void on_sign(sign_t s) { + require_numeric_argument(); + if (is_integral_type(arg_type_) && arg_type_ != type::int_type && + arg_type_ != type::long_long_type && arg_type_ != type::int128_type && + arg_type_ != type::char_type) { + this->on_error("format specifier requires signed argument"); + } + Handler::on_sign(s); + } + + FMT_CONSTEXPR void on_hash() { + require_numeric_argument(); + Handler::on_hash(); + } + + FMT_CONSTEXPR void on_localized() { + require_numeric_argument(); + Handler::on_localized(); + } + + FMT_CONSTEXPR void on_zero() { + require_numeric_argument(); + Handler::on_zero(); + } + + FMT_CONSTEXPR void end_precision() { + if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type) + this->on_error("precision not allowed for this argument type"); + } +}; + +constexpr int invalid_arg_index = -1; + +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +template <int N, typename T, typename... Args, typename Char> +constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int { + if constexpr (detail::is_statically_named_arg<T>()) { + if (name == T::name) return N; + } + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name<N + 1, Args...>(name); + (void)name; // Workaround an MSVC bug about "unused" parameter. + return invalid_arg_index; +} +#endif + +template <typename... Args, typename Char> +FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int { +#if FMT_USE_NONTYPE_TEMPLATE_ARGS + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name<0, Args...>(name); +#endif + (void)name; + return invalid_arg_index; +} + +template <typename Char, typename ErrorHandler, typename... Args> +class format_string_checker { + private: + // In the future basic_format_parse_context will replace compile_parse_context + // here and will use is_constant_evaluated and downcasting to access the data + // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1. + using parse_context_type = compile_parse_context<Char, ErrorHandler>; + static constexpr int 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 ? static_cast<size_t>(num_args) : 1]; + type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1]; + + public: + explicit FMT_CONSTEXPR format_string_checker( + basic_string_view<Char> format_str, ErrorHandler eh) + : context_(format_str, num_args, types_, eh), + parse_funcs_{&parse_format_specs<Args, parse_context_type>...}, + types_{ + mapped_type_constant<Args, + basic_format_context<Char*, Char>>::value...} { + } + + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + + FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } + FMT_CONSTEXPR auto on_arg_id(int id) -> int { + return context_.check_arg_id(id), id; + } + FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int { +#if FMT_USE_NONTYPE_TEMPLATE_ARGS + auto index = get_arg_index_by_name<Args...>(id); + if (index == invalid_arg_index) on_error("named argument is not found"); + return context_.check_arg_id(index), index; +#else + (void)id; + on_error("compile-time checks for named arguments require C++20 support"); + return 0; +#endif + } + + FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} + + FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*) + -> const Char* { + context_.advance_to(context_.begin() + (begin - &*context_.begin())); + // id >= 0 check is a workaround for gcc 10 bug (#2065). + return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin; + } + + FMT_CONSTEXPR void on_error(const char* message) { + context_.on_error(message); + } +}; + +// 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... Args, typename S, + FMT_ENABLE_IF(is_compile_string<S>::value)> +void check_format_string(S format_str) { + FMT_CONSTEXPR auto s = basic_string_view<typename S::char_type>(format_str); + using checker = format_string_checker<typename S::char_type, error_handler, + remove_cvref_t<Args>...>; + FMT_CONSTEXPR bool invalid_format = + (parse_format_string<true>(s, checker(s, {})), true); + ignore_unused(invalid_format); +} + +template <typename Char> +void vformat_to( + buffer<Char>& buf, basic_string_view<Char> fmt, + basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args, + locale_ref loc = {}); + +FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); +#ifndef _WIN32 +inline void vprint_mojibake(std::FILE*, string_view, format_args) {} +#endif +FMT_END_DETAIL_NAMESPACE + +// 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>> { + private: + detail::dynamic_format_specs<Char> specs_; + + public: + // 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()) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin == end) return begin; + using handler_type = detail::dynamic_specs_handler<ParseContext>; + auto type = detail::type_constant<T, Char>::value; + auto checker = + detail::specs_checker<handler_type>(handler_type(specs_, ctx), type); + auto it = detail::parse_format_specs(begin, end, checker); + auto eh = ctx.error_handler(); + switch (type) { + case detail::type::none_type: + FMT_ASSERT(false, "invalid argument type"); + break; + case detail::type::bool_type: + if (specs_.type == presentation_type::none || + specs_.type == presentation_type::string) { + break; + } + FMT_FALLTHROUGH; + 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: + detail::check_int_type_spec(specs_.type, eh); + break; + case detail::type::char_type: + detail::check_char_specs(specs_, 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::check_cstring_type_spec(specs_.type, 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 are checked in parse functions of + // formatter specializations. + break; + } + return it; + } + + template <detail::type U = detail::type_constant<T, Char>::value, + enable_if_t<(U == detail::type::string_type || + U == detail::type::cstring_type || + U == detail::type::char_type), + int> = 0> + FMT_CONSTEXPR void set_debug_format() { + specs_.type = presentation_type::debug; + } + + template <typename FormatContext> + FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const + -> decltype(ctx.out()); +}; + +#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) const \ + -> decltype(ctx.out()) { \ + return formatter<Base, Char>::format(static_cast<Base>(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 basic_runtime { basic_string_view<Char> str; }; + +/** A compile-time format string. */ +template <typename Char, typename... Args> class basic_format_string { + private: + basic_string_view<Char> str_; + + public: + template <typename S, + FMT_ENABLE_IF( + std::is_convertible<const S&, basic_string_view<Char>>::value)> + FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) { + static_assert( + detail::count< + (std::is_base_of<detail::view, remove_reference_t<Args>>::value && + std::is_reference<Args>::value)...>() == 0, + "passing views as lvalues is disallowed"); +#ifdef FMT_HAS_CONSTEVAL + if constexpr (detail::count_named_args<Args...>() == + detail::count_statically_named_args<Args...>()) { + using checker = detail::format_string_checker<Char, detail::error_handler, + remove_cvref_t<Args>...>; + detail::parse_format_string<true>(str_, checker(s, {})); + } +#else + detail::check_format_string<Args...>(s); +#endif + } + basic_format_string(basic_runtime<Char> r) : str_(r.str) {} + + FMT_INLINE operator basic_string_view<Char>() const { return str_; } +}; + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +// Workaround broken conversion on older gcc. +template <typename...> using format_string = string_view; +inline auto runtime(string_view s) -> string_view { return s; } +#else +template <typename... Args> +using format_string = basic_format_string<char, type_identity_t<Args>...>; +/** + \rst + Creates a runtime format string. + + **Example**:: + + // Check format string at runtime instead of compile-time. + fmt::print(fmt::runtime("{:d}"), "I am not a number"); + \endrst + */ +inline auto runtime(string_view s) -> basic_runtime<char> { return {{s}}; } +#endif + +FMT_API auto vformat(string_view fmt, format_args args) -> std::string; + +/** + \rst + Formats ``args`` according to specifications in ``fmt`` and returns the result + as a string. + + **Example**:: + + #include <fmt/core.h> + std::string message = fmt::format("The answer is {}.", 42); + \endrst +*/ +template <typename... T> +FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T&&... args) + -> std::string { + return vformat(fmt, fmt::make_format_args(args...)); +} + +/** Formats a string and writes the output to ``out``. */ +template <typename OutputIt, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)> +auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { + using detail::get_buffer; + auto&& buf = get_buffer<char>(out); + detail::vformat_to(buf, fmt, args, {}); + return detail::get_iterator(buf); +} + +/** + \rst + Formats ``args`` according to specifications in ``fmt``, writes the result to + the output iterator ``out`` and returns the iterator past the end of the output + range. `format_to` does not append a terminating null character. + + **Example**:: + + auto out = std::vector<char>(); + fmt::format_to(std::back_inserter(out), "{}", 42); + \endrst + */ +template <typename OutputIt, typename... T, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)> +FMT_INLINE auto format_to(OutputIt out, format_string<T...> fmt, T&&... args) + -> OutputIt { + return vformat_to(out, fmt, fmt::make_format_args(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... T, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)> +auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) + -> format_to_n_result<OutputIt> { + using traits = detail::fixed_buffer_traits; + auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n); + detail::vformat_to(buf, fmt, args, {}); + return {buf.out(), buf.count()}; +} + +/** + \rst + Formats ``args`` according to specifications in ``fmt``, writes up to ``n`` + characters of the result to the output iterator ``out`` and returns the total + (not truncated) output size and the iterator past the end of the output range. + `format_to_n` does not append a terminating null character. + \endrst + */ +template <typename OutputIt, typename... T, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)> +FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt, + T&&... args) -> format_to_n_result<OutputIt> { + return vformat_to_n(out, n, fmt, fmt::make_format_args(args...)); +} + +/** Returns the number of chars in the output of ``format(fmt, args...)``. */ +template <typename... T> +FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt, + T&&... args) -> size_t { + auto buf = detail::counting_buffer<>(); + detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...), {}); + return buf.count(); +} + +FMT_API void vprint(string_view fmt, format_args args); +FMT_API void vprint(std::FILE* f, string_view fmt, format_args args); + +/** + \rst + Formats ``args`` according to specifications in ``fmt`` and writes the output + to ``stdout``. + + **Example**:: + + fmt::print("Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template <typename... T> +FMT_INLINE void print(format_string<T...> fmt, T&&... args) { + const auto& vargs = fmt::make_format_args(args...); + return detail::is_utf8() ? vprint(fmt, vargs) + : detail::vprint_mojibake(stdout, fmt, vargs); +} + +/** + \rst + Formats ``args`` according to specifications in ``fmt`` and writes the + output to the file ``f``. + + **Example**:: + + fmt::print(stderr, "Don't {}!", "panic"); + \endrst + */ +template <typename... T> +FMT_INLINE void print(std::FILE* f, format_string<T...> fmt, T&&... args) { + const auto& vargs = fmt::make_format_args(args...); + return detail::is_utf8() ? vprint(f, fmt, vargs) + : detail::vprint_mojibake(f, fmt, vargs); +} + +FMT_MODULE_EXPORT_END +FMT_GCC_PRAGMA("GCC pop_options") +FMT_END_NAMESPACE + +#ifdef FMT_HEADER_ONLY +# include "format.h" +#endif +#endif // FMT_CORE_H_ diff --git a/src/fmt/include/fmt/format-inl.h b/src/fmt/include/fmt/format-inl.h new file mode 100644 index 000000000..22b1ec8df --- /dev/null +++ b/src/fmt/include/fmt/format-inl.h @@ -0,0 +1,1723 @@ +// 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 <algorithm> +#include <cctype> +#include <cerrno> // errno +#include <climits> +#include <cmath> +#include <cstdarg> +#include <cstring> // std::memmove +#include <cwchar> +#include <exception> + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +# include <locale> +#endif + +#ifdef _WIN32 +# include <io.h> // _isatty +#endif + +#include "format.h" + +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(); +} + +FMT_FUNC void throw_format_error(const char* message) { + FMT_THROW(format_error(message)); +} + +FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code, + string_view message) noexcept { + // Report error code making sure that the output fits into + // inline_buffer_size to avoid dynamic memory allocation and potential + // bad_alloc. + out.try_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 = buffer_appender<char>(out); + if (message.size() <= inline_buffer_size - error_code_size) + format_to(it, FMT_STRING("{}{}"), message, SEP); + format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); + FMT_ASSERT(out.size() <= inline_buffer_size, ""); +} + +FMT_FUNC void report_error(format_func func, int error_code, + const char* message) noexcept { + memory_buffer full_message; + func(full_message, error_code, message); + // Don't use fwrite_fully because the latter may throw. + if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0) + std::fputc('\n', stderr); +} + +// A wrapper around fwrite that throws on error. +inline 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, FMT_STRING("cannot write to file"))); +} + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +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 auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> { + auto& facet = std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()); + auto grouping = facet.grouping(); + auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); + return {std::move(grouping), 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(); +} +#else +template <typename Char> +FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result<Char> { + return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR}; +} +template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) { + return '.'; +} +#endif +} // namespace detail + +#if !FMT_MSC_VERSION +FMT_API FMT_FUNC format_error::~format_error() noexcept = default; +#endif + +FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str, + format_args args) { + auto ec = std::error_code(error_code, std::generic_category()); + return std::system_error(ec, vformat(format_str, args)); +} + +namespace detail { + +template <typename F> inline bool operator==(basic_fp<F> x, basic_fp<F> y) { + return x.f == y.f && x.e == y.e; +} + +// Compilers should be able to optimize this into the ror instruction. +FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept { + r &= 31; + return (n >> r) | (n << (32 - r)); +} +FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept { + r &= 63; + return (n >> r) | (n << (64 - r)); +} + +// Computes 128-bit result of multiplication of two 64-bit unsigned integers. +inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept { +#if FMT_USE_INT128 + auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y); + return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)}; +#elif defined(_MSC_VER) && defined(_M_X64) + auto result = uint128_fallback(); + result.lo_ = _umul128(x, y, &result.hi_); + return result; +#else + const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>()); + + uint64_t a = x >> 32; + uint64_t b = x & mask; + uint64_t c = y >> 32; + uint64_t d = y & mask; + + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; + + uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask); + + return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), + (intermediate << 32) + (bd & mask)}; +#endif +} + +// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. +namespace dragonbox { +// Computes upper 64 bits of multiplication of two 64-bit unsigned integers. +inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { +#if FMT_USE_INT128 + auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y); + return static_cast<uint64_t>(p >> 64); +#elif defined(_MSC_VER) && defined(_M_X64) + return __umulh(x, y); +#else + return umul128(x, y).high(); +#endif +} + +// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a +// 128-bit unsigned integer. +inline uint128_fallback umul192_upper128(uint64_t x, + uint128_fallback y) noexcept { + uint128_fallback r = umul128(x, y.high()); + r += umul128_upper64(x, y.low()); + return r; +} + +// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a +// 64-bit unsigned integer. +inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept { + return umul128_upper64(static_cast<uint64_t>(x) << 32, y); +} + +// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a +// 128-bit unsigned integer. +inline uint128_fallback umul192_lower128(uint64_t x, + uint128_fallback y) noexcept { + uint64_t high = x * y.high(); + uint128_fallback high_low = umul128(x, y.low()); + return {high + high_low.high(), high_low.low()}; +} + +// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a +// 64-bit unsigned integer. +inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept { + return x * y; +} + +// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from +// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1. +inline int floor_log10_pow2(int e) noexcept { + FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent"); + static_assert((-1 >> 1) == -1, "right shift is not arithmetic"); + return (e * 315653) >> 20; +} + +// Various fast log computations. +inline int floor_log2_pow10(int e) noexcept { + FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); + return (e * 1741647) >> 19; +} +inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept { + FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent"); + return (e * 631305 - 261663) >> 21; +} + +static constexpr struct { + uint32_t divisor; + int shift_amount; +} div_small_pow10_infos[] = {{10, 16}, {100, 16}}; + +// Replaces n by floor(n / pow(10, N)) returning true if and only if n is +// divisible by pow(10, N). +// Precondition: n <= pow(10, N + 1). +template <int N> +bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept { + // The numbers below are chosen such that: + // 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100, + // 2. nm mod 2^k < m if and only if n is divisible by d, + // where m is magic_number, k is shift_amount + // and d is divisor. + // + // Item 1 is a common technique of replacing division by a constant with + // multiplication, see e.g. "Division by Invariant Integers Using + // Multiplication" by Granlund and Montgomery (1994). magic_number (m) is set + // to ceil(2^k/d) for large enough k. + // The idea for item 2 originates from Schubfach. + constexpr auto info = div_small_pow10_infos[N - 1]; + FMT_ASSERT(n <= info.divisor * 10, "n is too large"); + constexpr uint32_t magic_number = + (1u << info.shift_amount) / info.divisor + 1; + n *= magic_number; + const uint32_t comparison_mask = (1u << info.shift_amount) - 1; + bool result = (n & comparison_mask) < magic_number; + n >>= info.shift_amount; + return result; +} + +// Computes floor(n / pow(10, N)) for small n and N. +// Precondition: n <= pow(10, N + 1). +template <int N> uint32_t small_division_by_pow10(uint32_t n) noexcept { + constexpr auto info = div_small_pow10_infos[N - 1]; + FMT_ASSERT(n <= info.divisor * 10, "n is too large"); + constexpr uint32_t magic_number = + (1u << info.shift_amount) / info.divisor + 1; + return (n * magic_number) >> info.shift_amount; +} + +// Computes floor(n / 10^(kappa + 1)) (float) +inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept { + // 1374389535 = ceil(2^37/100) + return static_cast<uint32_t>((static_cast<uint64_t>(n) * 1374389535) >> 37); +} +// Computes floor(n / 10^(kappa + 1)) (double) +inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept { + // 2361183241434822607 = ceil(2^(64+7)/1000) + return umul128_upper64(n, 2361183241434822607ull) >> 7; +} + +// Various subroutines using pow10 cache +template <class T> struct cache_accessor; + +template <> struct cache_accessor<float> { + using carrier_uint = float_info<float>::carrier_uint; + using cache_entry_type = uint64_t; + + static uint64_t get_cached_power(int k) noexcept { + FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k, + "k is out of range"); + static constexpr const uint64_t pow10_significands[] = { + 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, + 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, + 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, + 0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb, + 0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a, + 0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810, + 0xe12e13424bb40e14, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff, + 0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd, + 0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424, + 0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b, + 0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000, + 0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000, + 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, + 0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000, + 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000, + 0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, + 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, + 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, + 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0, + 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940985, + 0xa18f07d736b90be6, 0xc9f2c9cd04674edf, 0xfc6f7c4045812297, + 0x9dc5ada82b70b59e, 0xc5371912364ce306, 0xf684df56c3e01bc7, + 0x9a130b963a6c115d, 0xc097ce7bc90715b4, 0xf0bdc21abb48db21, + 0x96769950b50d88f5, 0xbc143fa4e250eb32, 0xeb194f8e1ae525fe, + 0x92efd1b8d0cf37bf, 0xb7abc627050305ae, 0xe596b7b0c643c71a, + 0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f}; + return pow10_significands[k - float_info<float>::min_k]; + } + + struct compute_mul_result { + carrier_uint result; + bool is_integer; + }; + struct compute_mul_parity_result { + bool parity; + bool is_integer; + }; + + static compute_mul_result compute_mul( + carrier_uint u, const cache_entry_type& cache) noexcept { + auto r = umul96_upper64(u, cache); + return {static_cast<carrier_uint>(r >> 32), + static_cast<carrier_uint>(r) == 0}; + } + + static uint32_t compute_delta(const cache_entry_type& cache, + int beta) noexcept { + return static_cast<uint32_t>(cache >> (64 - 1 - beta)); + } + + static compute_mul_parity_result compute_mul_parity( + carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { + FMT_ASSERT(beta >= 1, ""); + FMT_ASSERT(beta < 64, ""); + + auto r = umul96_lower64(two_f, cache); + return {((r >> (64 - beta)) & 1) != 0, + static_cast<uint32_t>(r >> (32 - beta)) == 0}; + } + + static carrier_uint compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept { + return static_cast<carrier_uint>( + (cache - (cache >> (num_significand_bits<float>() + 2))) >> + (64 - num_significand_bits<float>() - 1 - beta)); + } + + static carrier_uint compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept { + return static_cast<carrier_uint>( + (cache + (cache >> (num_significand_bits<float>() + 1))) >> + (64 - num_significand_bits<float>() - 1 - beta)); + } + + static carrier_uint compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept { + return (static_cast<carrier_uint>( + cache >> (64 - num_significand_bits<float>() - 2 - beta)) + + 1) / + 2; + } +}; + +template <> struct cache_accessor<double> { + using carrier_uint = float_info<double>::carrier_uint; + using cache_entry_type = uint128_fallback; + + static uint128_fallback get_cached_power(int k) noexcept { + FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k, + "k is out of range"); + + static constexpr const uint128_fallback pow10_significands[] = { +#if FMT_USE_FULL_CACHE_DRAGONBOX + {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, + {0x9faacf3df73609b1, 0x77b191618c54e9ad}, + {0xc795830d75038c1d, 0xd59df5b9ef6a2418}, + {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e}, + {0x9becce62836ac577, 0x4ee367f9430aec33}, + {0xc2e801fb244576d5, 0x229c41f793cda740}, + {0xf3a20279ed56d48a, 0x6b43527578c11110}, + {0x9845418c345644d6, 0x830a13896b78aaaa}, + {0xbe5691ef416bd60c, 0x23cc986bc656d554}, + {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9}, + {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa}, + {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54}, + {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69}, + {0x91376c36d99995be, 0x23100809b9c21fa2}, + {0xb58547448ffffb2d, 0xabd40a0c2832a78b}, + {0xe2e69915b3fff9f9, 0x16c90c8f323f516d}, + {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4}, + {0xb1442798f49ffb4a, 0x99cd11cfdf41779d}, + {0xdd95317f31c7fa1d, 0x40405643d711d584}, + {0x8a7d3eef7f1cfc52, 0x482835ea666b2573}, + {0xad1c8eab5ee43b66, 0xda3243650005eed0}, + {0xd863b256369d4a40, 0x90bed43e40076a83}, + {0x873e4f75e2224e68, 0x5a7744a6e804a292}, + {0xa90de3535aaae202, 0x711515d0a205cb37}, + {0xd3515c2831559a83, 0x0d5a5b44ca873e04}, + {0x8412d9991ed58091, 0xe858790afe9486c3}, + {0xa5178fff668ae0b6, 0x626e974dbe39a873}, + {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, + {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a}, + {0xa139029f6a239f72, 0x1c1fffc1ebc44e81}, + {0xc987434744ac874e, 0xa327ffb266b56221}, + {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9}, + {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa}, + {0xc4ce17b399107c22, 0xcb550fb4384d21d4}, + {0xf6019da07f549b2b, 0x7e2a53a146606a49}, + {0x99c102844f94e0fb, 0x2eda7444cbfc426e}, + {0xc0314325637a1939, 0xfa911155fefb5309}, + {0xf03d93eebc589f88, 0x793555ab7eba27cb}, + {0x96267c7535b763b5, 0x4bc1558b2f3458df}, + {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17}, + {0xea9c227723ee8bcb, 0x465e15a979c1cadd}, + {0x92a1958a7675175f, 0x0bfacd89ec191eca}, + {0xb749faed14125d36, 0xcef980ec671f667c}, + {0xe51c79a85916f484, 0x82b7e12780e7401b}, + {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811}, + {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16}, + {0xdfbdcece67006ac9, 0x67a791e093e1d49b}, + {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1}, + {0xaecc49914078536d, 0x58fae9f773886e19}, + {0xda7f5bf590966848, 0xaf39a475506a899f}, + {0x888f99797a5e012d, 0x6d8406c952429604}, + {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84}, + {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65}, + {0x855c3be0a17fcd26, 0x5cf2eea09a550680}, + {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, + {0xd0601d8efc57b08b, 0xf13b94daf124da27}, + {0x823c12795db6ce57, 0x76c53d08d6b70859}, + {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f}, + {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a}, + {0xfe5d54150b090b02, 0xd3f93b35435d7c4d}, + {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0}, + {0xc6b8e9b0709f109a, 0x359ab6419ca1091c}, + {0xf867241c8cc6d4c0, 0xc30163d203c94b63}, + {0x9b407691d7fc44f8, 0x79e0de63425dcf1e}, + {0xc21094364dfb5636, 0x985915fc12f542e5}, + {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e}, + {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43}, + {0xbd8430bd08277231, 0x50c6ff782a838354}, + {0xece53cec4a314ebd, 0xa4f8bf5635246429}, + {0x940f4613ae5ed136, 0x871b7795e136be9a}, + {0xb913179899f68584, 0x28e2557b59846e40}, + {0xe757dd7ec07426e5, 0x331aeada2fe589d0}, + {0x9096ea6f3848984f, 0x3ff0d2c85def7622}, + {0xb4bca50b065abe63, 0x0fed077a756b53aa}, + {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895}, + {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d}, + {0xb080392cc4349dec, 0xbd8d794d96aacfb4}, + {0xdca04777f541c567, 0xecf0d7a0fc5583a1}, + {0x89e42caaf9491b60, 0xf41686c49db57245}, + {0xac5d37d5b79b6239, 0x311c2875c522ced6}, + {0xd77485cb25823ac7, 0x7d633293366b828c}, + {0x86a8d39ef77164bc, 0xae5dff9c02033198}, + {0xa8530886b54dbdeb, 0xd9f57f830283fdfd}, + {0xd267caa862a12d66, 0xd072df63c324fd7c}, + {0x8380dea93da4bc60, 0x4247cb9e59f71e6e}, + {0xa46116538d0deb78, 0x52d9be85f074e609}, + {0xcd795be870516656, 0x67902e276c921f8c}, + {0x806bd9714632dff6, 0x00ba1cd8a3db53b7}, + {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5}, + {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce}, + {0xfad2a4b13d1b5d6c, 0x796b805720085f82}, + {0x9cc3a6eec6311a63, 0xcbe3303674053bb1}, + {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d}, + {0xf4f1b4d515acb93b, 0xee92fb5515482d45}, + {0x991711052d8bf3c5, 0x751bdd152d4d1c4b}, + {0xbf5cd54678eef0b6, 0xd262d45a78a0635e}, + {0xef340a98172aace4, 0x86fb897116c87c35}, + {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1}, + {0xbae0a846d2195712, 0x8974836059cca10a}, + {0xe998d258869facd7, 0x2bd1a438703fc94c}, + {0x91ff83775423cc06, 0x7b6306a34627ddd0}, + {0xb67f6455292cbf08, 0x1a3bc84c17b1d543}, + {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94}, + {0x8e938662882af53e, 0x547eb47b7282ee9d}, + {0xb23867fb2a35b28d, 0xe99e619a4f23aa44}, + {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5}, + {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05}, + {0xae0b158b4738705e, 0x9624ab50b148d446}, + {0xd98ddaee19068c76, 0x3badd624dd9b0958}, + {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7}, + {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d}, + {0xd47487cc8470652b, 0x7647c32000696720}, + {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074}, + {0xa5fb0a17c777cf09, 0xf468107100525891}, + {0xcf79cc9db955c2cc, 0x7182148d4066eeb5}, + {0x81ac1fe293d599bf, 0xc6f14cd848405531}, + {0xa21727db38cb002f, 0xb8ada00e5a506a7d}, + {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d}, + {0xfd442e4688bd304a, 0x908f4a166d1da664}, + {0x9e4a9cec15763e2e, 0x9a598e4e043287ff}, + {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe}, + {0xf7549530e188c128, 0xd12bee59e68ef47d}, + {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf}, + {0xc13a148e3032d6e7, 0xe36a52363c1faf02}, + {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2}, + {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba}, + {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8}, + {0xebdf661791d60f56, 0x111b495b3464ad22}, + {0x936b9fcebb25c995, 0xcab10dd900beec35}, + {0xb84687c269ef3bfb, 0x3d5d514f40eea743}, + {0xe65829b3046b0afa, 0x0cb4a5a3112a5113}, + {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac}, + {0xb3f4e093db73a093, 0x59ed216765690f57}, + {0xe0f218b8d25088b8, 0x306869c13ec3532d}, + {0x8c974f7383725573, 0x1e414218c73a13fc}, + {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, + {0xdbac6c247d62a583, 0xdf45f746b74abf3a}, + {0x894bc396ce5da772, 0x6b8bba8c328eb784}, + {0xab9eb47c81f5114f, 0x066ea92f3f326565}, + {0xd686619ba27255a2, 0xc80a537b0efefebe}, + {0x8613fd0145877585, 0xbd06742ce95f5f37}, + {0xa798fc4196e952e7, 0x2c48113823b73705}, + {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6}, + {0x82ef85133de648c4, 0x9a984d73dbe722fc}, + {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb}, + {0xcc963fee10b7d1b3, 0x318df905079926a9}, + {0xffbbcfe994e5c61f, 0xfdf17746497f7053}, + {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634}, + {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1}, + {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1}, + {0x9c1661a651213e2d, 0x06bea10ca65c084f}, + {0xc31bfa0fe5698db8, 0x486e494fcff30a63}, + {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb}, + {0x986ddb5c6b3a76b7, 0xf89629465a75e01d}, + {0xbe89523386091465, 0xf6bbb397f1135824}, + {0xee2ba6c0678b597f, 0x746aa07ded582e2d}, + {0x94db483840b717ef, 0xa8c2a44eb4571cdd}, + {0xba121a4650e4ddeb, 0x92f34d62616ce414}, + {0xe896a0d7e51e1566, 0x77b020baf9c81d18}, + {0x915e2486ef32cd60, 0x0ace1474dc1d122f}, + {0xb5b5ada8aaff80b8, 0x0d819992132456bb}, + {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a}, + {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, + {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3}, + {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf}, + {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c}, + {0xad4ab7112eb3929d, 0x86c16c98d2c953c7}, + {0xd89d64d57a607744, 0xe871c7bf077ba8b8}, + {0x87625f056c7c4a8b, 0x11471cd764ad4973}, + {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0}, + {0xd389b47879823479, 0x4aff1d108d4ec2c4}, + {0x843610cb4bf160cb, 0xcedf722a585139bb}, + {0xa54394fe1eedb8fe, 0xc2974eb4ee658829}, + {0xce947a3da6a9273e, 0x733d226229feea33}, + {0x811ccc668829b887, 0x0806357d5a3f5260}, + {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8}, + {0xc9bcff6034c13052, 0xfc89b393dd02f0b6}, + {0xfc2c3f3841f17c67, 0xbbac2078d443ace3}, + {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e}, + {0xc5029163f384a931, 0x0a9e795e65d4df12}, + {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6}, + {0x99ea0196163fa42e, 0x504bced1bf8e4e46}, + {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7}, + {0xf07da27a82c37088, 0x5d767327bb4e5a4d}, + {0x964e858c91ba2655, 0x3a6a07f8d510f870}, + {0xbbe226efb628afea, 0x890489f70a55368c}, + {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f}, + {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e}, + {0xb77ada0617e3bbcb, 0x09ce6ebb40173745}, + {0xe55990879ddcaabd, 0xcc420a6a101d0516}, + {0x8f57fa54c2a9eab6, 0x9fa946824a12232e}, + {0xb32df8e9f3546564, 0x47939822dc96abfa}, + {0xdff9772470297ebd, 0x59787e2b93bc56f8}, + {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b}, + {0xaefae51477a06b03, 0xede622920b6b23f2}, + {0xdab99e59958885c4, 0xe95fab368e45ecee}, + {0x88b402f7fd75539b, 0x11dbcb0218ebb415}, + {0xaae103b5fcd2a881, 0xd652bdc29f26a11a}, + {0xd59944a37c0752a2, 0x4be76d3346f04960}, + {0x857fcae62d8493a5, 0x6f70a4400c562ddc}, + {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953}, + {0xd097ad07a71f26b2, 0x7e2000a41346a7a8}, + {0x825ecc24c873782f, 0x8ed400668c0c28c9}, + {0xa2f67f2dfa90563b, 0x728900802f0f32fb}, + {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba}, + {0xfea126b7d78186bc, 0xe2f610c84987bfa9}, + {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca}, + {0xc6ede63fa05d3143, 0x91503d1c79720dbc}, + {0xf8a95fcf88747d94, 0x75a44c6397ce912b}, + {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb}, + {0xc24452da229b021b, 0xfbe85badce996169}, + {0xf2d56790ab41c2a2, 0xfae27299423fb9c4}, + {0x97c560ba6b0919a5, 0xdccd879fc967d41b}, + {0xbdb6b8e905cb600f, 0x5400e987bbc1c921}, + {0xed246723473e3813, 0x290123e9aab23b69}, + {0x9436c0760c86e30b, 0xf9a0b6720aaf6522}, + {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, + {0xe7958cb87392c2c2, 0xb60b1d1230b20e05}, + {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3}, + {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4}, + {0xe2280b6c20dd5232, 0x25c6da63c38de1b1}, + {0x8d590723948a535f, 0x579c487e5a38ad0f}, + {0xb0af48ec79ace837, 0x2d835a9df0c6d852}, + {0xdcdb1b2798182244, 0xf8e431456cf88e66}, + {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900}, + {0xac8b2d36eed2dac5, 0xe272467e3d222f40}, + {0xd7adf884aa879177, 0x5b0ed81dcc6abb10}, + {0x86ccbb52ea94baea, 0x98e947129fc2b4ea}, + {0xa87fea27a539e9a5, 0x3f2398d747b36225}, + {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae}, + {0x83a3eeeef9153e89, 0x1953cf68300424ad}, + {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8}, + {0xcdb02555653131b6, 0x3792f412cb06794e}, + {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1}, + {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5}, + {0xc8de047564d20a8b, 0xf245825a5a445276}, + {0xfb158592be068d2e, 0xeed6e2f0f0d56713}, + {0x9ced737bb6c4183d, 0x55464dd69685606c}, + {0xc428d05aa4751e4c, 0xaa97e14c3c26b887}, + {0xf53304714d9265df, 0xd53dd99f4b3066a9}, + {0x993fe2c6d07b7fab, 0xe546a8038efe402a}, + {0xbf8fdb78849a5f96, 0xde98520472bdd034}, + {0xef73d256a5c0f77c, 0x963e66858f6d4441}, + {0x95a8637627989aad, 0xdde7001379a44aa9}, + {0xbb127c53b17ec159, 0x5560c018580d5d53}, + {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7}, + {0x9226712162ab070d, 0xcab3961304ca70e9}, + {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23}, + {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b}, + {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243}, + {0xb267ed1940f1c61c, 0x55f038b237591ed4}, + {0xdf01e85f912e37a3, 0x6b6c46dec52f6689}, + {0x8b61313bbabce2c6, 0x2323ac4b3b3da016}, + {0xae397d8aa96c1b77, 0xabec975e0a0d081b}, + {0xd9c7dced53c72255, 0x96e7bd358c904a22}, + {0x881cea14545c7575, 0x7e50d64177da2e55}, + {0xaa242499697392d2, 0xdde50bd1d5d0b9ea}, + {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865}, + {0x84ec3c97da624ab4, 0xbd5af13bef0b113f}, + {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f}, + {0xcfb11ead453994ba, 0x67de18eda5814af3}, + {0x81ceb32c4b43fcf4, 0x80eacf948770ced8}, + {0xa2425ff75e14fc31, 0xa1258379a94d028e}, + {0xcad2f7f5359a3b3e, 0x096ee45813a04331}, + {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd}, + {0x9e74d1b791e07e48, 0x775ea264cf55347e}, + {0xc612062576589dda, 0x95364afe032a819e}, + {0xf79687aed3eec551, 0x3a83ddbd83f52205}, + {0x9abe14cd44753b52, 0xc4926a9672793543}, + {0xc16d9a0095928a27, 0x75b7053c0f178294}, + {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, + {0x971da05074da7bee, 0xd3f6fc16ebca5e04}, + {0xbce5086492111aea, 0x88f4bb1ca6bcf585}, + {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6}, + {0x9392ee8e921d5d07, 0x3aff322e62439fd0}, + {0xb877aa3236a4b449, 0x09befeb9fad487c3}, + {0xe69594bec44de15b, 0x4c2ebe687989a9b4}, + {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11}, + {0xb424dc35095cd80f, 0x538484c19ef38c95}, + {0xe12e13424bb40e13, 0x2865a5f206b06fba}, + {0x8cbccc096f5088cb, 0xf93f87b7442e45d4}, + {0xafebff0bcb24aafe, 0xf78f69a51539d749}, + {0xdbe6fecebdedd5be, 0xb573440e5a884d1c}, + {0x89705f4136b4a597, 0x31680a88f8953031}, + {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e}, + {0xd6bf94d5e57a42bc, 0x3d32907604691b4d}, + {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110}, + {0xa7c5ac471b478423, 0x0fcf80dc33721d54}, + {0xd1b71758e219652b, 0xd3c36113404ea4a9}, + {0x83126e978d4fdf3b, 0x645a1cac083126ea}, + {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4}, + {0xcccccccccccccccc, 0xcccccccccccccccd}, + {0x8000000000000000, 0x0000000000000000}, + {0xa000000000000000, 0x0000000000000000}, + {0xc800000000000000, 0x0000000000000000}, + {0xfa00000000000000, 0x0000000000000000}, + {0x9c40000000000000, 0x0000000000000000}, + {0xc350000000000000, 0x0000000000000000}, + {0xf424000000000000, 0x0000000000000000}, + {0x9896800000000000, 0x0000000000000000}, + {0xbebc200000000000, 0x0000000000000000}, + {0xee6b280000000000, 0x0000000000000000}, + {0x9502f90000000000, 0x0000000000000000}, + {0xba43b74000000000, 0x0000000000000000}, + {0xe8d4a51000000000, 0x0000000000000000}, + {0x9184e72a00000000, 0x0000000000000000}, + {0xb5e620f480000000, 0x0000000000000000}, + {0xe35fa931a0000000, 0x0000000000000000}, + {0x8e1bc9bf04000000, 0x0000000000000000}, + {0xb1a2bc2ec5000000, 0x0000000000000000}, + {0xde0b6b3a76400000, 0x0000000000000000}, + {0x8ac7230489e80000, 0x0000000000000000}, + {0xad78ebc5ac620000, 0x0000000000000000}, + {0xd8d726b7177a8000, 0x0000000000000000}, + {0x878678326eac9000, 0x0000000000000000}, + {0xa968163f0a57b400, 0x0000000000000000}, + {0xd3c21bcecceda100, 0x0000000000000000}, + {0x84595161401484a0, 0x0000000000000000}, + {0xa56fa5b99019a5c8, 0x0000000000000000}, + {0xcecb8f27f4200f3a, 0x0000000000000000}, + {0x813f3978f8940984, 0x4000000000000000}, + {0xa18f07d736b90be5, 0x5000000000000000}, + {0xc9f2c9cd04674ede, 0xa400000000000000}, + {0xfc6f7c4045812296, 0x4d00000000000000}, + {0x9dc5ada82b70b59d, 0xf020000000000000}, + {0xc5371912364ce305, 0x6c28000000000000}, + {0xf684df56c3e01bc6, 0xc732000000000000}, + {0x9a130b963a6c115c, 0x3c7f400000000000}, + {0xc097ce7bc90715b3, 0x4b9f100000000000}, + {0xf0bdc21abb48db20, 0x1e86d40000000000}, + {0x96769950b50d88f4, 0x1314448000000000}, + {0xbc143fa4e250eb31, 0x17d955a000000000}, + {0xeb194f8e1ae525fd, 0x5dcfab0800000000}, + {0x92efd1b8d0cf37be, 0x5aa1cae500000000}, + {0xb7abc627050305ad, 0xf14a3d9e40000000}, + {0xe596b7b0c643c719, 0x6d9ccd05d0000000}, + {0x8f7e32ce7bea5c6f, 0xe4820023a2000000}, + {0xb35dbf821ae4f38b, 0xdda2802c8a800000}, + {0xe0352f62a19e306e, 0xd50b2037ad200000}, + {0x8c213d9da502de45, 0x4526f422cc340000}, + {0xaf298d050e4395d6, 0x9670b12b7f410000}, + {0xdaf3f04651d47b4c, 0x3c0cdd765f114000}, + {0x88d8762bf324cd0f, 0xa5880a69fb6ac800}, + {0xab0e93b6efee0053, 0x8eea0d047a457a00}, + {0xd5d238a4abe98068, 0x72a4904598d6d880}, + {0x85a36366eb71f041, 0x47a6da2b7f864750}, + {0xa70c3c40a64e6c51, 0x999090b65f67d924}, + {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d}, + {0x82818f1281ed449f, 0xbff8f10e7a8921a5}, + {0xa321f2d7226895c7, 0xaff72d52192b6a0e}, + {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764491}, + {0xfee50b7025c36a08, 0x02f236d04753d5b5}, + {0x9f4f2726179a2245, 0x01d762422c946591}, + {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef6}, + {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb3}, + {0x9b934c3b330c8577, 0x63cc55f49f88eb30}, + {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fc}, + {0xf316271c7fc3908a, 0x8bef464e3945ef7b}, + {0x97edd871cfda3a56, 0x97758bf0e3cbb5ad}, + {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea318}, + {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bde}, + {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6b}, + {0xb975d6b6ee39e436, 0xb3e2fd538e122b45}, + {0xe7d34c64a9c85d44, 0x60dbbca87196b617}, + {0x90e40fbeea1d3a4a, 0xbc8955e946fe31ce}, + {0xb51d13aea4a488dd, 0x6babab6398bdbe42}, + {0xe264589a4dcdab14, 0xc696963c7eed2dd2}, + {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca3}, + {0xb0de65388cc8ada8, 0x3b25a55f43294bcc}, + {0xdd15fe86affad912, 0x49ef0eb713f39ebf}, + {0x8a2dbf142dfcc7ab, 0x6e3569326c784338}, + {0xacb92ed9397bf996, 0x49c2c37f07965405}, + {0xd7e77a8f87daf7fb, 0xdc33745ec97be907}, + {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a4}, + {0xa8acd7c0222311bc, 0xc40832ea0d68ce0d}, + {0xd2d80db02aabd62b, 0xf50a3fa490c30191}, + {0x83c7088e1aab65db, 0x792667c6da79e0fb}, + {0xa4b8cab1a1563f52, 0x577001b891185939}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87}, + {0x80b05e5ac60b6178, 0x544f8158315b05b5}, + {0xa0dc75f1778e39d6, 0x696361ae3db1c722}, + {0xc913936dd571c84c, 0x03bc3a19cd1e38ea}, + {0xfb5878494ace3a5f, 0x04ab48a04065c724}, + {0x9d174b2dcec0e47b, 0x62eb0d64283f9c77}, + {0xc45d1df942711d9a, 0x3ba5d0bd324f8395}, + {0xf5746577930d6500, 0xca8f44ec7ee3647a}, + {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecc}, + {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67f}, + {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101f}, + {0x95d04aee3b80ece5, 0xbba1f1d158724a13}, + {0xbb445da9ca61281f, 0x2a8a6e45ae8edc98}, + {0xea1575143cf97226, 0xf52d09d71a3293be}, + {0x924d692ca61be758, 0x593c2626705f9c57}, + {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836d}, + {0xe498f455c38b997a, 0x0b6dfb9c0f956448}, + {0x8edf98b59a373fec, 0x4724bd4189bd5ead}, + {0xb2977ee300c50fe7, 0x58edec91ec2cb658}, + {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ee}, + {0x8b865b215899f46c, 0xbd79e0d20082ee75}, + {0xae67f1e9aec07187, 0xecd8590680a3aa12}, + {0xda01ee641a708de9, 0xe80e6f4820cc9496}, + {0x884134fe908658b2, 0x3109058d147fdcde}, + {0xaa51823e34a7eede, 0xbd4b46f0599fd416}, + {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91b}, + {0x850fadc09923329e, 0x03e2cf6bc604ddb1}, + {0xa6539930bf6bff45, 0x84db8346b786151d}, + {0xcfe87f7cef46ff16, 0xe612641865679a64}, + {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07f}, + {0xa26da3999aef7749, 0xe3be5e330f38f09e}, + {0xcb090c8001ab551c, 0x5cadf5bfd3072cc6}, + {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f7}, + {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afb}, + {0xc646d63501a1511d, 0xb281e1fd541501b9}, + {0xf7d88bc24209a565, 0x1f225a7ca91a4227}, + {0x9ae757596946075f, 0x3375788de9b06959}, + {0xc1a12d2fc3978937, 0x0052d6b1641c83af}, + {0xf209787bb47d6b84, 0xc0678c5dbd23a49b}, + {0x9745eb4d50ce6332, 0xf840b7ba963646e1}, + {0xbd176620a501fbff, 0xb650e5a93bc3d899}, + {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebf}, + {0x93ba47c980e98cdf, 0xc66f336c36b10138}, + {0xb8a8d9bbe123f017, 0xb80b0047445d4185}, + {0xe6d3102ad96cec1d, 0xa60dc059157491e6}, + {0x9043ea1ac7e41392, 0x87c89837ad68db30}, + {0xb454e4a179dd1877, 0x29babe4598c311fc}, + {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67b}, + {0x8ce2529e2734bb1d, 0x1899e4a65f58660d}, + {0xb01ae745b101e9e4, 0x5ec05dcff72e7f90}, + {0xdc21a1171d42645d, 0x76707543f4fa1f74}, + {0x899504ae72497eba, 0x6a06494a791c53a9}, + {0xabfa45da0edbde69, 0x0487db9d17636893}, + {0xd6f8d7509292d603, 0x45a9d2845d3c42b7}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3}, + {0xa7f26836f282b732, 0x8e6cac7768d7141f}, + {0xd1ef0244af2364ff, 0x3207d795430cd927}, + {0x8335616aed761f1f, 0x7f44e6bd49e807b9}, + {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a7}, + {0xcd036837130890a1, 0x36dba887c37a8c10}, + {0x802221226be55a64, 0xc2494954da2c978a}, + {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6d}, + {0xc83553c5c8965d3d, 0x6f92829494e5acc8}, + {0xfa42a8b73abbf48c, 0xcb772339ba1f17fa}, + {0x9c69a97284b578d7, 0xff2a760414536efc}, + {0xc38413cf25e2d70d, 0xfef5138519684abb}, + {0xf46518c2ef5b8cd1, 0x7eb258665fc25d6a}, + {0x98bf2f79d5993802, 0xef2f773ffbd97a62}, + {0xbeeefb584aff8603, 0xaafb550ffacfd8fb}, + {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf39}, + {0x952ab45cfa97a0b2, 0xdd945a747bf26184}, + {0xba756174393d88df, 0x94f971119aeef9e5}, + {0xe912b9d1478ceb17, 0x7a37cd5601aab85e}, + {0x91abb422ccb812ee, 0xac62e055c10ab33b}, + {0xb616a12b7fe617aa, 0x577b986b314d600a}, + {0xe39c49765fdf9d94, 0xed5a7e85fda0b80c}, + {0x8e41ade9fbebc27d, 0x14588f13be847308}, + {0xb1d219647ae6b31c, 0x596eb2d8ae258fc9}, + {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bc}, + {0x8aec23d680043bee, 0x25de7bb9480d5855}, + {0xada72ccc20054ae9, 0xaf561aa79a10ae6b}, + {0xd910f7ff28069da4, 0x1b2ba1518094da05}, + {0x87aa9aff79042286, 0x90fb44d2f05d0843}, + {0xa99541bf57452b28, 0x353a1607ac744a54}, + {0xd3fa922f2d1675f2, 0x42889b8997915ce9}, + {0x847c9b5d7c2e09b7, 0x69956135febada12}, + {0xa59bc234db398c25, 0x43fab9837e699096}, + {0xcf02b2c21207ef2e, 0x94f967e45e03f4bc}, + {0x8161afb94b44f57d, 0x1d1be0eebac278f6}, + {0xa1ba1ba79e1632dc, 0x6462d92a69731733}, + {0xca28a291859bbf93, 0x7d7b8f7503cfdcff}, + {0xfcb2cb35e702af78, 0x5cda735244c3d43f}, + {0x9defbf01b061adab, 0x3a0888136afa64a8}, + {0xc56baec21c7a1916, 0x088aaa1845b8fdd1}, + {0xf6c69a72a3989f5b, 0x8aad549e57273d46}, + {0x9a3c2087a63f6399, 0x36ac54e2f678864c}, + {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7de}, + {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d6}, + {0x969eb7c47859e743, 0x9f644ae5a4b1b326}, + {0xbc4665b596706114, 0x873d5d9f0dde1fef}, + {0xeb57ff22fc0c7959, 0xa90cb506d155a7eb}, + {0x9316ff75dd87cbd8, 0x09a7f12442d588f3}, + {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb30}, + {0xe5d3ef282a242e81, 0x8f1668c8a86da5fb}, + {0x8fa475791a569d10, 0xf96e017d694487bd}, + {0xb38d92d760ec4455, 0x37c981dcc395a9ad}, + {0xe070f78d3927556a, 0x85bbe253f47b1418}, + {0x8c469ab843b89562, 0x93956d7478ccec8f}, + {0xaf58416654a6babb, 0x387ac8d1970027b3}, + {0xdb2e51bfe9d0696a, 0x06997b05fcc0319f}, + {0x88fcf317f22241e2, 0x441fece3bdf81f04}, + {0xab3c2fddeeaad25a, 0xd527e81cad7626c4}, + {0xd60b3bd56a5586f1, 0x8a71e223d8d3b075}, + {0x85c7056562757456, 0xf6872d5667844e4a}, + {0xa738c6bebb12d16c, 0xb428f8ac016561dc}, + {0xd106f86e69d785c7, 0xe13336d701beba53}, + {0x82a45b450226b39c, 0xecc0024661173474}, + {0xa34d721642b06084, 0x27f002d7f95d0191}, + {0xcc20ce9bd35c78a5, 0x31ec038df7b441f5}, + {0xff290242c83396ce, 0x7e67047175a15272}, + {0x9f79a169bd203e41, 0x0f0062c6e984d387}, + {0xc75809c42c684dd1, 0x52c07b78a3e60869}, + {0xf92e0c3537826145, 0xa7709a56ccdf8a83}, + {0x9bbcc7a142b17ccb, 0x88a66076400bb692}, + {0xc2abf989935ddbfe, 0x6acff893d00ea436}, + {0xf356f7ebf83552fe, 0x0583f6b8c4124d44}, + {0x98165af37b2153de, 0xc3727a337a8b704b}, + {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5d}, + {0xeda2ee1c7064130c, 0x1162def06f79df74}, + {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba9}, + {0xb9a74a0637ce2ee1, 0x6d953e2bd7173693}, + {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0438}, + {0x910ab1d4db9914a0, 0x1d9c9892400a22a3}, + {0xb54d5e4a127f59c8, 0x2503beb6d00cab4c}, + {0xe2a0b5dc971f303a, 0x2e44ae64840fd61e}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d3}, + {0xb10d8e1456105dad, 0x7425a83e872c5f48}, + {0xdd50f1996b947518, 0xd12f124e28f7771a}, + {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa70}, + {0xace73cbfdc0bfb7b, 0x636cc64d1001550c}, + {0xd8210befd30efa5a, 0x3c47f7e05401aa4f}, + {0x8714a775e3e95c78, 0x65acfaec34810a72}, + {0xa8d9d1535ce3b396, 0x7f1839a741a14d0e}, + {0xd31045a8341ca07c, 0x1ede48111209a051}, + {0x83ea2b892091e44d, 0x934aed0aab460433}, + {0xa4e4b66b68b65d60, 0xf81da84d56178540}, + {0xce1de40642e3f4b9, 0x36251260ab9d668f}, + {0x80d2ae83e9ce78f3, 0xc1d72b7c6b42601a}, + {0xa1075a24e4421730, 0xb24cf65b8612f820}, + {0xc94930ae1d529cfc, 0xdee033f26797b628}, + {0xfb9b7cd9a4a7443c, 0x169840ef017da3b2}, + {0x9d412e0806e88aa5, 0x8e1f289560ee864f}, + {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e3}, + {0xf5b5d7ec8acb58a2, 0xae10af696774b1dc}, + {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef2a}, + {0xbff610b0cc6edd3f, 0x17fd090a58d32af4}, + {0xeff394dcff8a948e, 0xddfc4b4cef07f5b1}, + {0x95f83d0a1fb69cd9, 0x4abdaf101564f98f}, + {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f2}, + {0xea53df5fd18d5513, 0x84c86189216dc5ee}, + {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb5}, + {0xb7118682dbb66a77, 0x3fbc8c33221dc2a2}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, + {0x8f05b1163ba6832d, 0x29cb4d87f2a7400f}, + {0xb2c71d5bca9023f8, 0x743e20e9ef511013}, + {0xdf78e4b2bd342cf6, 0x914da9246b255417}, + {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548f}, + {0xae9672aba3d0c320, 0xa184ac2473b529b2}, + {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741f}, + {0x8865899617fb1871, 0x7e2fa67c7a658893}, + {0xaa7eebfb9df9de8d, 0xddbb901b98feeab8}, + {0xd51ea6fa85785631, 0x552a74227f3ea566}, + {0x8533285c936b35de, 0xd53a88958f872760}, + {0xa67ff273b8460356, 0x8a892abaf368f138}, + {0xd01fef10a657842c, 0x2d2b7569b0432d86}, + {0x8213f56a67f6b29b, 0x9c3b29620e29fc74}, + {0xa298f2c501f45f42, 0x8349f3ba91b47b90}, + {0xcb3f2f7642717713, 0x241c70a936219a74}, + {0xfe0efb53d30dd4d7, 0xed238cd383aa0111}, + {0x9ec95d1463e8a506, 0xf4363804324a40ab}, + {0xc67bb4597ce2ce48, 0xb143c6053edcd0d6}, + {0xf81aa16fdc1b81da, 0xdd94b7868e94050b}, + {0x9b10a4e5e9913128, 0xca7cf2b4191c8327}, + {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f1}, + {0xf24a01a73cf2dccf, 0xbc633b39673c8ced}, + {0x976e41088617ca01, 0xd5be0503e085d814}, + {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e19}, + {0xec9c459d51852ba2, 0xddf8e7d60ed1219f}, + {0x93e1ab8252f33b45, 0xcabb90e5c942b504}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936244}, + {0xe7109bfba19c0c9d, 0x0cc512670a783ad5}, + {0x906a617d450187e2, 0x27fb2b80668b24c6}, + {0xb484f9dc9641e9da, 0xb1f9f660802dedf7}, + {0xe1a63853bbd26451, 0x5e7873f8a0396974}, + {0x8d07e33455637eb2, 0xdb0b487b6423e1e9}, + {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda63}, + {0xdc5c5301c56b75f7, 0x7641a140cc7810fc}, + {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9e}, + {0xac2820d9623bf429, 0x546345fa9fbdcd45}, + {0xd732290fbacaf133, 0xa97c177947ad4096}, + {0x867f59a9d4bed6c0, 0x49ed8eabcccc485e}, + {0xa81f301449ee8c70, 0x5c68f256bfff5a75}, + {0xd226fc195c6a2f8c, 0x73832eec6fff3112}, + {0x83585d8fd9c25db7, 0xc831fd53c5ff7eac}, + {0xa42e74f3d032f525, 0xba3e7ca8b77f5e56}, + {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35ec}, + {0x80444b5e7aa7cf85, 0x7980d163cf5b81b4}, + {0xa0555e361951c366, 0xd7e105bcc3326220}, + {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa8}, + {0xfa856334878fc150, 0xb14f98f6f0feb952}, + {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d4}, + {0xc3b8358109e84f07, 0x0a862f80ec4700c9}, + {0xf4a642e14c6262c8, 0xcd27bb612758c0fb}, + {0x98e7e9cccfbd7dbd, 0x8038d51cb897789d}, + {0xbf21e44003acdd2c, 0xe0470a63e6bd56c4}, + {0xeeea5d5004981478, 0x1858ccfce06cac75}, + {0x95527a5202df0ccb, 0x0f37801e0c43ebc9}, + {0xbaa718e68396cffd, 0xd30560258f54e6bb}, + {0xe950df20247c83fd, 0x47c6b82ef32a206a}, + {0x91d28b7416cdd27e, 0x4cdc331d57fa5442}, + {0xb6472e511c81471d, 0xe0133fe4adf8e953}, + {0xe3d8f9e563a198e5, 0x58180fddd97723a7}, + {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7649}, + {0xb201833b35d63f73, 0x2cd2cc6551e513db}, + {0xde81e40a034bcf4f, 0xf8077f7ea65e58d2}, + {0x8b112e86420f6191, 0xfb04afaf27faf783}, + {0xadd57a27d29339f6, 0x79c5db9af1f9b564}, + {0xd94ad8b1c7380874, 0x18375281ae7822bd}, + {0x87cec76f1c830548, 0x8f2293910d0b15b6}, + {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb23}, + {0xd433179d9c8cb841, 0x5fa60692a46151ec}, + {0x849feec281d7f328, 0xdbc7c41ba6bcd334}, + {0xa5c7ea73224deff3, 0x12b9b522906c0801}, + {0xcf39e50feae16bef, 0xd768226b34870a01}, + {0x81842f29f2cce375, 0xe6a1158300d46641}, + {0xa1e53af46f801c53, 0x60495ae3c1097fd1}, + {0xca5e89b18b602368, 0x385bb19cb14bdfc5}, + {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6}, + {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2}, + {0xc5a05277621be293, 0xc7098b7305241886}, + { 0xf70867153aa2db38, + 0xb8cbee4fc66d1ea8 } +#else + {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, + {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, + {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, + {0x86a8d39ef77164bc, 0xae5dff9c02033198}, + {0xd98ddaee19068c76, 0x3badd624dd9b0958}, + {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, + {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, + {0xe55990879ddcaabd, 0xcc420a6a101d0516}, + {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, + {0x95a8637627989aad, 0xdde7001379a44aa9}, + {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, + {0xc350000000000000, 0x0000000000000000}, + {0x9dc5ada82b70b59d, 0xf020000000000000}, + {0xfee50b7025c36a08, 0x02f236d04753d5b5}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87}, + {0xa6539930bf6bff45, 0x84db8346b786151d}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3}, + {0xd910f7ff28069da4, 0x1b2ba1518094da05}, + {0xaf58416654a6babb, 0x387ac8d1970027b3}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d3}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936244}, + { 0x95527a5202df0ccb, + 0x0f37801e0c43ebc9 } +#endif + }; + +#if FMT_USE_FULL_CACHE_DRAGONBOX + return pow10_significands[k - float_info<double>::min_k]; +#else + static constexpr const uint64_t powers_of_5_64[] = { + 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, + 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, + 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, + 0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd, + 0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9, + 0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5, + 0x000003782dace9d9, 0x00001158e460913d, 0x000056bc75e2d631, + 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, + 0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9}; + + static const int compression_ratio = 27; + + // Compute base index. + int cache_index = (k - float_info<double>::min_k) / compression_ratio; + int kb = cache_index * compression_ratio + float_info<double>::min_k; + int offset = k - kb; + + // Get base cache. + uint128_fallback base_cache = pow10_significands[cache_index]; + if (offset == 0) return base_cache; + + // Compute the required amount of bit-shift. + int alpha = floor_log2_pow10(kb + offset) - floor_log2_pow10(kb) - offset; + FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected"); + + // Try to recover the real cache. + uint64_t pow5 = powers_of_5_64[offset]; + uint128_fallback recovered_cache = umul128(base_cache.high(), pow5); + uint128_fallback middle_low = umul128(base_cache.low(), pow5); + + recovered_cache += middle_low.high(); + + uint64_t high_to_middle = recovered_cache.high() << (64 - alpha); + uint64_t middle_to_low = recovered_cache.low() << (64 - alpha); + + recovered_cache = + uint128_fallback{(recovered_cache.low() >> alpha) | high_to_middle, + ((middle_low.low() >> alpha) | middle_to_low)}; + FMT_ASSERT(recovered_cache.low() + 1 != 0, ""); + return {recovered_cache.high(), recovered_cache.low() + 1}; +#endif + } + + struct compute_mul_result { + carrier_uint result; + bool is_integer; + }; + struct compute_mul_parity_result { + bool parity; + bool is_integer; + }; + + static compute_mul_result compute_mul( + carrier_uint u, const cache_entry_type& cache) noexcept { + auto r = umul192_upper128(u, cache); + return {r.high(), r.low() == 0}; + } + + static uint32_t compute_delta(cache_entry_type const& cache, + int beta) noexcept { + return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta)); + } + + static compute_mul_parity_result compute_mul_parity( + carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { + FMT_ASSERT(beta >= 1, ""); + FMT_ASSERT(beta < 64, ""); + + auto r = umul192_lower128(two_f, cache); + return {((r.high() >> (64 - beta)) & 1) != 0, + ((r.high() << beta) | (r.low() >> (64 - beta))) == 0}; + } + + static carrier_uint compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept { + return (cache.high() - + (cache.high() >> (num_significand_bits<double>() + 2))) >> + (64 - num_significand_bits<double>() - 1 - beta); + } + + static carrier_uint compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept { + return (cache.high() + + (cache.high() >> (num_significand_bits<double>() + 1))) >> + (64 - num_significand_bits<double>() - 1 - beta); + } + + static carrier_uint compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept { + return ((cache.high() >> (64 - num_significand_bits<double>() - 2 - beta)) + + 1) / + 2; + } +}; + +// Various integer checks +template <class T> +bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept { + const int case_shorter_interval_left_endpoint_lower_threshold = 2; + const int case_shorter_interval_left_endpoint_upper_threshold = 3; + return exponent >= case_shorter_interval_left_endpoint_lower_threshold && + exponent <= case_shorter_interval_left_endpoint_upper_threshold; +} + +// Remove trailing zeros from n and return the number of zeros removed (float) +FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept { + FMT_ASSERT(n != 0, ""); + const uint32_t mod_inv_5 = 0xcccccccd; + const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5; + + int s = 0; + while (true) { + auto q = rotr(n * mod_inv_25, 2); + if (q > max_value<uint32_t>() / 100) break; + n = q; + s += 2; + } + auto q = rotr(n * mod_inv_5, 1); + if (q <= max_value<uint32_t>() / 10) { + n = q; + s |= 1; + } + + return s; +} + +// Removes trailing zeros and returns the number of zeros removed (double) +FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { + FMT_ASSERT(n != 0, ""); + + // This magic number is ceil(2^90 / 10^8). + constexpr uint64_t magic_number = 12379400392853802749ull; + auto nm = umul128(n, magic_number); + + // Is n is divisible by 10^8? + if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) { + // If yes, work with the quotient. + auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64)); + + const uint32_t mod_inv_5 = 0xcccccccd; + const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5; + + int s = 8; + while (true) { + auto q = rotr(n32 * mod_inv_25, 2); + if (q > max_value<uint32_t>() / 100) break; + n32 = q; + s += 2; + } + auto q = rotr(n32 * mod_inv_5, 1); + if (q <= max_value<uint32_t>() / 10) { + n32 = q; + s |= 1; + } + + n = n32; + return s; + } + + // If n is not divisible by 10^8, work with n itself. + const uint64_t mod_inv_5 = 0xcccccccccccccccd; + const uint64_t mod_inv_25 = mod_inv_5 * mod_inv_5; + + int s = 0; + while (true) { + auto q = rotr(n * mod_inv_25, 2); + if (q > max_value<uint64_t>() / 100) break; + n = q; + s += 2; + } + auto q = rotr(n * mod_inv_5, 1); + if (q <= max_value<uint64_t>() / 10) { + n = q; + s |= 1; + } + + return s; +} + +// The main algorithm for shorter interval case +template <class T> +FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept { + decimal_fp<T> ret_value; + // Compute k and beta + const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); + const int beta = exponent + floor_log2_pow10(-minus_k); + + // Compute xi and zi + using cache_entry_type = typename cache_accessor<T>::cache_entry_type; + const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k); + + auto xi = cache_accessor<T>::compute_left_endpoint_for_shorter_interval_case( + cache, beta); + auto zi = cache_accessor<T>::compute_right_endpoint_for_shorter_interval_case( + cache, beta); + + // If the left endpoint is not an integer, increase it + if (!is_left_endpoint_integer_shorter_interval<T>(exponent)) ++xi; + + // Try bigger divisor + ret_value.significand = zi / 10; + + // If succeed, remove trailing zeros if necessary and return + if (ret_value.significand * 10 >= xi) { + ret_value.exponent = minus_k + 1; + ret_value.exponent += remove_trailing_zeros(ret_value.significand); + return ret_value; + } + + // Otherwise, compute the round-up of y + ret_value.significand = + cache_accessor<T>::compute_round_up_for_shorter_interval_case(cache, + beta); + ret_value.exponent = minus_k; + + // When tie occurs, choose one of them according to the rule + if (exponent >= float_info<T>::shorter_interval_tie_lower_threshold && + exponent <= float_info<T>::shorter_interval_tie_upper_threshold) { + ret_value.significand = ret_value.significand % 2 == 0 + ? ret_value.significand + : ret_value.significand - 1; + } else if (ret_value.significand < xi) { + ++ret_value.significand; + } + return ret_value; +} + +template <typename T> decimal_fp<T> to_decimal(T x) noexcept { + // Step 1: integer promotion & Schubfach multiplier calculation. + + using carrier_uint = typename float_info<T>::carrier_uint; + using cache_entry_type = typename cache_accessor<T>::cache_entry_type; + auto br = bit_cast<carrier_uint>(x); + + // Extract significand bits and exponent bits. + const carrier_uint significand_mask = + (static_cast<carrier_uint>(1) << num_significand_bits<T>()) - 1; + carrier_uint significand = (br & significand_mask); + int exponent = + static_cast<int>((br & exponent_mask<T>()) >> num_significand_bits<T>()); + + if (exponent != 0) { // Check if normal. + exponent -= exponent_bias<T>() + num_significand_bits<T>(); + + // Shorter interval case; proceed like Schubfach. + // In fact, when exponent == 1 and significand == 0, the interval is + // regular. However, it can be shown that the end-results are anyway same. + if (significand == 0) return shorter_interval_case<T>(exponent); + + significand |= (static_cast<carrier_uint>(1) << num_significand_bits<T>()); + } else { + // Subnormal case; the interval is always regular. + if (significand == 0) return {0, 0}; + exponent = + std::numeric_limits<T>::min_exponent - num_significand_bits<T>() - 1; + } + + const bool include_left_endpoint = (significand % 2 == 0); + const bool include_right_endpoint = include_left_endpoint; + + // Compute k and beta. + const int minus_k = floor_log10_pow2(exponent) - float_info<T>::kappa; + const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k); + const int beta = exponent + floor_log2_pow10(-minus_k); + + // Compute zi and deltai. + // 10^kappa <= deltai < 10^(kappa + 1) + const uint32_t deltai = cache_accessor<T>::compute_delta(cache, beta); + const carrier_uint two_fc = significand << 1; + + // For the case of binary32, the result of integer check is not correct for + // 29711844 * 2^-82 + // = 6.1442653300000000008655037797566933477355632930994033813476... * 10^-18 + // and 29711844 * 2^-81 + // = 1.2288530660000000001731007559513386695471126586198806762695... * 10^-17, + // and they are the unique counterexamples. However, since 29711844 is even, + // this does not cause any problem for the endpoints calculations; it can only + // cause a problem when we need to perform integer check for the center. + // Fortunately, with these inputs, that branch is never executed, so we are + // fine. + const typename cache_accessor<T>::compute_mul_result z_mul = + cache_accessor<T>::compute_mul((two_fc | 1) << beta, cache); + + // Step 2: Try larger divisor; remove trailing zeros if necessary. + + // Using an upper bound on zi, we might be able to optimize the division + // better than the compiler; we are computing zi / big_divisor here. + decimal_fp<T> ret_value; + ret_value.significand = divide_by_10_to_kappa_plus_1(z_mul.result); + uint32_t r = static_cast<uint32_t>(z_mul.result - float_info<T>::big_divisor * + ret_value.significand); + + if (r < deltai) { + // Exclude the right endpoint if necessary. + if (r == 0 && (z_mul.is_integer & !include_right_endpoint)) { + --ret_value.significand; + r = float_info<T>::big_divisor; + goto small_divisor_case_label; + } + } else if (r > deltai) { + goto small_divisor_case_label; + } else { + // r == deltai; compare fractional parts. + const typename cache_accessor<T>::compute_mul_parity_result x_mul = + cache_accessor<T>::compute_mul_parity(two_fc - 1, cache, beta); + + if (!(x_mul.parity | (x_mul.is_integer & include_left_endpoint))) + goto small_divisor_case_label; + } + ret_value.exponent = minus_k + float_info<T>::kappa + 1; + + // We may need to remove trailing zeros. + ret_value.exponent += remove_trailing_zeros(ret_value.significand); + return ret_value; + + // Step 3: Find the significand with the smaller divisor. + +small_divisor_case_label: + ret_value.significand *= 10; + ret_value.exponent = minus_k + float_info<T>::kappa; + + uint32_t dist = r - (deltai / 2) + (float_info<T>::small_divisor / 2); + const bool approx_y_parity = + ((dist ^ (float_info<T>::small_divisor / 2)) & 1) != 0; + + // Is dist divisible by 10^kappa? + const bool divisible_by_small_divisor = + check_divisibility_and_divide_by_pow10<float_info<T>::kappa>(dist); + + // Add dist / 10^kappa to the significand. + ret_value.significand += dist; + + if (!divisible_by_small_divisor) return ret_value; + + // Check z^(f) >= epsilon^(f). + // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, + // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f). + // Since there are only 2 possibilities, we only need to care about the + // parity. Also, zi and r should have the same parity since the divisor + // is an even number. + const auto y_mul = cache_accessor<T>::compute_mul_parity(two_fc, cache, beta); + + // If z^(f) >= epsilon^(f), we might have a tie when z^(f) == epsilon^(f), + // or equivalently, when y is an integer. + if (y_mul.parity != approx_y_parity) + --ret_value.significand; + else if (y_mul.is_integer & (ret_value.significand % 2 != 0)) + --ret_value.significand; + return ret_value; +} +} // namespace dragonbox + +#ifdef _MSC_VER +FMT_FUNC auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...) + -> int { + auto args = va_list(); + va_start(args, fmt); + int result = vsnprintf_s(buf, size, _TRUNCATE, fmt, args); + va_end(args); + return result; +} +#endif +} // namespace detail + +template <> struct formatter<detail::bigint> { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) + -> format_parse_context::iterator { + return ctx.begin(); + } + + template <typename FormatContext> + auto format(const detail::bigint& n, FormatContext& ctx) const -> + typename FormatContext::iterator { + 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, FMT_STRING("{:x}"), value); + first = false; + continue; + } + out = format_to(out, FMT_STRING("{:08x}"), value); + } + if (n.exp_ > 0) + out = format_to(out, FMT_STRING("p{}"), + n.exp_ * detail::bigint::bigit_bits); + return out; + } +}; + +FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { + for_each_codepoint(s, [this](uint32_t cp, string_view) { + if (cp == invalid_code_point) 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 true; + }); + buffer_.push_back(0); +} + +FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code, + const char* message) noexcept { + FMT_TRY { + auto ec = std::error_code(error_code, std::generic_category()); + write(std::back_inserter(out), std::system_error(ec, message).what()); + return; + } + FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +FMT_FUNC void report_system_error(int error_code, + const char* message) noexcept { + report_error(format_system_error, error_code, message); +} + +FMT_FUNC std::string vformat(string_view fmt, format_args args) { + // Don't optimize the "{}" case to keep the binary size small and because it + // can be better optimized in fmt::format anyway. + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); + return to_string(buffer); +} + +namespace detail { +#ifdef _WIN32 +using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>; +extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // + void*, const void*, dword, dword*, void*); + +FMT_FUNC bool write_console(std::FILE* f, string_view text) { + auto fd = _fileno(f); + if (_isatty(fd)) { + detail::utf8_to_utf16 u16(string_view(text.data(), text.size())); + auto written = detail::dword(); + if (detail::WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), + u16.c_str(), static_cast<uint32_t>(u16.size()), + &written, nullptr)) { + return true; + } + } + // We return false if the file descriptor was not TTY, or it was but + // SetConsoleW failed which can happen if the output has been redirected to + // NUL. In both cases when we return false, we should attempt to do regular + // write via fwrite or std::ostream::write. + return false; +} +#endif + +FMT_FUNC void print(std::FILE* f, string_view text) { +#ifdef _WIN32 + if (write_console(f, text)) return; +#endif + detail::fwrite_fully(text.data(), 1, text.size(), f); +} +} // namespace detail + +FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { + memory_buffer buffer; + detail::vformat_to(buffer, format_str, args); + detail::print(f, {buffer.data(), buffer.size()}); +} + +#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); +} + +namespace detail { + +struct singleton { + unsigned char upper; + unsigned char lower_count; +}; + +inline auto is_printable(uint16_t x, const singleton* singletons, + size_t singletons_size, + const unsigned char* singleton_lowers, + const unsigned char* normal, size_t normal_size) + -> bool { + auto upper = x >> 8; + auto lower_start = 0; + for (size_t i = 0; i < singletons_size; ++i) { + auto s = singletons[i]; + auto lower_end = lower_start + s.lower_count; + if (upper < s.upper) break; + if (upper == s.upper) { + for (auto j = lower_start; j < lower_end; ++j) { + if (singleton_lowers[j] == (x & 0xff)) return false; + } + } + lower_start = lower_end; + } + + auto xsigned = static_cast<int>(x); + auto current = true; + for (size_t i = 0; i < normal_size; ++i) { + auto v = static_cast<int>(normal[i]); + auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v; + xsigned -= len; + if (xsigned < 0) break; + current = !current; + } + return current; +} + +// This code is generated by support/printable.py. +FMT_FUNC auto is_printable(uint32_t cp) -> bool { + static constexpr singleton singletons0[] = { + {0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8}, + {0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13}, + {0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5}, + {0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22}, + {0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3}, + {0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8}, + {0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9}, + }; + static constexpr unsigned char singletons0_lower[] = { + 0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90, + 0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f, + 0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1, + 0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04, + 0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d, + 0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf, + 0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, + 0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d, + 0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d, + 0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d, + 0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, + 0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7, + 0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, + 0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7, + 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7, + 0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e, + 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16, + 0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e, + 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f, + 0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf, + 0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0, + 0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, + 0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91, + 0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, + 0xfe, 0xff, + }; + static constexpr singleton singletons1[] = { + {0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2}, + {0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5}, + {0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5}, + {0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2}, + {0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5}, + {0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2}, + {0xfa, 2}, {0xfb, 1}, + }; + static constexpr unsigned char singletons1_lower[] = { + 0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07, + 0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36, + 0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87, + 0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, + 0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b, + 0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9, + 0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66, + 0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27, + 0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc, + 0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, + 0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6, + 0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c, + 0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66, + 0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0, + 0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93, + }; + static constexpr unsigned char normal0[] = { + 0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04, + 0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0, + 0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01, + 0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03, + 0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03, + 0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a, + 0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15, + 0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f, + 0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80, + 0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07, + 0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06, + 0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04, + 0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac, + 0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c, + 0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11, + 0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c, + 0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b, + 0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6, + 0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03, + 0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80, + 0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06, + 0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c, + 0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17, + 0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80, + 0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80, + 0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d, + }; + static constexpr unsigned char normal1[] = { + 0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f, + 0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e, + 0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04, + 0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09, + 0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16, + 0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f, + 0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36, + 0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33, + 0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08, + 0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e, + 0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41, + 0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03, + 0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22, + 0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04, + 0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45, + 0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03, + 0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81, + 0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75, + 0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1, + 0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a, + 0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11, + 0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09, + 0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89, + 0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6, + 0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09, + 0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50, + 0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05, + 0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83, + 0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05, + 0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80, + 0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80, + 0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07, + 0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e, + 0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07, + 0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06, + }; + auto lower = static_cast<uint16_t>(cp); + if (cp < 0x10000) { + return is_printable(lower, singletons0, + sizeof(singletons0) / sizeof(*singletons0), + singletons0_lower, normal0, sizeof(normal0)); + } + if (cp < 0x20000) { + return is_printable(lower, singletons1, + sizeof(singletons1) / sizeof(*singletons1), + singletons1_lower, normal1, sizeof(normal1)); + } + if (0x2a6de <= cp && cp < 0x2a700) return false; + if (0x2b735 <= cp && cp < 0x2b740) return false; + if (0x2b81e <= cp && cp < 0x2b820) return false; + if (0x2cea2 <= cp && cp < 0x2ceb0) return false; + if (0x2ebe1 <= cp && cp < 0x2f800) return false; + if (0x2fa1e <= cp && cp < 0x30000) return false; + if (0x3134b <= cp && cp < 0xe0100) return false; + if (0xe01f0 <= cp && cp < 0x110000) return false; + return cp < 0x110000; +} + +} // namespace detail + +FMT_END_NAMESPACE + +#endif // FMT_FORMAT_INL_H_ diff --git a/src/fmt/include/fmt/format.h b/src/fmt/include/fmt/format.h new file mode 100644 index 000000000..7c607dbd3 --- /dev/null +++ b/src/fmt/include/fmt/format.h @@ -0,0 +1,4217 @@ +/* + 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 <cmath> // std::signbit +#include <cstdint> // uint32_t +#include <cstring> // std::memcpy +#include <limits> // std::numeric_limits +#include <memory> // std::uninitialized_copy +#include <stdexcept> // std::runtime_error +#include <system_error> // std::system_error + +#ifdef __cpp_lib_bit_cast +# include <bit> // std::bitcast +#endif + +#include "core.h" + +#if FMT_GCC_VERSION +# define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden"))) +#else +# define FMT_GCC_VISIBILITY_HIDDEN +#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 FMT_MSC_VERSION +# define FMT_MSC_DEFAULT = default +#else +# define FMT_MSC_DEFAULT +#endif + +#ifndef FMT_THROW +# if FMT_EXCEPTIONS +# if FMT_MSC_VERSION || defined(__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 { \ + FMT_ASSERT(false, (x).what()); \ + } 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_MAYBE_UNUSED +# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) +# define FMT_MAYBE_UNUSED [[maybe_unused]] +# else +# define FMT_MAYBE_UNUSED +# endif +#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_VERSION >= 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 + +// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of +// integer formatter template instantiations to just one by only using the +// largest integer type. This results in a reduction in binary size but will +// cause a decrease in integer formatting performance. +#if !defined(FMT_REDUCE_INT_INSTANTIATIONS) +# define FMT_REDUCE_INT_INSTANTIATIONS 0 +#endif + +// __builtin_clz is broken in clang with Microsoft CodeGen: +// https://github.com/fmtlib/fmt/issues/519. +#if !FMT_MSC_VERSION +# if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +# endif +# if FMT_HAS_BUILTIN(__builtin_clzll) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +# endif +#endif + +// __builtin_ctz is broken in Intel Compiler Classic on Windows: +// https://github.com/fmtlib/fmt/issues/2510. +#ifndef __ICL +# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \ + defined(__NVCOMPILER) +# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) +# endif +# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \ + FMT_ICC_VERSION || defined(__NVCOMPILER) +# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) +# endif +#endif + +#if FMT_MSC_VERSION +# include <intrin.h> // _BitScanReverse[64], _BitScanForward[64], _umul128 +#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_VERSION && !defined(FMT_BUILTIN_CLZLL) && \ + !defined(FMT_BUILTIN_CTZLL) +FMT_BEGIN_NAMESPACE +namespace detail { +// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. +# if !defined(__clang__) +# pragma intrinsic(_BitScanForward) +# pragma intrinsic(_BitScanReverse) +# if defined(_WIN64) +# pragma intrinsic(_BitScanForward64) +# pragma intrinsic(_BitScanReverse64) +# endif +# endif + +inline auto clz(uint32_t x) -> int { + 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_MSC_WARNING(suppress : 6102) + return 31 ^ static_cast<int>(r); +} +# define FMT_BUILTIN_CLZ(n) detail::clz(n) + +inline auto clzll(uint64_t x) -> int { + 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, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. + return 63 ^ static_cast<int>(r); +} +# define FMT_BUILTIN_CLZLL(n) detail::clzll(n) + +inline auto ctz(uint32_t x) -> int { + unsigned long r = 0; + _BitScanForward(&r, x); + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. + return static_cast<int>(r); +} +# define FMT_BUILTIN_CTZ(n) detail::ctz(n) + +inline auto ctzll(uint64_t x) -> int { + unsigned long r = 0; + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. +# ifdef _WIN64 + _BitScanForward64(&r, x); +# else + // Scan the low 32 bits. + if (_BitScanForward(&r, static_cast<uint32_t>(x))) return static_cast<int>(r); + // Scan the high 32 bits. + _BitScanForward(&r, static_cast<uint32_t>(x >> 32)); + r += 32; +# endif + return static_cast<int>(r); +} +# define FMT_BUILTIN_CTZLL(n) detail::ctzll(n) +} // namespace detail +FMT_END_NAMESPACE +#endif + +FMT_BEGIN_NAMESPACE +namespace detail { + +FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { + ignore_unused(condition); +#ifdef FMT_FUZZ + if (condition) throw std::runtime_error("fuzzing limit reached"); +#endif +} + +template <typename CharT, CharT... C> struct string_literal { + static constexpr CharT value[sizeof...(C)] = {C...}; + constexpr operator basic_string_view<CharT>() const { + return {value, sizeof...(C)}; + } +}; + +#if FMT_CPLUSPLUS < 201703L +template <typename CharT, CharT... C> +constexpr CharT string_literal<CharT, C...>::value[sizeof...(C)]; +#endif + +template <typename Streambuf> class formatbuf : public Streambuf { + private: + using char_type = typename Streambuf::char_type; + using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0)); + using int_type = typename Streambuf::int_type; + using traits_type = typename Streambuf::traits_type; + + buffer<char_type>& buffer_; + + public: + explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {} + + protected: + // The put area is always empty. This makes the implementation simpler and has + // the advantage that the streambuf and the buffer are always in sync and + // sputc never writes into uninitialized memory. A disadvantage is that each + // call to sputc always results in a (virtual) call to overflow. There is no + // disadvantage here for sputn since this always results in a call to xsputn. + + auto overflow(int_type ch) -> int_type override { + if (!traits_type::eq_int_type(ch, traits_type::eof())) + buffer_.push_back(static_cast<char_type>(ch)); + return ch; + } + + auto xsputn(const char_type* s, streamsize count) -> streamsize override { + buffer_.append(s, s + count); + return count; + } +}; + +// Implementation of std::bit_cast for pre-C++20. +template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) == sizeof(From))> +FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { +#ifdef __cpp_lib_bit_cast + if (is_constant_evaluated()) return std::bit_cast<To>(from); +#endif + auto to = To(); + // The cast suppresses a bogus -Wclass-memaccess on GCC. + std::memcpy(static_cast<void*>(&to), &from, sizeof(to)); + return to; +} + +inline auto is_big_endian() -> bool { +#ifdef _WIN32 + return false; +#elif defined(__BIG_ENDIAN__) + return true; +#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) + return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__; +#else + struct bytes { + char data[sizeof(int)]; + }; + return bit_cast<bytes>(1).data[0] == 0; +#endif +} + +class uint128_fallback { + private: + uint64_t lo_, hi_; + + friend uint128_fallback umul128(uint64_t x, uint64_t y) noexcept; + + public: + constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {} + constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {} + + constexpr uint64_t high() const noexcept { return hi_; } + constexpr uint64_t low() const noexcept { return lo_; } + + template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> + constexpr explicit operator T() const { + return static_cast<T>(lo_); + } + + friend constexpr auto operator==(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> bool { + return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_; + } + friend constexpr auto operator!=(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> bool { + return !(lhs == rhs); + } + friend constexpr auto operator>(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> bool { + return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_; + } + friend constexpr auto operator|(const uint128_fallback& lhs, + const uint128_fallback& rhs) + -> uint128_fallback { + return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_}; + } + friend constexpr auto operator&(const uint128_fallback& lhs, + const uint128_fallback& rhs) + -> uint128_fallback { + return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_}; + } + friend auto operator+(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> uint128_fallback { + auto result = uint128_fallback(lhs); + result += rhs; + return result; + } + friend auto operator*(const uint128_fallback& lhs, uint32_t rhs) + -> uint128_fallback { + FMT_ASSERT(lhs.hi_ == 0, ""); + uint64_t hi = (lhs.lo_ >> 32) * rhs; + uint64_t lo = (lhs.lo_ & ~uint32_t()) * rhs; + uint64_t new_lo = (hi << 32) + lo; + return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo}; + } + friend auto operator-(const uint128_fallback& lhs, uint64_t rhs) + -> uint128_fallback { + return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs}; + } + FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback { + if (shift == 64) return {0, hi_}; + if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64); + return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)}; + } + FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback { + if (shift == 64) return {lo_, 0}; + if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64); + return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)}; + } + FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& { + return *this = *this >> shift; + } + FMT_CONSTEXPR void operator+=(uint128_fallback n) { + uint64_t new_lo = lo_ + n.lo_; + uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0); + FMT_ASSERT(new_hi >= hi_, ""); + lo_ = new_lo; + hi_ = new_hi; + } + + FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept { + if (is_constant_evaluated()) { + lo_ += n; + hi_ += (lo_ < n ? 1 : 0); + return *this; + } +#if FMT_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__) + unsigned long long carry; + lo_ = __builtin_addcll(lo_, n, 0, &carry); + hi_ += carry; +#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__) + unsigned long long result; + auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result); + lo_ = result; + hi_ += carry; +#elif defined(_MSC_VER) && defined(_M_X64) + auto carry = _addcarry_u64(0, lo_, n, &lo_); + _addcarry_u64(carry, hi_, 0, &hi_); +#else + lo_ += n; + hi_ += (lo_ < n ? 1 : 0); +#endif + return *this; + } +}; + +using uint128_t = conditional_t<FMT_USE_INT128, uint128_opt, uint128_fallback>; + +#ifdef UINTPTR_MAX +using uintptr_t = ::uintptr_t; +#else +using uintptr_t = uint128_t; +#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 auto max_value() -> T { + return (std::numeric_limits<T>::max)(); +} +template <typename T> constexpr auto num_bits() -> int { + return std::numeric_limits<T>::digits; +} +// std::numeric_limits<T>::digits may return 0 for 128-bit ints. +template <> constexpr auto num_bits<int128_opt>() -> int { return 128; } +template <> constexpr auto num_bits<uint128_t>() -> int { return 128; } + +// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t +// and 128-bit pointers to uint128_fallback. +template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) > sizeof(From))> +inline auto bit_cast(const From& from) -> To { + constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned)); + struct data_t { + unsigned value[static_cast<unsigned>(size)]; + } data = bit_cast<data_t>(from); + auto result = To(); + if (const_check(is_big_endian())) { + for (int i = 0; i < size; ++i) + result = (result << num_bits<unsigned>()) | data.value[i]; + } else { + for (int i = size - 1; i >= 0; --i) + result = (result << num_bits<unsigned>()) | data.value[i]; + } + return result; +} + +FMT_INLINE void assume(bool condition) { + (void)condition; +#if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION + __builtin_assume(condition); +#endif +} + +// 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&>())); + +// A workaround for std::string not having mutable data() until C++17. +template <typename Char> +inline auto get_data(std::basic_string<Char>& s) -> Char* { + return &s[0]; +} +template <typename Container> +inline auto get_data(Container& c) -> typename Container::value_type* { + 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> +constexpr auto make_checked(T* p, size_t size) -> checked_ptr<T> { + return {p, size}; +} +#else +template <typename T> using checked_ptr = T*; +template <typename T> constexpr auto make_checked(T* p, size_t) -> T* { + return p; +} +#endif + +// Attempts to reserve space for n extra characters in the output range. +// Returns a pointer to the reserved range or a reference to it. +template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)> +#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION +__attribute__((no_sanitize("undefined"))) +#endif +inline auto +reserve(std::back_insert_iterator<Container> it, size_t n) + -> checked_ptr<typename Container::value_type> { + Container& c = get_container(it); + size_t size = c.size(); + c.resize(size + n); + return make_checked(get_data(c) + size, n); +} + +template <typename T> +inline auto reserve(buffer_appender<T> it, size_t n) -> buffer_appender<T> { + buffer<T>& buf = get_container(it); + buf.try_reserve(buf.size() + n); + return it; +} + +template <typename Iterator> +constexpr auto reserve(Iterator& it, size_t) -> Iterator& { + return it; +} + +template <typename OutputIt> +using reserve_iterator = + remove_reference_t<decltype(reserve(std::declval<OutputIt&>(), 0))>; + +template <typename T, typename OutputIt> +constexpr auto to_pointer(OutputIt, size_t) -> T* { + return nullptr; +} +template <typename T> auto to_pointer(buffer_appender<T> it, size_t n) -> T* { + buffer<T>& buf = get_container(it); + auto size = buf.size(); + if (buf.capacity() < size + n) return nullptr; + buf.try_resize(size + n); + return buf.data() + size; +} + +template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)> +inline auto base_iterator(std::back_insert_iterator<Container>& it, + checked_ptr<typename Container::value_type>) + -> std::back_insert_iterator<Container> { + return it; +} + +template <typename Iterator> +constexpr auto base_iterator(Iterator, Iterator it) -> Iterator { + return it; +} + +// <algorithm> is spectacularly slow to compile in C++20 so use a simple fill_n +// instead (#1998). +template <typename OutputIt, typename Size, typename T> +FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) + -> OutputIt { + for (Size i = 0; i < count; ++i) *out++ = value; + return out; +} +template <typename T, typename Size> +FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* { + if (is_constant_evaluated()) { + return fill_n<T*, Size, T>(out, count, value); + } + std::memset(out, value, to_unsigned(count)); + return out + count; +} + +#ifdef __cpp_char8_t +using char8_type = char8_t; +#else +enum char8_type : unsigned char {}; +#endif + +template <typename OutChar, typename InputIt, typename OutputIt> +FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, + OutputIt out) -> OutputIt { + return copy_str<OutChar>(begin, end, out); +} + +// A public domain branchless UTF-8 decoder by Christopher Wellons: +// https://github.com/skeeto/branchless-utf8 +/* Decode the next character, c, from s, 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_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) + -> const char* { + constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; + constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; + constexpr const int shiftc[] = {0, 18, 12, 6, 0}; + constexpr const int shifte[] = {0, 6, 4, 2, 0}; + + int len = code_point_length_impl(*s); + // 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 = s + len + !len; + + using uchar = unsigned char; + + // Assume a four-byte character and load four bytes. Unused bits are + // shifted out. + *c = uint32_t(uchar(s[0]) & masks[len]) << 18; + *c |= uint32_t(uchar(s[1]) & 0x3f) << 12; + *c |= uint32_t(uchar(s[2]) & 0x3f) << 6; + *c |= uint32_t(uchar(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 |= (uchar(s[1]) & 0xc0) >> 2; + *e |= (uchar(s[2]) & 0xc0) >> 4; + *e |= uchar(s[3]) >> 6; + *e ^= 0x2a; // top two bits of each tail byte correct? + *e >>= shifte[len]; + + return next; +} + +constexpr uint32_t invalid_code_point = ~uint32_t(); + +// Invokes f(cp, sv) for every code point cp in s with sv being the string view +// corresponding to the code point. cp is invalid_code_point on error. +template <typename F> +FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { + auto decode = [f](const char* buf_ptr, const char* ptr) { + auto cp = uint32_t(); + auto error = 0; + auto end = utf8_decode(buf_ptr, &cp, &error); + bool result = f(error ? invalid_code_point : cp, + string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr))); + return result ? (error ? buf_ptr + 1 : end) : nullptr; + }; + 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 = decode(p, p); + if (!p) return; + } + } + if (auto num_chars_left = s.data() + s.size() - p) { + char buf[2 * block_size - 1] = {}; + copy_str<char>(p, p + num_chars_left, buf); + const char* buf_ptr = buf; + do { + auto end = decode(buf_ptr, p); + if (!end) return; + p += end - buf_ptr; + buf_ptr = end; + } while (buf_ptr - buf < num_chars_left); + } +} + +template <typename Char> +inline auto compute_width(basic_string_view<Char> s) -> size_t { + return s.size(); +} + +// Computes approximate display width of a UTF-8 string. +FMT_CONSTEXPR inline size_t compute_width(string_view s) { + size_t num_code_points = 0; + // It is not a lambda for compatibility with C++14. + struct count_code_points { + size_t* count; + FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool { + *count += detail::to_unsigned( + 1 + + (cp >= 0x1100 && + (cp <= 0x115f || // Hangul Jamo init. consonants + cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET + cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET + // CJK ... Yi except IDEOGRAPHIC HALF FILL SPACE: + (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) || + (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables + (cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs + (cp >= 0xfe10 && cp <= 0xfe19) || // Vertical Forms + (cp >= 0xfe30 && cp <= 0xfe6f) || // CJK Compatibility Forms + (cp >= 0xff00 && cp <= 0xff60) || // Fullwidth Forms + (cp >= 0xffe0 && cp <= 0xffe6) || // Fullwidth Forms + (cp >= 0x20000 && cp <= 0x2fffd) || // CJK + (cp >= 0x30000 && cp <= 0x3fffd) || + // Miscellaneous Symbols and Pictographs + Emoticons: + (cp >= 0x1f300 && cp <= 0x1f64f) || + // Supplemental Symbols and Pictographs: + (cp >= 0x1f900 && cp <= 0x1f9ff)))); + return true; + } + }; + for_each_codepoint(s, count_code_points{&num_code_points}); + return num_code_points; +} + +inline auto compute_width(basic_string_view<char8_type> s) -> size_t { + return compute_width( + string_view(reinterpret_cast<const char*>(s.data()), s.size())); +} + +template <typename Char> +inline auto code_point_index(basic_string_view<Char> s, size_t n) -> size_t { + size_t size = s.size(); + return n < size ? n : size; +} + +// Calculates the index of the nth code point in a UTF-8 string. +inline auto code_point_index(string_view s, size_t n) -> size_t { + 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 > n) return i; + } + return s.size(); +} + +inline auto code_point_index(basic_string_view<char8_type> s, size_t n) + -> size_t { + return code_point_index( + string_view(reinterpret_cast<const char*>(s.data()), s.size()), n); +} + +#ifndef FMT_USE_FLOAT128 +# ifdef __SIZEOF_FLOAT128__ +# define FMT_USE_FLOAT128 1 +# else +# define FMT_USE_FLOAT128 0 +# endif +#endif +#if FMT_USE_FLOAT128 +using float128 = __float128; +#else +using float128 = void; +#endif +template <typename T> using is_float128 = std::is_same<T, float128>; + +template <typename T> +using is_floating_point = + bool_constant<std::is_floating_point<T>::value || is_float128<T>::value>; + +template <typename T, bool = std::is_floating_point<T>::value> +struct is_fast_float : bool_constant<std::numeric_limits<T>::is_iec559 && + sizeof(T) <= sizeof(double)> {}; +template <typename T> struct is_fast_float<T, false> : std::false_type {}; + +template <typename T> +using is_double_double = bool_constant<std::numeric_limits<T>::digits == 106>; + +#ifndef FMT_USE_FULL_CACHE_DRAGONBOX +# define FMT_USE_FULL_CACHE_DRAGONBOX 0 +#endif + +template <typename T> +template <typename U> +void buffer<T>::append(const U* begin, const U* end) { + while (begin != end) { + auto count = to_unsigned(end - begin); + try_reserve(size_ + count); + auto free_cap = capacity_ - size_; + if (free_cap < count) count = free_cap; + std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count)); + size_ += count; + begin += count; + } +} + +template <typename T, typename Enable = void> +struct is_locale : std::false_type {}; +template <typename T> +struct is_locale<T, void_t<decltype(T::classic())>> : std::true_type {}; +} // namespace detail + +FMT_MODULE_EXPORT_BEGIN + +// 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 the ``memory_buffer`` type alias for ``char`` instead. + + **Example**:: + + auto out = fmt::memory_buffer(); + format_to(std::back_inserter(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 final : 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. + FMT_CONSTEXPR20 void deallocate() { + T* data = this->data(); + if (data != store_) alloc_.deallocate(data, this->capacity()); + } + + protected: + FMT_CONSTEXPR20 void grow(size_t size) override; + + public: + using value_type = T; + using const_reference = const T&; + + FMT_CONSTEXPR20 explicit basic_memory_buffer( + const Allocator& alloc = Allocator()) + : alloc_(alloc) { + this->set(store_, SIZE); + if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T()); + } + FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); } + + private: + // Move data from other to this buffer. + FMT_CONSTEXPR20 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); + detail::copy_str<T>(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); + other.clear(); + } + this->resize(size); + } + + public: + /** + \rst + Constructs a :class:`fmt::basic_memory_buffer` object moving the content + of the other object to it. + \endrst + */ + FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept { + move(other); + } + + /** + \rst + Moves the content of the other ``basic_memory_buffer`` object to this one. + \endrst + */ + auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& { + FMT_ASSERT(this != &other, ""); + deallocate(); + move(other); + return *this; + } + + // Returns a copy of the allocator associated with this buffer. + auto get_allocator() const -> Allocator { return alloc_; } + + /** + Resizes the buffer to contain *count* elements. If T is a POD type new + elements may not be initialized. + */ + FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); } + + /** Increases the buffer capacity to *new_capacity*. */ + void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } + + // Directly append data into the buffer + using detail::buffer<T>::append; + template <typename ContiguousRange> + void append(const ContiguousRange& range) { + append(range.data(), range.data() + range.size()); + } +}; + +template <typename T, size_t SIZE, typename Allocator> +FMT_CONSTEXPR20 void basic_memory_buffer<T, SIZE, Allocator>::grow( + size_t size) { + detail::abort_fuzzing_if(size > 5000); + const size_t max_size = std::allocator_traits<Allocator>::max_size(alloc_); + size_t old_capacity = this->capacity(); + size_t new_capacity = old_capacity + old_capacity / 2; + if (size > new_capacity) + new_capacity = size; + else if (new_capacity > max_size) + new_capacity = size > max_size ? size : max_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>; + +template <typename T, size_t SIZE, typename Allocator> +struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type { +}; + +namespace detail { +#ifdef _WIN32 +FMT_API bool write_console(std::FILE* f, string_view text); +#endif +FMT_API void print(std::FILE*, string_view); +} // namespace detail + +/** 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() noexcept override FMT_MSC_DEFAULT; +}; + +namespace detail_exported { +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +template <typename Char, size_t N> struct fixed_string { + constexpr fixed_string(const Char (&str)[N]) { + detail::copy_str<Char, const Char*, Char*>(static_cast<const Char*>(str), + str + N, data); + } + Char data[N] = {}; +}; +#endif + +// Converts a compile-time string to basic_string_view. +template <typename Char, size_t N> +constexpr auto compile_string_to_view(const Char (&s)[N]) + -> basic_string_view<Char> { + // Remove trailing NUL character if needed. Won't be present if this is used + // with a 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)}; +} +template <typename Char> +constexpr auto compile_string_to_view(detail::std_string_view<Char> s) + -> basic_string_view<Char> { + return {s.data(), s.size()}; +} +} // namespace detail_exported + +FMT_BEGIN_DETAIL_NAMESPACE + +template <typename T> struct is_integral : std::is_integral<T> {}; +template <> struct is_integral<int128_opt> : std::true_type {}; +template <> struct is_integral<uint128_t> : std::true_type {}; + +template <typename T> +using is_signed = + std::integral_constant<bool, std::numeric_limits<T>::is_signed || + std::is_same<T, int128_opt>::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)> +constexpr auto is_negative(T value) -> bool { + return value < 0; +} +template <typename T, FMT_ENABLE_IF(!is_signed<T>::value)> +constexpr auto is_negative(T) -> bool { + return false; +} + +template <typename T> +FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool { + if (std::is_same<T, float>()) return FMT_USE_FLOAT; + if (std::is_same<T, double>()) return FMT_USE_DOUBLE; + if (std::is_same<T, long double>()) return FMT_USE_LONG_DOUBLE; + return true; +} + +// Smallest of uint32_t, uint64_t, uint128_t that is large enough to +// represent all values of an integral type T. +template <typename T> +using uint32_or_64_or_128_t = + conditional_t<num_bits<T>() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS, + uint32_t, + conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>; +template <typename T> +using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>; + +#define FMT_POWERS_OF_10(factor) \ + factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ + (factor)*1000000, (factor)*10000000, (factor)*100000000, \ + (factor)*1000000000 + +// Converts value in the range [0, 100) to a string. +constexpr const char* digits2(size_t value) { + // GCC generates slightly better code when value is pointer-size. + return &"0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"[value * 2]; +} + +// Sign is a template parameter to workaround a bug in gcc 4.8. +template <typename Char, typename Sign> constexpr Char sign(Sign s) { +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604 + static_assert(std::is_same<Sign, sign_t>::value, ""); +#endif + return static_cast<Char>("\0-+ "[s]); +} + +template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { + 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; + } +} +#if FMT_USE_INT128 +FMT_CONSTEXPR inline auto count_digits(uint128_opt n) -> int { + return count_digits_fallback(n); +} +#endif + +#ifdef FMT_BUILTIN_CLZLL +// It is a separate function rather than a part of count_digits to workaround +// the lack of static constexpr in constexpr functions. +inline auto do_count_digits(uint64_t n) -> int { + // This has comparable performance to the version by Kendall Willets + // (https://github.com/fmtlib/format-benchmark/blob/master/digits10) + // but uses smaller tables. + // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). + static constexpr uint8_t bsr2log10[] = { + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, + 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; + auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; + static constexpr const uint64_t zero_or_powers_of_10[] = { + 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + return t - (n < zero_or_powers_of_10[t]); +} +#endif + +// Returns the number of decimal digits in n. Leading zeros are not counted +// except for n == 0 in which case count_digits returns 1. +FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { +#ifdef FMT_BUILTIN_CLZLL + if (!is_constant_evaluated()) { + return do_count_digits(n); + } +#endif + return count_digits_fallback(n); +} + +// Counts the number of digits in n. BITS = log2(radix). +template <int BITS, typename UInt> +FMT_CONSTEXPR auto count_digits(UInt n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (!is_constant_evaluated() && num_bits<UInt>() == 32) + return (FMT_BUILTIN_CLZ(static_cast<uint32_t>(n) | 1) ^ 31) / BITS + 1; +#endif + // Lambda avoids unreachable code warnings from NVHPC. + return [](UInt m) { + int num_digits = 0; + do { + ++num_digits; + } while ((m >>= BITS) != 0); + return num_digits; + }(n); +} + +#ifdef FMT_BUILTIN_CLZ +// It is a separate function rather than a part of count_digits to workaround +// the lack of static constexpr in constexpr functions. +FMT_INLINE auto do_count_digits(uint32_t n) -> int { +// An optimization by Kendall Willets from https://bit.ly/3uOIQrB. +// This increments the upper 32 bits (log10(T) - 1) when >= T is added. +# define FMT_INC(T) (((sizeof(# T) - 1ull) << 32) - T) + static constexpr uint64_t table[] = { + FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8 + FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64 + FMT_INC(100), FMT_INC(100), FMT_INC(100), // 512 + FMT_INC(1000), FMT_INC(1000), FMT_INC(1000), // 4096 + FMT_INC(10000), FMT_INC(10000), FMT_INC(10000), // 32k + FMT_INC(100000), FMT_INC(100000), FMT_INC(100000), // 256k + FMT_INC(1000000), FMT_INC(1000000), FMT_INC(1000000), // 2048k + FMT_INC(10000000), FMT_INC(10000000), FMT_INC(10000000), // 16M + FMT_INC(100000000), FMT_INC(100000000), FMT_INC(100000000), // 128M + FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000), // 1024M + FMT_INC(1000000000), FMT_INC(1000000000) // 4B + }; + auto inc = table[FMT_BUILTIN_CLZ(n | 1) ^ 31]; + return static_cast<int>((n + inc) >> 32); +} +#endif + +// Optional version of count_digits for better performance on 32-bit platforms. +FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (!is_constant_evaluated()) { + return do_count_digits(n); + } +#endif + return count_digits_fallback(n); +} + +template <typename Int> constexpr auto digits10() noexcept -> int { + return std::numeric_limits<Int>::digits10; +} +template <> constexpr auto digits10<int128_opt>() noexcept -> int { return 38; } +template <> constexpr auto digits10<uint128_t>() noexcept -> int { return 38; } + +template <typename Char> struct thousands_sep_result { + std::string grouping; + Char thousands_sep; +}; + +template <typename Char> +FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char>; +template <typename Char> +inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<Char> { + auto result = thousands_sep_impl<char>(loc); + return {result.grouping, Char(result.thousands_sep)}; +} +template <> +inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<wchar_t> { + return thousands_sep_impl<wchar_t>(loc); +} + +template <typename Char> +FMT_API auto decimal_point_impl(locale_ref loc) -> Char; +template <typename Char> inline auto decimal_point(locale_ref loc) -> Char { + return Char(decimal_point_impl<char>(loc)); +} +template <> inline auto decimal_point(locale_ref loc) -> wchar_t { + return decimal_point_impl<wchar_t>(loc); +} + +// Compares two characters for equality. +template <typename Char> auto equal2(const Char* lhs, const char* rhs) -> bool { + return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]); +} +inline auto equal2(const char* lhs, const char* rhs) -> bool { + return memcmp(lhs, rhs, 2) == 0; +} + +// Copies two characters from src to dst. +template <typename Char> +FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) { + if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) { + memcpy(dst, src, 2); + return; + } + *dst++ = static_cast<Char>(*src++); + *dst = static_cast<Char>(*src); +} + +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> +FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) + -> format_decimal_result<Char*> { + 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, digits2(static_cast<size_t>(value % 100))); + value /= 100; + } + if (value < 10) { + *--out = static_cast<Char>('0' + value); + return {out, end}; + } + out -= 2; + copy2(out, digits2(static_cast<size_t>(value))); + return {out, end}; +} + +template <typename Char, typename UInt, typename Iterator, + FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)> +FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size) + -> format_decimal_result<Iterator> { + // Buffer is large enough to hold all digits (digits10 + 1). + Char buffer[digits10<UInt>() + 1]; + auto end = format_decimal(buffer, value, size).end; + return {out, detail::copy_str_noinline<Char>(buffer, end, out)}; +} + +template <unsigned BASE_BITS, typename Char, typename UInt> +FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, + bool upper = false) -> Char* { + buffer += num_digits; + Char* end = buffer; + do { + const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; + unsigned digit = static_cast<unsigned>(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, typename It, typename UInt> +inline auto format_uint(It out, UInt value, int num_digits, bool upper = false) + -> It { + if (auto ptr = to_pointer<Char>(out, to_unsigned(num_digits))) { + format_uint<BASE_BITS>(ptr, value, num_digits, upper); + return out; + } + // 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_noinline<Char>(buffer, buffer + num_digits, out); +} + +// A converter from UTF-8 to UTF-16. +class utf8_to_utf16 { + private: + basic_memory_buffer<wchar_t> buffer_; + + public: + FMT_API explicit utf8_to_utf16(string_view s); + operator basic_string_view<wchar_t>() const { return {&buffer_[0], size()}; } + auto size() const -> size_t { return buffer_.size() - 1; } + auto c_str() const -> const wchar_t* { return &buffer_[0]; } + auto str() const -> std::wstring { return {&buffer_[0], size()}; } +}; + +namespace dragonbox { + +// Type-specific information that Dragonbox uses. +template <typename T, typename Enable = void> struct float_info; + +template <> struct float_info<float> { + using carrier_uint = uint32_t; + static const int exponent_bits = 8; + static const int kappa = 1; + static const int big_divisor = 100; + static const int small_divisor = 10; + static const int min_k = -31; + static const int max_k = 46; + static const int shorter_interval_tie_lower_threshold = -35; + static const int shorter_interval_tie_upper_threshold = -35; +}; + +template <> struct float_info<double> { + using carrier_uint = uint64_t; + static const int exponent_bits = 11; + static const int kappa = 2; + static const int big_divisor = 1000; + static const int small_divisor = 100; + static const int min_k = -292; + static const int max_k = 326; + static const int shorter_interval_tie_lower_threshold = -77; + static const int shorter_interval_tie_upper_threshold = -77; +}; + +// An 80- or 128-bit floating point number. +template <typename T> +struct float_info<T, enable_if_t<std::numeric_limits<T>::digits == 64 || + std::numeric_limits<T>::digits == 113 || + is_float128<T>::value>> { + using carrier_uint = detail::uint128_t; + static const int exponent_bits = 15; +}; + +// A double-double floating point number. +template <typename T> +struct float_info<T, enable_if_t<is_double_double<T>::value>> { + using carrier_uint = detail::uint128_t; +}; + +template <typename T> struct decimal_fp { + using significand_type = typename float_info<T>::carrier_uint; + significand_type significand; + int exponent; +}; + +template <typename T> FMT_API auto to_decimal(T x) noexcept -> decimal_fp<T>; +} // namespace dragonbox + +// Returns true iff Float has the implicit bit which is not stored. +template <typename Float> constexpr bool has_implicit_bit() { + // An 80-bit FP number has a 64-bit significand an no implicit bit. + return std::numeric_limits<Float>::digits != 64; +} + +// Returns the number of significand bits stored in Float. The implicit bit is +// not counted since it is not stored. +template <typename Float> constexpr int num_significand_bits() { + // std::numeric_limits may not support __float128. + return is_float128<Float>() ? 112 + : (std::numeric_limits<Float>::digits - + (has_implicit_bit<Float>() ? 1 : 0)); +} + +template <typename Float> +constexpr auto exponent_mask() -> + typename dragonbox::float_info<Float>::carrier_uint { + using uint = typename dragonbox::float_info<Float>::carrier_uint; + return ((uint(1) << dragonbox::float_info<Float>::exponent_bits) - 1) + << num_significand_bits<Float>(); +} +template <typename Float> constexpr auto exponent_bias() -> int { + // std::numeric_limits may not support __float128. + return is_float128<Float>() ? 16383 + : std::numeric_limits<Float>::max_exponent - 1; +} + +// Writes the exponent exp in the form "[+-]d{2,3}" to buffer. +template <typename Char, typename It> +FMT_CONSTEXPR auto write_exponent(int exp, It 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 = digits2(to_unsigned(exp / 100)); + if (exp >= 1000) *it++ = static_cast<Char>(top[0]); + *it++ = static_cast<Char>(top[1]); + exp %= 100; + } + const char* d = digits2(to_unsigned(exp)); + *it++ = static_cast<Char>(d[0]); + *it++ = static_cast<Char>(d[1]); + return it; +} + +// A floating-point number f * pow(2, e) where F is an unsigned type. +template <typename F> struct basic_fp { + F f; + int e; + + static constexpr const int num_significand_bits = + static_cast<int>(sizeof(F) * num_bits<unsigned char>()); + + constexpr basic_fp() : f(0), e(0) {} + constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} + + // Constructs fp from an IEEE754 floating-point number. + template <typename Float> FMT_CONSTEXPR basic_fp(Float n) { assign(n); } + + // Assigns n to this and return true iff predecessor is closer than successor. + template <typename Float, FMT_ENABLE_IF(!is_double_double<Float>::value)> + FMT_CONSTEXPR auto assign(Float n) -> bool { + static_assert(std::numeric_limits<Float>::digits <= 113, "unsupported FP"); + // Assume Float is in the format [sign][exponent][significand]. + using carrier_uint = typename dragonbox::float_info<Float>::carrier_uint; + const auto num_float_significand_bits = + detail::num_significand_bits<Float>(); + const auto implicit_bit = carrier_uint(1) << num_float_significand_bits; + const auto significand_mask = implicit_bit - 1; + auto u = bit_cast<carrier_uint>(n); + f = static_cast<F>(u & significand_mask); + auto biased_e = static_cast<int>((u & exponent_mask<Float>()) >> + num_float_significand_bits); + // The predecessor is closer if n is a normalized power of 2 (f == 0) + // other than the smallest normalized number (biased_e > 1). + auto is_predecessor_closer = f == 0 && biased_e > 1; + if (biased_e == 0) + biased_e = 1; // Subnormals use biased exponent 1 (min exponent). + else if (has_implicit_bit<Float>()) + f += static_cast<F>(implicit_bit); + e = biased_e - exponent_bias<Float>() - num_float_significand_bits; + if (!has_implicit_bit<Float>()) ++e; + return is_predecessor_closer; + } + + template <typename Float, FMT_ENABLE_IF(is_double_double<Float>::value)> + FMT_CONSTEXPR auto assign(Float n) -> bool { + static_assert(std::numeric_limits<double>::is_iec559, "unsupported FP"); + return assign(static_cast<double>(n)); + } +}; + +using fp = basic_fp<unsigned long long>; + +// Normalizes the value converted from double and multiplied by (1 << SHIFT). +template <int SHIFT = 0, typename F> +FMT_CONSTEXPR basic_fp<F> normalize(basic_fp<F> value) { + // Handle subnormals. + const auto implicit_bit = F(1) << num_significand_bits<double>(); + const auto shifted_implicit_bit = 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 = basic_fp<F>::num_significand_bits - + num_significand_bits<double>() - SHIFT - 1; + value.f <<= offset; + value.e -= offset; + return value; +} + +// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. +FMT_CONSTEXPR 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 +} + +FMT_CONSTEXPR inline fp operator*(fp x, fp y) { + return {multiply(x.f, y.f), x.e + y.e + 64}; +} + +template <typename T = void> struct basic_data { + // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. + // These are generated by support/compute-powers.py. + static constexpr uint64_t pow10_significands[87] = { + 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, + }; + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wnarrowing" +#endif + // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding + // to significands above. + static constexpr int16_t pow10_exponents[87] = { + -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}; +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +# pragma GCC diagnostic pop +#endif + + static constexpr uint64_t power_of_10_64[20] = { + 1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; +}; + +#if FMT_CPLUSPLUS < 201703L +template <typename T> constexpr uint64_t basic_data<T>::pow10_significands[]; +template <typename T> constexpr int16_t basic_data<T>::pow10_exponents[]; +template <typename T> constexpr uint64_t basic_data<T>::power_of_10_64[]; +#endif + +// This is a struct rather than an alias to avoid shadowing warnings in gcc. +struct data : basic_data<> {}; + +// 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`. +FMT_CONSTEXPR inline fp get_cached_power(int min_exponent, + int& pow10_exponent) { + const int shift = 32; + // log10(2) = 0x0.4d104d427de7fbcc... + const int64_t significand = 0x4d104d427de7fbcc; + int index = static_cast<int>( + ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) + + ((int64_t(1) << shift) - 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; + // Using *(x + index) instead of x[index] avoids an issue with some compilers + // using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode). + return {*(data::pow10_significands + index), + *(data::pow10_exponents + index)}; +} + +#ifndef _MSC_VER +# define FMT_SNPRINTF snprintf +#else +FMT_API auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...) -> int; +# define FMT_SNPRINTF fmt_snprintf +#endif // _MSC_VER + +// Formats a floating-point number with snprintf using the hexfloat format. +template <typename T> +auto snprintf_float(T value, int precision, float_specs specs, + buffer<char>& buf) -> int { + // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. + FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); + FMT_ASSERT(specs.format == float_format::hex, ""); + static_assert(!std::is_same<T, float>::value, ""); + + // Build the format string. + char format[7]; // The longest format is "%#.*Le". + char* format_ptr = format; + *format_ptr++ = '%'; + if (specs.showpoint) *format_ptr++ = '#'; + if (precision >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + if (std::is_same<T, long double>()) *format_ptr++ = 'L'; + *format_ptr++ = 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; + abort_fuzzing_if(precision > 100000); + // 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) { + // The buffer will grow exponentially. + buf.try_reserve(buf.capacity() + 1); + continue; + } + auto size = to_unsigned(result); + // Size equal to capacity means that the last character was truncated. + if (size < capacity) { + buf.try_resize(size + offset); + return 0; + } + buf.try_reserve(size + offset + 1); // Add 1 for the terminating '\0'. + } +} + +template <typename T> +using convert_float_result = + conditional_t<std::is_same<T, float>::value || sizeof(T) == sizeof(double), + double, T>; + +template <typename T> +constexpr auto convert_float(T value) -> convert_float_result<T> { + return static_cast<convert_float_result<T>>(value); +} + +template <typename OutputIt, typename Char> +FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, + const fill_t<Char>& fill) -> OutputIt { + auto fill_size = fill.size(); + if (fill_size == 1) return detail::fill_n(it, n, fill[0]); + auto data = fill.data(); + for (size_t i = 0; i < n; ++i) + it = copy_str<Char>(data, 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> +FMT_CONSTEXPR auto write_padded(OutputIt out, + const basic_format_specs<Char>& specs, + size_t size, size_t width, F&& f) -> OutputIt { + 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; + // Shifts are encoded as string literals because static constexpr is not + // supported in constexpr functions. + auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; + size_t left_padding = padding >> shifts[specs.align]; + size_t right_padding = padding - left_padding; + auto it = reserve(out, size + padding * specs.fill.size()); + if (left_padding != 0) it = fill(it, left_padding, specs.fill); + it = f(it); + if (right_padding != 0) it = fill(it, right_padding, specs.fill); + return base_iterator(out, it); +} + +template <align::type align = align::left, typename OutputIt, typename Char, + typename F> +constexpr auto write_padded(OutputIt out, const basic_format_specs<Char>& specs, + size_t size, F&& f) -> OutputIt { + return write_padded<align>(out, specs, size, size, f); +} + +template <align::type align = align::left, typename Char, typename OutputIt> +FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, + const basic_format_specs<Char>& specs) + -> OutputIt { + return write_padded<align>( + out, specs, bytes.size(), [bytes](reserve_iterator<OutputIt> it) { + const char* data = bytes.data(); + return copy_str<Char>(data, data + bytes.size(), it); + }); +} + +template <typename Char, typename OutputIt, typename UIntPtr> +auto write_ptr(OutputIt out, UIntPtr value, + const basic_format_specs<Char>* specs) -> OutputIt { + int num_digits = count_digits<4>(value); + auto size = to_unsigned(num_digits) + size_t(2); + auto write = [=](reserve_iterator<OutputIt> 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))); +} + +// Returns true iff the code point cp is printable. +FMT_API auto is_printable(uint32_t cp) -> bool; + +inline auto needs_escape(uint32_t cp) -> bool { + return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' || + !is_printable(cp); +} + +template <typename Char> struct find_escape_result { + const Char* begin; + const Char* end; + uint32_t cp; +}; + +template <typename Char> +using make_unsigned_char = + typename conditional_t<std::is_integral<Char>::value, + std::make_unsigned<Char>, + type_identity<uint32_t>>::type; + +template <typename Char> +auto find_escape(const Char* begin, const Char* end) + -> find_escape_result<Char> { + for (; begin != end; ++begin) { + uint32_t cp = static_cast<make_unsigned_char<Char>>(*begin); + if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue; + if (needs_escape(cp)) return {begin, begin + 1, cp}; + } + return {begin, nullptr, 0}; +} + +inline auto find_escape(const char* begin, const char* end) + -> find_escape_result<char> { + if (!is_utf8()) return find_escape<char>(begin, end); + auto result = find_escape_result<char>{end, nullptr, 0}; + for_each_codepoint(string_view(begin, to_unsigned(end - begin)), + [&](uint32_t cp, string_view sv) { + if (needs_escape(cp)) { + result = {sv.begin(), sv.end(), cp}; + return false; + } + return true; + }); + return result; +} + +#define FMT_STRING_IMPL(s, base, explicit) \ + [] { \ + /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ + /* Use a macro-like name to avoid shadowing warnings. */ \ + struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \ + using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t<decltype(s[0])>; \ + FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ + operator fmt::basic_string_view<char_type>() const { \ + return fmt::detail_exported::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::detail::compile_string, ) + +template <size_t width, typename Char, typename OutputIt> +auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { + *out++ = static_cast<Char>('\\'); + *out++ = static_cast<Char>(prefix); + Char buf[width]; + fill_n(buf, width, static_cast<Char>('0')); + format_uint<4>(buf, cp, width); + return copy_str<Char>(buf, buf + width, out); +} + +template <typename OutputIt, typename Char> +auto write_escaped_cp(OutputIt out, const find_escape_result<Char>& escape) + -> OutputIt { + auto c = static_cast<Char>(escape.cp); + switch (escape.cp) { + case '\n': + *out++ = static_cast<Char>('\\'); + c = static_cast<Char>('n'); + break; + case '\r': + *out++ = static_cast<Char>('\\'); + c = static_cast<Char>('r'); + break; + case '\t': + *out++ = static_cast<Char>('\\'); + c = static_cast<Char>('t'); + break; + case '"': + FMT_FALLTHROUGH; + case '\'': + FMT_FALLTHROUGH; + case '\\': + *out++ = static_cast<Char>('\\'); + break; + default: + if (is_utf8()) { + if (escape.cp < 0x100) { + return write_codepoint<2, Char>(out, 'x', escape.cp); + } + if (escape.cp < 0x10000) { + return write_codepoint<4, Char>(out, 'u', escape.cp); + } + if (escape.cp < 0x110000) { + return write_codepoint<8, Char>(out, 'U', escape.cp); + } + } + for (Char escape_char : basic_string_view<Char>( + escape.begin, to_unsigned(escape.end - escape.begin))) { + out = write_codepoint<2, Char>(out, 'x', + static_cast<uint32_t>(escape_char) & 0xFF); + } + return out; + } + *out++ = c; + return out; +} + +template <typename Char, typename OutputIt> +auto write_escaped_string(OutputIt out, basic_string_view<Char> str) + -> OutputIt { + *out++ = static_cast<Char>('"'); + auto begin = str.begin(), end = str.end(); + do { + auto escape = find_escape(begin, end); + out = copy_str<Char>(begin, escape.begin, out); + begin = escape.end; + if (!begin) break; + out = write_escaped_cp<OutputIt, Char>(out, escape); + } while (begin != end); + *out++ = static_cast<Char>('"'); + return out; +} + +template <typename Char, typename OutputIt> +auto write_escaped_char(OutputIt out, Char v) -> OutputIt { + *out++ = static_cast<Char>('\''); + if ((needs_escape(static_cast<uint32_t>(v)) && v != static_cast<Char>('"')) || + v == static_cast<Char>('\'')) { + out = write_escaped_cp( + out, find_escape_result<Char>{&v, &v + 1, static_cast<uint32_t>(v)}); + } else { + *out++ = v; + } + *out++ = static_cast<Char>('\''); + return out; +} + +template <typename Char, typename OutputIt> +FMT_CONSTEXPR auto write_char(OutputIt out, Char value, + const basic_format_specs<Char>& specs) + -> OutputIt { + bool is_debug = specs.type == presentation_type::debug; + return write_padded(out, specs, 1, [=](reserve_iterator<OutputIt> it) { + if (is_debug) return write_escaped_char(it, value); + *it++ = value; + return it; + }); +} +template <typename Char, typename OutputIt> +FMT_CONSTEXPR auto write(OutputIt out, Char value, + const basic_format_specs<Char>& specs, + locale_ref loc = {}) -> OutputIt { + return check_char_specs(specs) + ? write_char(out, value, specs) + : write(out, static_cast<int>(value), specs, loc); +} + +// 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; + + FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix, + const basic_format_specs<Char>& specs) + : size((prefix >> 24) + 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 >> 24) + 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 write_digits(it). +// prefix contains chars in three lower bytes and the size in the fourth byte. +template <typename OutputIt, typename Char, typename W> +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, + unsigned prefix, + const basic_format_specs<Char>& specs, + W write_digits) -> OutputIt { + // Slightly faster check for specs.width == 0 && specs.precision == -1. + if ((specs.width | (specs.precision + 1)) == 0) { + auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); + if (prefix != 0) { + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast<Char>(p & 0xff); + } + return base_iterator(out, write_digits(it)); + } + auto data = write_int_data<Char>(num_digits, prefix, specs); + return write_padded<align::right>( + out, specs, data.size, [=](reserve_iterator<OutputIt> it) { + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast<Char>(p & 0xff); + it = detail::fill_n(it, data.padding, static_cast<Char>('0')); + return write_digits(it); + }); +} + +template <typename Char> class digit_grouping { + private: + thousands_sep_result<Char> sep_; + + struct next_state { + std::string::const_iterator group; + int pos; + }; + next_state initial_state() const { return {sep_.grouping.begin(), 0}; } + + // Returns the next digit group separator position. + int next(next_state& state) const { + if (!sep_.thousands_sep) return max_value<int>(); + if (state.group == sep_.grouping.end()) + return state.pos += sep_.grouping.back(); + if (*state.group <= 0 || *state.group == max_value<char>()) + return max_value<int>(); + state.pos += *state.group++; + return state.pos; + } + + public: + explicit digit_grouping(locale_ref loc, bool localized = true) { + if (localized) + sep_ = thousands_sep<Char>(loc); + else + sep_.thousands_sep = Char(); + } + explicit digit_grouping(thousands_sep_result<Char> sep) : sep_(sep) {} + + Char separator() const { return sep_.thousands_sep; } + + int count_separators(int num_digits) const { + int count = 0; + auto state = initial_state(); + while (num_digits > next(state)) ++count; + return count; + } + + // Applies grouping to digits and write the output to out. + template <typename Out, typename C> + Out apply(Out out, basic_string_view<C> digits) const { + auto num_digits = static_cast<int>(digits.size()); + auto separators = basic_memory_buffer<int>(); + separators.push_back(0); + auto state = initial_state(); + while (int i = next(state)) { + if (i >= num_digits) break; + separators.push_back(i); + } + for (int i = 0, sep_index = static_cast<int>(separators.size() - 1); + i < num_digits; ++i) { + if (num_digits - i == separators[sep_index]) { + *out++ = separator(); + --sep_index; + } + *out++ = static_cast<Char>(digits[to_unsigned(i)]); + } + return out; + } +}; + +template <typename OutputIt, typename UInt, typename Char> +auto write_int_localized(OutputIt out, UInt value, unsigned prefix, + const basic_format_specs<Char>& specs, + const digit_grouping<Char>& grouping) -> OutputIt { + static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, ""); + int num_digits = count_digits(value); + char digits[40]; + format_decimal(digits, value, num_digits); + unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits + + grouping.count_separators(num_digits)); + return write_padded<align::right>( + out, specs, size, size, [&](reserve_iterator<OutputIt> it) { + if (prefix != 0) { + char sign = static_cast<char>(prefix); + *it++ = static_cast<Char>(sign); + } + return grouping.apply(it, string_view(digits, to_unsigned(num_digits))); + }); +} + +template <typename OutputIt, typename UInt, typename Char> +auto write_int_localized(OutputIt& out, UInt value, unsigned prefix, + const basic_format_specs<Char>& specs, locale_ref loc) + -> bool { + auto grouping = digit_grouping<Char>(loc); + out = write_int_localized(out, value, prefix, specs, grouping); + return true; +} + +FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { + prefix |= prefix != 0 ? value << 8 : value; + prefix += (1u + (value > 0xff ? 1 : 0)) << 24; +} + +template <typename UInt> struct write_int_arg { + UInt abs_value; + unsigned prefix; +}; + +template <typename T> +FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) + -> write_int_arg<uint32_or_64_or_128_t<T>> { + auto prefix = 0u; + auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value); + if (is_negative(value)) { + prefix = 0x01000000 | '-'; + abs_value = 0 - abs_value; + } else { + constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', + 0x1000000u | ' '}; + prefix = prefixes[sign]; + } + return {abs_value, prefix}; +} + +template <typename Char, typename OutputIt, typename T> +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg, + const basic_format_specs<Char>& specs, + locale_ref loc) -> OutputIt { + static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, ""); + auto abs_value = arg.abs_value; + auto prefix = arg.prefix; + switch (specs.type) { + case presentation_type::none: + case presentation_type::dec: { + if (specs.localized && + write_int_localized(out, static_cast<uint64_or_128_t<T>>(abs_value), + prefix, specs, loc)) { + return out; + } + auto num_digits = count_digits(abs_value); + return write_int( + out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) { + return format_decimal<Char>(it, abs_value, num_digits).end; + }); + } + case presentation_type::hex_lower: + case presentation_type::hex_upper: { + bool upper = specs.type == presentation_type::hex_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); + int num_digits = count_digits<4>(abs_value); + return write_int( + out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) { + return format_uint<4, Char>(it, abs_value, num_digits, upper); + }); + } + case presentation_type::bin_lower: + case presentation_type::bin_upper: { + bool upper = specs.type == presentation_type::bin_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); + int num_digits = count_digits<1>(abs_value); + return write_int(out, num_digits, prefix, specs, + [=](reserve_iterator<OutputIt> it) { + return format_uint<1, Char>(it, abs_value, num_digits); + }); + } + case presentation_type::oct: { + int num_digits = count_digits<3>(abs_value); + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + if (specs.alt && specs.precision <= num_digits && abs_value != 0) + prefix_append(prefix, '0'); + return write_int(out, num_digits, prefix, specs, + [=](reserve_iterator<OutputIt> it) { + return format_uint<3, Char>(it, abs_value, num_digits); + }); + } + case presentation_type::chr: + return write_char(out, static_cast<Char>(abs_value), specs); + default: + throw_format_error("invalid type specifier"); + } + return out; +} +template <typename Char, typename OutputIt, typename T> +FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline( + OutputIt out, write_int_arg<T> arg, const basic_format_specs<Char>& specs, + locale_ref loc) -> OutputIt { + return write_int(out, arg, specs, loc); +} +template <typename Char, typename OutputIt, typename T, + FMT_ENABLE_IF(is_integral<T>::value && + !std::is_same<T, bool>::value && + std::is_same<OutputIt, buffer_appender<Char>>::value)> +FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, + const basic_format_specs<Char>& specs, + locale_ref loc) -> OutputIt { + return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs, + loc); +} +// An inlined version of write used in format string compilation. +template <typename Char, typename OutputIt, typename T, + FMT_ENABLE_IF(is_integral<T>::value && + !std::is_same<T, bool>::value && + !std::is_same<OutputIt, buffer_appender<Char>>::value)> +FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, + const basic_format_specs<Char>& specs, + locale_ref loc) -> OutputIt { + return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); +} + +// 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; + FMT_UNCHECKED_ITERATOR(counting_iterator); + + struct value_type { + template <typename T> FMT_CONSTEXPR void operator=(const T&) {} + }; + + FMT_CONSTEXPR counting_iterator() : count_(0) {} + + FMT_CONSTEXPR size_t count() const { return count_; } + + FMT_CONSTEXPR counting_iterator& operator++() { + ++count_; + return *this; + } + FMT_CONSTEXPR counting_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } + + FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it, + difference_type n) { + it.count_ += static_cast<size_t>(n); + return it; + } + + FMT_CONSTEXPR value_type operator*() const { return {}; } +}; + +template <typename Char, typename OutputIt> +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s, + const basic_format_specs<Char>& specs) -> OutputIt { + 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)); + bool is_debug = specs.type == presentation_type::debug; + size_t width = 0; + if (specs.width != 0) { + if (is_debug) + width = write_escaped_string(counting_iterator{}, s).count(); + else + width = compute_width(basic_string_view<Char>(data, size)); + } + return write_padded(out, specs, size, width, + [=](reserve_iterator<OutputIt> it) { + if (is_debug) return write_escaped_string(it, s); + return copy_str<Char>(data, data + size, it); + }); +} +template <typename Char, typename OutputIt> +FMT_CONSTEXPR auto write(OutputIt out, + basic_string_view<type_identity_t<Char>> s, + const basic_format_specs<Char>& specs, locale_ref) + -> OutputIt { + check_string_type_spec(specs.type); + return write(out, s, specs); +} +template <typename Char, typename OutputIt> +FMT_CONSTEXPR auto write(OutputIt out, const Char* s, + const basic_format_specs<Char>& specs, locale_ref) + -> OutputIt { + return check_cstring_type_spec(specs.type) + ? write(out, basic_string_view<Char>(s), specs, {}) + : write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs); +} + +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)> +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { + 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 size = (negative ? 1 : 0) + static_cast<size_t>(num_digits); + auto it = reserve(out, size); + if (auto ptr = to_pointer<Char>(it, size)) { + if (negative) *ptr++ = static_cast<Char>('-'); + format_decimal<Char>(ptr, abs_value, num_digits); + return out; + } + 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> +FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, + basic_format_specs<Char> specs, + const float_specs& fspecs) -> OutputIt { + auto str = + isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf"); + constexpr size_t str_size = 3; + auto sign = fspecs.sign; + auto size = str_size + (sign ? 1 : 0); + // Replace '0'-padding with space for non-finite values. + const bool is_zero_fill = + specs.fill.size() == 1 && *specs.fill.data() == static_cast<Char>('0'); + if (is_zero_fill) specs.fill[0] = static_cast<Char>(' '); + return write_padded(out, specs, size, [=](reserve_iterator<OutputIt> it) { + if (sign) *it++ = detail::sign<Char>(sign); + return copy_str<Char>(str, str + str_size, it); + }); +} + +// A decimal floating-point number significand * pow(10, exp). +struct big_decimal_fp { + const char* significand; + int significand_size; + int exponent; +}; + +constexpr auto get_significand_size(const big_decimal_fp& f) -> int { + return f.significand_size; +} +template <typename T> +inline auto get_significand_size(const dragonbox::decimal_fp<T>& f) -> int { + return count_digits(f.significand); +} + +template <typename Char, typename OutputIt> +constexpr auto write_significand(OutputIt out, const char* significand, + int significand_size) -> OutputIt { + return copy_str<Char>(significand, significand + significand_size, out); +} +template <typename Char, typename OutputIt, typename UInt> +inline auto write_significand(OutputIt out, UInt significand, + int significand_size) -> OutputIt { + return format_decimal<Char>(out, significand, significand_size).end; +} +template <typename Char, typename OutputIt, typename T, typename Grouping> +FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, + int significand_size, int exponent, + const Grouping& grouping) -> OutputIt { + if (!grouping.separator()) { + out = write_significand<Char>(out, significand, significand_size); + return detail::fill_n(out, exponent, static_cast<Char>('0')); + } + auto buffer = memory_buffer(); + write_significand<char>(appender(buffer), significand, significand_size); + detail::fill_n(appender(buffer), exponent, '0'); + return grouping.apply(out, string_view(buffer.data(), buffer.size())); +} + +template <typename Char, typename UInt, + FMT_ENABLE_IF(std::is_integral<UInt>::value)> +inline auto write_significand(Char* out, UInt significand, int significand_size, + int integral_size, Char decimal_point) -> Char* { + if (!decimal_point) + return format_decimal(out, significand, significand_size).end; + out += significand_size + 1; + Char* end = out; + int floating_size = significand_size - integral_size; + for (int i = floating_size / 2; i > 0; --i) { + out -= 2; + copy2(out, digits2(static_cast<std::size_t>(significand % 100))); + significand /= 100; + } + if (floating_size % 2 != 0) { + *--out = static_cast<Char>('0' + significand % 10); + significand /= 10; + } + *--out = decimal_point; + format_decimal(out - integral_size, significand, integral_size); + return end; +} + +template <typename OutputIt, typename UInt, typename Char, + FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<OutputIt>>::value)> +inline auto write_significand(OutputIt out, UInt significand, + int significand_size, int integral_size, + Char decimal_point) -> OutputIt { + // Buffer is large enough to hold digits (digits10 + 1) and a decimal point. + Char buffer[digits10<UInt>() + 2]; + auto end = write_significand(buffer, significand, significand_size, + integral_size, decimal_point); + return detail::copy_str_noinline<Char>(buffer, end, out); +} + +template <typename OutputIt, typename Char> +FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand, + int significand_size, int integral_size, + Char decimal_point) -> OutputIt { + out = detail::copy_str_noinline<Char>(significand, + significand + integral_size, out); + if (!decimal_point) return out; + *out++ = decimal_point; + return detail::copy_str_noinline<Char>(significand + integral_size, + significand + significand_size, out); +} + +template <typename OutputIt, typename Char, typename T, typename Grouping> +FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, + int significand_size, int integral_size, + Char decimal_point, + const Grouping& grouping) -> OutputIt { + if (!grouping.separator()) { + return write_significand(out, significand, significand_size, integral_size, + decimal_point); + } + auto buffer = basic_memory_buffer<Char>(); + write_significand(buffer_appender<Char>(buffer), significand, + significand_size, integral_size, decimal_point); + grouping.apply( + out, basic_string_view<Char>(buffer.data(), to_unsigned(integral_size))); + return detail::copy_str_noinline<Char>(buffer.data() + integral_size, + buffer.end(), out); +} + +template <typename OutputIt, typename DecimalFP, typename Char, + typename Grouping = digit_grouping<Char>> +FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, + const basic_format_specs<Char>& specs, + float_specs fspecs, locale_ref loc) + -> OutputIt { + auto significand = f.significand; + int significand_size = get_significand_size(f); + const Char zero = static_cast<Char>('0'); + auto sign = fspecs.sign; + size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); + using iterator = reserve_iterator<OutputIt>; + + Char decimal_point = + fspecs.locale ? detail::decimal_point<Char>(loc) : static_cast<Char>('.'); + + int output_exp = f.exponent + significand_size - 1; + auto use_exp_format = [=]() { + if (fspecs.format == float_format::exp) return true; + if (fspecs.format != float_format::general) return false; + // Use the fixed notation if the exponent is in [exp_lower, exp_upper), + // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. + const int exp_lower = -4, exp_upper = 16; + return output_exp < exp_lower || + output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper); + }; + if (use_exp_format()) { + int num_zeros = 0; + if (fspecs.showpoint) { + num_zeros = fspecs.precision - significand_size; + if (num_zeros < 0) num_zeros = 0; + size += to_unsigned(num_zeros); + } else if (significand_size == 1) { + decimal_point = Char(); + } + auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp; + int exp_digits = 2; + if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3; + + size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits); + char exp_char = fspecs.upper ? 'E' : 'e'; + auto write = [=](iterator it) { + if (sign) *it++ = detail::sign<Char>(sign); + // Insert a decimal point after the first digit and add an exponent. + it = write_significand(it, significand, significand_size, 1, + decimal_point); + if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero); + *it++ = static_cast<Char>(exp_char); + return write_exponent<Char>(output_exp, it); + }; + return specs.width > 0 ? write_padded<align::right>(out, specs, size, write) + : base_iterator(out, write(reserve(out, size))); + } + + int exp = f.exponent + significand_size; + if (f.exponent >= 0) { + // 1234e5 -> 123400000[.0+] + size += to_unsigned(f.exponent); + int num_zeros = fspecs.precision - exp; + abort_fuzzing_if(num_zeros > 5000); + if (fspecs.showpoint) { + ++size; + if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1; + if (num_zeros > 0) size += to_unsigned(num_zeros); + } + auto grouping = Grouping(loc, fspecs.locale); + size += to_unsigned(grouping.count_separators(exp)); + return write_padded<align::right>(out, specs, size, [&](iterator it) { + if (sign) *it++ = detail::sign<Char>(sign); + it = write_significand<Char>(it, significand, significand_size, + f.exponent, grouping); + if (!fspecs.showpoint) return it; + *it++ = decimal_point; + return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; + }); + } else if (exp > 0) { + // 1234e-2 -> 12.34[0+] + int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0; + size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); + auto grouping = Grouping(loc, fspecs.locale); + size += to_unsigned(grouping.count_separators(significand_size)); + return write_padded<align::right>(out, specs, size, [&](iterator it) { + if (sign) *it++ = detail::sign<Char>(sign); + it = write_significand(it, significand, significand_size, exp, + decimal_point, grouping); + return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; + }); + } + // 1234e-6 -> 0.001234 + int num_zeros = -exp; + if (significand_size == 0 && fspecs.precision >= 0 && + fspecs.precision < num_zeros) { + num_zeros = fspecs.precision; + } + bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint; + size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros); + return write_padded<align::right>(out, specs, size, [&](iterator it) { + if (sign) *it++ = detail::sign<Char>(sign); + *it++ = zero; + if (!pointy) return it; + *it++ = decimal_point; + it = detail::fill_n(it, num_zeros, zero); + return write_significand<Char>(it, significand, significand_size); + }); +} + +template <typename Char> class fallback_digit_grouping { + public: + constexpr fallback_digit_grouping(locale_ref, bool) {} + + constexpr Char separator() const { return Char(); } + + constexpr int count_separators(int) const { return 0; } + + template <typename Out, typename C> + constexpr Out apply(Out out, basic_string_view<C>) const { + return out; + } +}; + +template <typename OutputIt, typename DecimalFP, typename Char> +FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, + const basic_format_specs<Char>& specs, + float_specs fspecs, locale_ref loc) + -> OutputIt { + if (is_constant_evaluated()) { + return do_write_float<OutputIt, DecimalFP, Char, + fallback_digit_grouping<Char>>(out, f, specs, fspecs, + loc); + } else { + return do_write_float(out, f, specs, fspecs, loc); + } +} + +template <typename T> constexpr bool isnan(T value) { + return !(value >= value); // std::isnan doesn't support __float128. +} + +template <typename T, typename Enable = void> +struct has_isfinite : std::false_type {}; + +template <typename T> +struct has_isfinite<T, enable_if_t<sizeof(std::isfinite(T())) != 0>> + : std::true_type {}; + +template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value&& + has_isfinite<T>::value)> +FMT_CONSTEXPR20 bool isfinite(T value) { + constexpr T inf = T(std::numeric_limits<double>::infinity()); + if (is_constant_evaluated()) + return !detail::isnan(value) && value != inf && value != -inf; + return std::isfinite(value); +} +template <typename T, FMT_ENABLE_IF(!has_isfinite<T>::value)> +FMT_CONSTEXPR bool isfinite(T value) { + T inf = T(std::numeric_limits<double>::infinity()); + // std::isfinite doesn't support __float128. + return !detail::isnan(value) && value != inf && value != -inf; +} + +template <typename T, FMT_ENABLE_IF(is_floating_point<T>::value)> +FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { + if (is_constant_evaluated()) { +#ifdef __cpp_if_constexpr + if constexpr (std::numeric_limits<double>::is_iec559) { + auto bits = detail::bit_cast<uint64_t>(static_cast<double>(value)); + return (bits >> (num_bits<uint64_t>() - 1)) != 0; + } +#endif + } + return std::signbit(static_cast<double>(value)); +} + +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. +FMT_CONSTEXPR 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. +}; +} + +struct gen_digits_handler { + char* buf; + int size; + int precision; + int exp10; + bool fixed; + + FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor, + uint64_t remainder, uint64_t error, + bool integral) { + FMT_ASSERT(remainder < divisor, ""); + buf[size++] = digit; + if (!integral && error >= remainder) return digits::error; + 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'; + if (fixed) + buf[size++] = '0'; + else + ++exp10; + } + return digits::done; + } +}; + +inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) { + // Adjust fixed precision by exponent because it is relative to decimal + // point. + if (exp10 > 0 && precision > max_value<int>() - exp10) + FMT_THROW(format_error("number is too big")); + precision += exp10; +} + +// 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). +FMT_INLINE FMT_CONSTEXPR20 auto grisu_gen_digits(fp value, uint64_t error, + int& exp, + gen_digits_handler& handler) + -> digits::result { + 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 = count_digits(integral); // kappa in Grisu. + // Non-fixed formats require at least one digit and no precision adjustment. + if (handler.fixed) { + adjust_precision(handler.precision, exp + handler.exp10); + // Check if precision is satisfied just by leading zeros, e.g. + // format("{:.2f}", 0.001) gives "0.00" without generating any digits. + if (handler.precision <= 0) { + if (handler.precision < 0) return digits::done; + // Divide by 10 to prevent overflow. + uint64_t divisor = data::power_of_10_64[exp - 1] << -one.e; + auto dir = get_round_direction(divisor, value.f / 10, error * 10); + if (dir == round_direction::unknown) return digits::error; + handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0'; + return digits::done; + } + } + // 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; + auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional; + auto result = handler.on_digit(static_cast<char>('0' + digit), + data::power_of_10_64[exp] << -one.e, + remainder, error, 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' + (fractional >> -one.e)); + fractional &= one.f - 1; + --exp; + auto result = handler.on_digit(digit, one.f, fractional, error, false); + if (result != digits::more) return result; + } +} + +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_; + + FMT_CONSTEXPR20 bigit operator[](int index) const { + return bigits_[to_unsigned(index)]; + } + FMT_CONSTEXPR20 bigit& operator[](int index) { + return bigits_[to_unsigned(index)]; + } + + static constexpr const int bigit_bits = num_bits<bigit>(); + + friend struct formatter<bigint>; + + FMT_CONSTEXPR20 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)); + } + + FMT_CONSTEXPR20 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. + FMT_CONSTEXPR20 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(); + } + + FMT_CONSTEXPR20 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); + } + + template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value || + std::is_same<UInt, uint128_t>::value)> + FMT_CONSTEXPR20 void multiply(UInt value) { + using half_uint = + conditional_t<std::is_same<UInt, uint128_t>::value, uint64_t, uint32_t>; + const int shift = num_bits<half_uint>() - bigit_bits; + const UInt lower = static_cast<half_uint>(value); + const UInt upper = value >> num_bits<half_uint>(); + UInt carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + UInt result = lower * bigits_[i] + static_cast<bigit>(carry); + carry = (upper * bigits_[i] << shift) + (result >> bigit_bits) + + (carry >> bigit_bits); + bigits_[i] = static_cast<bigit>(result); + } + while (carry != 0) { + bigits_.push_back(static_cast<bigit>(carry)); + carry >>= bigit_bits; + } + } + + template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value || + std::is_same<UInt, uint128_t>::value)> + FMT_CONSTEXPR20 void assign(UInt n) { + size_t num_bigits = 0; + do { + bigits_[num_bigits++] = static_cast<bigit>(n); + n >>= bigit_bits; + } while (n != 0); + bigits_.resize(num_bigits); + exp_ = 0; + } + + public: + FMT_CONSTEXPR20 bigint() : exp_(0) {} + explicit bigint(uint64_t n) { assign(n); } + + bigint(const bigint&) = delete; + void operator=(const bigint&) = delete; + + FMT_CONSTEXPR20 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_; + } + + template <typename Int> FMT_CONSTEXPR20 void operator=(Int n) { + FMT_ASSERT(n > 0, ""); + assign(uint64_or_128_t<Int>(n)); + } + + FMT_CONSTEXPR20 int num_bigits() const { + return static_cast<int>(bigits_.size()) + exp_; + } + + FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) { + FMT_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> FMT_CONSTEXPR20 bigint& operator*=(Int value) { + FMT_ASSERT(value > 0, ""); + multiply(uint32_or_64_or_128_t<Int>(value)); + return *this; + } + + friend FMT_CONSTEXPR20 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 FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2, + const bigint& rhs) { + auto minimum = [](int a, int b) { return a < b ? a : b; }; + auto maximum = [](int a, int b) { return a > b ? a : b; }; + int max_lhs_bigits = maximum(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 = minimum(minimum(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. + FMT_CONSTEXPR20 void assign_pow10(int exp) { + FMT_ASSERT(exp >= 0, ""); + if (exp == 0) return *this = 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. + *this = 5; + bitmask >>= 1; + while (bitmask != 0) { + square(); + if ((exp & bitmask) != 0) *this *= 5; + bitmask >>= 1; + } + *this <<= exp; // Multiply by pow(2, exp) by shifting. + } + + FMT_CONSTEXPR20 void square() { + int num_bigits = static_cast<int>(bigits_.size()); + int num_result_bigits = 2 * num_bigits; + basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_)); + bigits_.resize(to_unsigned(num_result_bigits)); + auto sum = uint128_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 >>= num_bits<bigit>(); // 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 >>= num_bits<bigit>(); + } + remove_leading_zeros(); + exp_ *= 2; + } + + // If this bigint has a bigger exponent than other, adds trailing zero to make + // exponents equal. This simplifies some operations such as subtraction. + FMT_CONSTEXPR20 void align(const bigint& other) { + int exp_difference = exp_ - other.exp_; + if (exp_difference <= 0) return; + int num_bigits = static_cast<int>(bigits_.size()); + 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; + } + + // Divides this bignum by divisor, assigning the remainder to this and + // returning the quotient. + FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) { + FMT_ASSERT(this != &divisor, ""); + if (compare(*this, divisor) < 0) return 0; + FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); + align(divisor); + int quotient = 0; + do { + subtract_aligned(divisor); + ++quotient; + } while (compare(*this, divisor) >= 0); + return quotient; + } +}; + +// format_dragon flags. +enum dragon { + predecessor_closer = 1, + fixup = 2, // Run fixup to correct exp10 which can be off by one. + fixed = 4, +}; + +// Formats a floating-point number using a variation of the Fixed-Precision +// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White: +// https://fmt.dev/papers/p372-steele.pdf. +FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value, + unsigned flags, int num_digits, + 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). + // 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. + bool is_predecessor_closer = (flags & dragon::predecessor_closer) != 0; + int shift = is_predecessor_closer ? 2 : 1; + if (value.e >= 0) { + numerator = value.f; + numerator <<= value.e + shift; + lower = 1; + lower <<= value.e; + if (is_predecessor_closer) { + upper_store = 1; + upper_store <<= value.e + 1; + upper = &upper_store; + } + denominator.assign_pow10(exp10); + denominator <<= shift; + } else if (exp10 < 0) { + numerator.assign_pow10(-exp10); + lower.assign(numerator); + if (is_predecessor_closer) { + upper_store.assign(numerator); + upper_store <<= 1; + upper = &upper_store; + } + numerator *= value.f; + numerator <<= shift; + denominator = 1; + denominator <<= shift - value.e; + } else { + numerator = value.f; + numerator <<= shift; + denominator.assign_pow10(exp10); + denominator <<= shift - value.e; + lower = 1; + if (is_predecessor_closer) { + upper_store = 1ULL << 1; + upper = &upper_store; + } + } + int even = static_cast<int>((value.f & 1) == 0); + if (!upper) upper = &lower; + if ((flags & dragon::fixup) != 0) { + if (add_compare(numerator, *upper, denominator) + even <= 0) { + --exp10; + numerator *= 10; + if (num_digits < 0) { + lower *= 10; + if (upper != &lower) *upper *= 10; + } + } + if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1); + } + // Invariant: value == (numerator / denominator) * pow(10, exp10). + if (num_digits < 0) { + // Generate the shortest representation. + 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.try_resize(to_unsigned(num_digits)); + exp10 -= num_digits - 1; + return; + } + numerator *= 10; + lower *= 10; + if (upper != &lower) *upper *= 10; + } + } + // Generate the given number of digits. + exp10 -= num_digits - 1; + if (num_digits == 0) { + denominator *= 10; + auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + buf.push_back(digit); + return; + } + buf.try_resize(to_unsigned(num_digits)); + for (int i = 0; i < num_digits - 1; ++i) { + int digit = numerator.divmod_assign(denominator); + buf[i] = static_cast<char>('0' + digit); + numerator *= 10; + } + int digit = numerator.divmod_assign(denominator); + auto result = add_compare(numerator, numerator, denominator); + if (result > 0 || (result == 0 && (digit % 2) != 0)) { + if (digit == 9) { + const auto overflow = '0' + 10; + buf[num_digits - 1] = overflow; + // Propagate the carry. + for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] == overflow) { + buf[0] = '1'; + ++exp10; + } + return; + } + ++digit; + } + buf[num_digits - 1] = static_cast<char>('0' + digit); +} + +template <typename Float> +FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, + buffer<char>& buf) -> int { + // float is passed as double to reduce the number of instantiations. + static_assert(!std::is_same<Float, float>::value, ""); + FMT_ASSERT(value >= 0, "value is negative"); + auto converted_value = convert_float(value); + + 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.try_resize(to_unsigned(precision)); + fill_n(buf.data(), precision, '0'); + return -precision; + } + + int exp = 0; + bool use_dragon = true; + unsigned dragon_flags = 0; + if (!is_fast_float<Float>()) { + const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10) + using info = dragonbox::float_info<decltype(converted_value)>; + const auto f = basic_fp<typename info::carrier_uint>(converted_value); + // Compute exp, an approximate power of 10, such that + // 10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1). + // This is based on log10(value) == log2(value) / log2(10) and approximation + // of log2(value) by e + num_fraction_bits idea from double-conversion. + exp = static_cast<int>( + std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10)); + dragon_flags = dragon::fixup; + } else if (!is_constant_evaluated() && precision < 0) { + // Use Dragonbox for the shortest format. + if (specs.binary32) { + auto dec = dragonbox::to_decimal(static_cast<float>(value)); + write<char>(buffer_appender<char>(buf), dec.significand); + return dec.exponent; + } + auto dec = dragonbox::to_decimal(static_cast<double>(value)); + write<char>(buffer_appender<char>(buf), dec.significand); + return dec.exponent; + } else { + // Use Grisu + Dragon4 for the given precision: + // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. + const int min_exp = -60; // alpha in Grisu. + int cached_exp10 = 0; // K in Grisu. + fp normalized = normalize(fp(converted_value)); + const auto cached_pow = get_cached_power( + min_exp - (normalized.e + fp::num_significand_bits), cached_exp10); + normalized = normalized * cached_pow; + gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; + if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error && + !is_constant_evaluated()) { + exp += handler.exp10; + buf.try_resize(to_unsigned(handler.size)); + use_dragon = false; + } else { + exp += handler.size - cached_exp10 - 1; + precision = handler.precision; + } + } + if (use_dragon) { + auto f = basic_fp<uint128_t>(); + bool is_predecessor_closer = specs.binary32 + ? f.assign(static_cast<float>(value)) + : f.assign(converted_value); + if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer; + if (fixed) dragon_flags |= dragon::fixed; + // Limit precision to the maximum possible number of significant digits in + // an IEEE754 double because we don't need to generate zeros. + const int max_double_digits = 767; + if (precision > max_double_digits) precision = max_double_digits; + format_dragon(f, dragon_flags, precision, buf, exp); + } + if (!fixed && !specs.showpoint) { + // Remove trailing zeros. + auto num_digits = buf.size(); + while (num_digits > 0 && buf[num_digits - 1] == '0') { + --num_digits; + ++exp; + } + buf.try_resize(num_digits); + } + return exp; +} + +template <typename Char, typename OutputIt, typename T, + FMT_ENABLE_IF(is_floating_point<T>::value)> +FMT_CONSTEXPR20 auto write(OutputIt out, T value, + basic_format_specs<Char> specs, locale_ref loc = {}) + -> OutputIt { + if (const_check(!is_supported_floating_point(value))) return out; + float_specs fspecs = parse_float_type_spec(specs); + fspecs.sign = specs.sign; + if (detail::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 (!detail::isfinite(value)) + return write_nonfinite(out, detail::isnan(value), specs, fspecs); + + if (specs.align == align::numeric && fspecs.sign) { + auto it = reserve(out, 1); + *it++ = detail::sign<Char>(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(detail::sign<char>(fspecs.sign)); + snprintf_float(convert_float(value), specs.precision, fspecs, buffer); + return write_bytes<align::right>(out, {buffer.data(), buffer.size()}, + specs); + } + int precision = specs.precision >= 0 || specs.type == presentation_type::none + ? specs.precision + : 6; + if (fspecs.format == float_format::exp) { + if (precision == max_value<int>()) + throw_format_error("number is too big"); + else + ++precision; + } else if (fspecs.format != float_format::fixed && precision == 0) { + precision = 1; + } + if (const_check(std::is_same<T, float>())) fspecs.binary32 = true; + int exp = format_float(convert_float(value), precision, fspecs, buffer); + fspecs.precision = precision; + auto f = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp}; + return write_float(out, f, specs, fspecs, loc); +} + +template <typename Char, typename OutputIt, typename T, + FMT_ENABLE_IF(is_fast_float<T>::value)> +FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { + if (is_constant_evaluated()) + return write(out, value, basic_format_specs<Char>()); + if (const_check(!is_supported_floating_point(value))) return out; + + auto fspecs = float_specs(); + if (detail::signbit(value)) { + fspecs.sign = sign::minus; + value = -value; + } + + constexpr auto specs = basic_format_specs<Char>(); + using floaty = conditional_t<std::is_same<T, long double>::value, double, T>; + using uint = typename dragonbox::float_info<floaty>::carrier_uint; + uint mask = exponent_mask<floaty>(); + if ((bit_cast<uint>(value) & mask) == mask) + return write_nonfinite(out, std::isnan(value), specs, fspecs); + + auto dec = dragonbox::to_decimal(static_cast<floaty>(value)); + return write_float(out, dec, specs, fspecs, {}); +} + +template <typename Char, typename OutputIt, typename T, + FMT_ENABLE_IF(is_floating_point<T>::value && + !is_fast_float<T>::value)> +inline auto write(OutputIt out, T value) -> OutputIt { + return write(out, value, basic_format_specs<Char>()); +} + +template <typename Char, typename OutputIt> +auto write(OutputIt out, monostate, basic_format_specs<Char> = {}, + locale_ref = {}) -> OutputIt { + FMT_ASSERT(false, ""); + return out; +} + +template <typename Char, typename OutputIt> +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> value) + -> OutputIt { + auto it = reserve(out, value.size()); + it = copy_str_noinline<Char>(value.begin(), value.end(), it); + return base_iterator(out, it); +} + +template <typename Char, typename OutputIt, typename T, + FMT_ENABLE_IF(is_string<T>::value)> +constexpr auto write(OutputIt out, const T& value) -> OutputIt { + return write<Char>(out, to_string_view(value)); +} + +// FMT_ENABLE_IF() condition separated to workaround an MSVC bug. +template < + typename Char, typename OutputIt, typename T, + bool check = + std::is_enum<T>::value && !std::is_same<T, Char>::value && + mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value != + type::custom_type, + FMT_ENABLE_IF(check)> +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { + return write<Char>(out, static_cast<underlying_t<T>>(value)); +} + +template <typename Char, typename OutputIt, typename T, + FMT_ENABLE_IF(std::is_same<T, bool>::value)> +FMT_CONSTEXPR auto write(OutputIt out, T value, + const basic_format_specs<Char>& specs = {}, + locale_ref = {}) -> OutputIt { + return specs.type != presentation_type::none && + specs.type != presentation_type::string + ? write(out, value ? 1 : 0, specs, {}) + : write_bytes(out, value ? "true" : "false", specs); +} + +template <typename Char, typename OutputIt> +FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { + auto it = reserve(out, 1); + *it++ = value; + return base_iterator(out, it); +} + +template <typename Char, typename OutputIt> +FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) + -> OutputIt { + if (!value) { + throw_format_error("string pointer is null"); + } else { + out = write(out, basic_string_view<Char>(value)); + } + return out; +} + +template <typename Char, typename OutputIt, typename T, + FMT_ENABLE_IF(std::is_same<T, void>::value)> +auto write(OutputIt out, const T* value, + const basic_format_specs<Char>& specs = {}, locale_ref = {}) + -> OutputIt { + check_pointer_type_spec(specs.type, error_handler()); + return write_ptr<Char>(out, bit_cast<uintptr_t>(value), &specs); +} + +// A write overload that handles implicit conversions. +template <typename Char, typename OutputIt, typename T, + typename Context = basic_format_context<OutputIt, Char>> +FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t< + std::is_class<T>::value && !is_string<T>::value && + !is_floating_point<T>::value && !std::is_same<T, Char>::value && + !std::is_same<const T&, + decltype(arg_mapper<Context>().map(value))>::value, + OutputIt> { + return write<Char>(out, arg_mapper<Context>().map(value)); +} + +template <typename Char, typename OutputIt, typename T, + typename Context = basic_format_context<OutputIt, Char>> +FMT_CONSTEXPR auto write(OutputIt out, const T& value) + -> enable_if_t<mapped_type_constant<T, Context>::value == type::custom_type, + OutputIt> { + using formatter_type = + conditional_t<has_formatter<T, Context>::value, + typename Context::template formatter_type<T>, + fallback_formatter<T, Char>>; + auto ctx = Context(out, {}, {}); + return formatter_type().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 Char> struct default_arg_formatter { + using iterator = buffer_appender<Char>; + using context = buffer_context<Char>; + + iterator out; + basic_format_args<context> args; + locale_ref loc; + + template <typename T> auto operator()(T value) -> iterator { + return write<Char>(out, value); + } + auto operator()(typename basic_format_arg<context>::handle h) -> iterator { + basic_format_parse_context<Char> parse_ctx({}); + context format_ctx(out, args, loc); + h.format(parse_ctx, format_ctx); + return format_ctx.out(); + } +}; + +template <typename Char> struct arg_formatter { + using iterator = buffer_appender<Char>; + using context = buffer_context<Char>; + + iterator out; + const basic_format_specs<Char>& specs; + locale_ref locale; + + template <typename T> + FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator { + return detail::write(out, value, specs, locale); + } + auto operator()(typename basic_format_arg<context>::handle) -> iterator { + // User-defined types are handled separately because they require access + // to the parse context. + return out; + } +}; + +template <typename Char> struct custom_formatter { + basic_format_parse_context<Char>& parse_ctx; + buffer_context<Char>& ctx; + + void operator()( + typename basic_format_arg<buffer_context<Char>>::handle h) const { + h.format(parse_ctx, ctx); + } + template <typename T> void operator()(T) const {} +}; + +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 auto operator()(T value) -> unsigned long long { + 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 auto operator()(T) -> unsigned long long { + 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 auto operator()(T value) -> unsigned long long { + 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 auto operator()(T) -> unsigned long long { + handler_.on_error("precision is not integer"); + return 0; + } + + private: + ErrorHandler& handler_; +}; + +template <template <typename> class Handler, typename FormatArg, + typename ErrorHandler> +FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg, ErrorHandler eh) -> int { + 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); +} + +template <typename Context, typename ID> +FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> + typename Context::format_arg { + auto arg = ctx.arg(id); + if (!arg) ctx.on_error("argument not found"); + return arg; +} + +// The standard format specifier handler with checking. +template <typename Char> class specs_handler : public specs_setter<Char> { + private: + basic_format_parse_context<Char>& parse_context_; + buffer_context<Char>& context_; + + // This is only needed for compatibility with gcc 4.4. + using format_arg = basic_format_arg<buffer_context<Char>>; + + FMT_CONSTEXPR auto get_arg(auto_id) -> format_arg { + return detail::get_arg(context_, parse_context_.next_arg_id()); + } + + FMT_CONSTEXPR auto get_arg(int arg_id) -> format_arg { + parse_context_.check_arg_id(arg_id); + return detail::get_arg(context_, arg_id); + } + + FMT_CONSTEXPR auto get_arg(basic_string_view<Char> arg_id) -> format_arg { + parse_context_.check_arg_id(arg_id); + return detail::get_arg(context_, arg_id); + } + + public: + FMT_CONSTEXPR specs_handler(basic_format_specs<Char>& specs, + basic_format_parse_context<Char>& parse_ctx, + buffer_context<Char>& ctx) + : specs_setter<Char>(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); } +}; + +template <template <typename> class Handler, typename Context> +FMT_CONSTEXPR 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; + } +} + +#if FMT_USE_USER_DEFINED_LITERALS +template <typename Char> struct udl_formatter { + basic_string_view<Char> str; + + template <typename... T> + auto operator()(T&&... args) const -> std::basic_string<Char> { + return vformat(str, fmt::make_format_args<buffer_context<Char>>(args...)); + } +}; + +# if FMT_USE_NONTYPE_TEMPLATE_ARGS +template <typename T, typename Char, size_t N, + fmt::detail_exported::fixed_string<Char, N> Str> +struct statically_named_arg : view { + static constexpr auto name = Str.data; + + const T& value; + statically_named_arg(const T& v) : value(v) {} +}; + +template <typename T, typename Char, size_t N, + fmt::detail_exported::fixed_string<Char, N> Str> +struct is_named_arg<statically_named_arg<T, Char, N, Str>> : std::true_type {}; + +template <typename T, typename Char, size_t N, + fmt::detail_exported::fixed_string<Char, N> Str> +struct is_statically_named_arg<statically_named_arg<T, Char, N, Str>> + : std::true_type {}; + +template <typename Char, size_t N, + fmt::detail_exported::fixed_string<Char, N> Str> +struct udl_arg { + template <typename T> auto operator=(T&& value) const { + return statically_named_arg<T, Char, N, Str>(std::forward<T>(value)); + } +}; +# else +template <typename Char> struct udl_arg { + const Char* str; + + template <typename T> auto operator=(T&& value) const -> named_arg<Char, T> { + return {str, std::forward<T>(value)}; + } +}; +# endif +#endif // FMT_USE_USER_DEFINED_LITERALS + +template <typename Locale, typename Char> +auto vformat(const Locale& loc, basic_string_view<Char> format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) + -> std::basic_string<Char> { + basic_memory_buffer<Char> buffer; + detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc)); + return {buffer.data(), buffer.size()}; +} + +using format_func = void (*)(detail::buffer<char>&, int, const char*); + +FMT_API void format_error_code(buffer<char>& out, int error_code, + string_view message) noexcept; + +FMT_API void report_error(format_func func, int error_code, + const char* message) noexcept; +FMT_END_DETAIL_NAMESPACE + +FMT_API auto vsystem_error(int error_code, string_view format_str, + format_args args) -> std::system_error; + +/** + \rst + Constructs :class:`std::system_error` with a message formatted with + ``fmt::format(fmt, args...)``. + *error_code* is a system error code as given by ``errno``. + + **Example**:: + + // This throws std::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... T> +auto system_error(int error_code, format_string<T...> fmt, T&&... args) + -> std::system_error { + return vsystem_error(error_code, fmt, fmt::make_format_args(args...)); +} + +/** + \rst + Formats an error message for an error returned by an operating system or a + language runtime, for example a file opening error, and writes it to *out*. + The format is the same as the one used by ``std::system_error(ec, message)`` + where ``ec`` is ``std::error_code(error_code, std::generic_category()})``. + It is implementation-defined but normally looks like: + + .. 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``. + \endrst + */ +FMT_API void format_system_error(detail::buffer<char>& out, int error_code, + const char* message) 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, const char* message) 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> auto format_unsigned(UInt value) -> char* { + 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> auto format_signed(Int value) -> char* { + 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. */ + auto size() const -> size_t { + return detail::to_unsigned(buffer_ - str_ + buffer_size - 1); + } + + /** + Returns a pointer to the output buffer content. No terminating null + character is appended. + */ + auto data() const -> const char* { return str_; } + + /** + Returns a pointer to the output buffer content with terminating null + character appended. + */ + auto c_str() const -> const char* { + buffer_[buffer_size - 1] = '\0'; + return str_; + } + + /** + \rst + Returns the content of the output buffer as an ``std::string``. + \endrst + */ + auto str() const -> std::string { return std::string(str_, size()); } +}; + +template <typename T, typename Char> +template <typename FormatContext> +FMT_CONSTEXPR FMT_INLINE auto +formatter<T, Char, + enable_if_t<detail::type_constant<T, Char>::value != + detail::type::custom_type>>::format(const T& val, + FormatContext& ctx) + const -> decltype(ctx.out()) { + if (specs_.width_ref.kind != detail::arg_id_kind::none || + specs_.precision_ref.kind != detail::arg_id_kind::none) { + auto specs = specs_; + 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<Char>(ctx.out(), val, specs, ctx.locale()); + } + return detail::write<Char>(ctx.out(), val, specs_, ctx.locale()); +} + +template <typename Char> +struct formatter<void*, Char> : formatter<const void*, Char> { + template <typename FormatContext> + auto format(void* val, FormatContext& ctx) const -> 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> + FMT_CONSTEXPR auto format(const Char* val, FormatContext& ctx) const + -> 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<> { +// auto format(const variant& v, format_context& ctx) { +// return visit([&](const auto& val) { +// return dynamic_formatter<>::format(val, ctx); +// }, v); +// } +// }; +template <typename Char = char> class dynamic_formatter { + private: + detail::dynamic_format_specs<Char> specs_; + const Char* format_str_; + + struct null_handler : detail::error_handler { + void on_align(align_t) {} + void on_sign(sign_t) {} + void on_hash() {} + }; + + 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); + } + + public: + template <typename ParseContext> + FMT_CONSTEXPR 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 detail::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); + if (specs_.sign != sign::none) checker.on_sign(specs_.sign); + if (specs_.alt) checker.on_hash(); + if (specs_.precision >= 0) checker.end_precision(); + return detail::write<Char>(ctx.out(), val, specs_, ctx.locale()); + } +}; + +/** + \rst + Converts ``p`` to ``const void*`` for pointer formatting. + + **Example**:: + + auto s = fmt::format("{}", fmt::ptr(p)); + \endrst + */ +template <typename T> auto ptr(T p) -> const void* { + static_assert(std::is_pointer<T>::value, ""); + return detail::bit_cast<const void*>(p); +} +template <typename T> auto ptr(const std::unique_ptr<T>& p) -> const void* { + return p.get(); +} +template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* { + return p.get(); +} + +/** + \rst + Converts ``e`` to the underlying type. + + **Example**:: + + enum class color { red, green, blue }; + auto s = fmt::format("{}", fmt::underlying(color::red)); + \endrst + */ +template <typename Enum> +constexpr auto underlying(Enum e) noexcept -> underlying_t<Enum> { + return static_cast<underlying_t<Enum>>(e); +} + +namespace enums { +template <typename Enum, FMT_ENABLE_IF(std::is_enum<Enum>::value)> +constexpr auto format_as(Enum e) noexcept -> underlying_t<Enum> { + return static_cast<underlying_t<Enum>>(e); +} +} // namespace enums + +class bytes { + private: + string_view data_; + friend struct formatter<bytes>; + + public: + explicit bytes(string_view data) : data_(data) {} +}; + +template <> struct formatter<bytes> { + private: + detail::dynamic_format_specs<char> specs_; + + public: + 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_); + } +}; + +// group_digits_view is not derived from view because it copies the argument. +template <typename T> struct group_digits_view { T value; }; + +/** + \rst + Returns a view that formats an integer value using ',' as a locale-independent + thousands separator. + + **Example**:: + + fmt::print("{}", fmt::group_digits(12345)); + // Output: "12,345" + \endrst + */ +template <typename T> auto group_digits(T value) -> group_digits_view<T> { + return {value}; +} + +template <typename T> struct formatter<group_digits_view<T>> : formatter<T> { + private: + detail::dynamic_format_specs<char> specs_; + + public: + 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::int_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(group_digits_view<T> t, 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_int_localized( + ctx.out(), static_cast<detail::uint64_or_128_t<T>>(t.value), 0, specs_, + detail::digit_grouping<char>({"\3", ','})); + } +}; + +template <typename It, typename Sentinel, typename Char = char> +struct join_view : detail::view { + It begin; + Sentinel end; + basic_string_view<Char> sep; + + join_view(It b, Sentinel e, basic_string_view<Char> s) + : begin(b), end(e), sep(s) {} +}; + +template <typename It, typename Sentinel, typename Char> +struct formatter<join_view<It, Sentinel, Char>, Char> { + private: + using value_type = +#ifdef __cpp_lib_ranges + std::iter_value_t<It>; +#else + typename std::iterator_traits<It>::value_type; +#endif + using context = buffer_context<Char>; + using mapper = detail::arg_mapper<context>; + + template <typename T, FMT_ENABLE_IF(has_formatter<T, context>::value)> + static auto map(const T& value) -> const T& { + return value; + } + template <typename T, FMT_ENABLE_IF(!has_formatter<T, context>::value)> + static auto map(const T& value) -> decltype(mapper().map(value)) { + return mapper().map(value); + } + + using formatter_type = + conditional_t<is_formattable<value_type, Char>::value, + formatter<remove_cvref_t<decltype(map( + std::declval<const value_type&>()))>, + Char>, + detail::fallback_formatter<value_type, Char>>; + + formatter_type value_formatter_; + + public: + template <typename ParseContext> + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return value_formatter_.parse(ctx); + } + + template <typename FormatContext> + auto format(const join_view<It, Sentinel, Char>& value, + FormatContext& ctx) const -> decltype(ctx.out()) { + auto it = value.begin; + auto out = ctx.out(); + if (it != value.end) { + out = value_formatter_.format(map(*it), ctx); + ++it; + while (it != value.end) { + out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out); + ctx.advance_to(out); + out = value_formatter_.format(map(*it), ctx); + ++it; + } + } + return out; + } +}; + +/** + Returns a view that formats the iterator range `[begin, end)` with elements + separated by `sep`. + */ +template <typename It, typename Sentinel> +auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> { + return {begin, end, sep}; +} + +/** + \rst + Returns a view 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> +auto join(Range&& range, string_view sep) + -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>> { + 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 auto to_string(const T& value) -> std::string { + auto result = std::string(); + detail::write<char>(std::back_inserter(result), value); + return result; +} + +template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> +FMT_NODISCARD inline auto to_string(T value) -> std::string { + // 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 ? static_cast<unsigned>(max_size) : 5]; + char* begin = buffer; + return std::string(begin, detail::write<char>(begin, value)); +} + +template <typename Char, size_t SIZE> +FMT_NODISCARD auto to_string(const basic_memory_buffer<Char, SIZE>& buf) + -> std::basic_string<Char> { + auto size = buf.size(); + detail::assume(size < std::basic_string<Char>().max_size()); + return std::basic_string<Char>(buf.data(), size); +} + +FMT_BEGIN_DETAIL_NAMESPACE + +template <typename Char> +void vformat_to( + buffer<Char>& buf, basic_string_view<Char> fmt, + basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args, + locale_ref loc) { + // workaround for msvc bug regarding name-lookup in module + // link names into function scope + using detail::arg_formatter; + using detail::buffer_appender; + using detail::custom_formatter; + using detail::default_arg_formatter; + using detail::get_arg; + using detail::locale_ref; + using detail::parse_format_specs; + using detail::specs_checker; + using detail::specs_handler; + using detail::to_unsigned; + using detail::type; + using detail::write; + auto out = buffer_appender<Char>(buf); + if (fmt.size() == 2 && equal2(fmt.data(), "{}")) { + auto arg = args.get(0); + if (!arg) error_handler().on_error("argument not found"); + visit_format_arg(default_arg_formatter<Char>{out, args, loc}, arg); + return; + } + + struct format_handler : error_handler { + basic_format_parse_context<Char> parse_context; + buffer_context<Char> context; + + format_handler(buffer_appender<Char> p_out, basic_string_view<Char> str, + basic_format_args<buffer_context<Char>> p_args, + locale_ref p_loc) + : parse_context(str), context(p_out, p_args, p_loc) {} + + void on_text(const Char* begin, const Char* end) { + auto text = basic_string_view<Char>(begin, to_unsigned(end - begin)); + context.advance_to(write<Char>(context.out(), text)); + } + + FMT_CONSTEXPR auto on_arg_id() -> int { + return parse_context.next_arg_id(); + } + FMT_CONSTEXPR auto on_arg_id(int id) -> int { + return parse_context.check_arg_id(id), id; + } + FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int { + 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<Char>{context.out(), context.args(), + context.locale()}, + arg)); + } + + auto on_format_specs(int id, const Char* begin, const Char* end) + -> const Char* { + auto arg = get_arg(context, id); + if (arg.type() == type::custom_type) { + parse_context.advance_to(parse_context.begin() + + (begin - &*parse_context.begin())); + visit_format_arg(custom_formatter<Char>{parse_context, context}, arg); + return parse_context.begin(); + } + auto specs = basic_format_specs<Char>(); + specs_checker<specs_handler<Char>> handler( + specs_handler<Char>(specs, parse_context, context), arg.type()); + begin = parse_format_specs(begin, end, handler); + if (begin == end || *begin != '}') + on_error("missing '}' in format string"); + auto f = arg_formatter<Char>{context.out(), specs, context.locale()}; + context.advance_to(visit_format_arg(f, arg)); + return begin; + } + }; + detail::parse_format_string<false>(fmt, format_handler(out, fmt, args, loc)); +} + +#ifndef FMT_HEADER_ONLY +extern template FMT_API auto thousands_sep_impl<char>(locale_ref) + -> thousands_sep_result<char>; +extern template FMT_API auto thousands_sep_impl<wchar_t>(locale_ref) + -> thousands_sep_result<wchar_t>; +extern template FMT_API auto decimal_point_impl(locale_ref) -> char; +extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; +#endif // FMT_HEADER_ONLY + +FMT_END_DETAIL_NAMESPACE + +#if FMT_USE_USER_DEFINED_LITERALS +inline namespace literals { +/** + \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 + */ +# if FMT_USE_NONTYPE_TEMPLATE_ARGS +template <detail_exported::fixed_string Str> constexpr auto operator""_a() { + using char_t = remove_cvref_t<decltype(Str.data[0])>; + return detail::udl_arg<char_t, sizeof(Str.data) / sizeof(char_t), Str>(); +} +# else +constexpr auto operator"" _a(const char* s, size_t) -> detail::udl_arg<char> { + return {s}; +} +# endif +} // namespace literals +#endif // FMT_USE_USER_DEFINED_LITERALS + +template <typename Locale, FMT_ENABLE_IF(detail::is_locale<Locale>::value)> +inline auto vformat(const Locale& loc, string_view fmt, format_args args) + -> std::string { + return detail::vformat(loc, fmt, args); +} + +template <typename Locale, typename... T, + FMT_ENABLE_IF(detail::is_locale<Locale>::value)> +inline auto format(const Locale& loc, format_string<T...> fmt, T&&... args) + -> std::string { + return vformat(loc, string_view(fmt), fmt::make_format_args(args...)); +} + +template <typename OutputIt, typename Locale, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&& + detail::is_locale<Locale>::value)> +auto vformat_to(OutputIt out, const Locale& loc, string_view fmt, + format_args args) -> OutputIt { + using detail::get_buffer; + auto&& buf = get_buffer<char>(out); + detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); + return detail::get_iterator(buf); +} + +template <typename OutputIt, typename Locale, typename... T, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&& + detail::is_locale<Locale>::value)> +FMT_INLINE auto format_to(OutputIt out, const Locale& loc, + format_string<T...> fmt, T&&... args) -> OutputIt { + return vformat_to(out, loc, fmt, fmt::make_format_args(args...)); +} + +FMT_MODULE_EXPORT_END +FMT_END_NAMESPACE + +#ifdef FMT_HEADER_ONLY +# define FMT_FUNC inline +# include "format-inl.h" +#else +# define FMT_FUNC +#endif + +#endif // FMT_FORMAT_H_ diff --git a/src/fmt/include/fmt/os.h b/src/fmt/include/fmt/os.h new file mode 100644 index 000000000..d82be1125 --- /dev/null +++ b/src/fmt/include/fmt/os.h @@ -0,0 +1,478 @@ +// Formatting library for C++ - optional OS-specific functionality +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_OS_H_ +#define FMT_OS_H_ + +#include <cerrno> +#include <cstddef> +#include <cstdio> +#include <system_error> // std::system_error + +#if defined __APPLE__ || defined(__FreeBSD__) +# include <xlocale.h> // for LC_NUMERIC_MASK on OS X +#endif + +#include "format.h" + +#ifndef FMT_USE_FCNTL +// UWP doesn't provide _pipe. +# if FMT_HAS_INCLUDE("winapifamily.h") +# include <winapifamily.h> +# endif +# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \ + defined(__linux__)) && \ + (!defined(WINAPI_FAMILY) || \ + (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) +# include <fcntl.h> // for O_RDONLY +# define FMT_USE_FCNTL 1 +# else +# define FMT_USE_FCNTL 0 +# endif +#endif + +#ifndef FMT_POSIX +# if defined(_WIN32) && !defined(__MINGW32__) +// Fix warnings about deprecated symbols. +# define FMT_POSIX(call) _##call +# else +# define FMT_POSIX(call) call +# endif +#endif + +// Calls to system functions are wrapped in FMT_SYSTEM for testability. +#ifdef FMT_SYSTEM +# define FMT_POSIX_CALL(call) FMT_SYSTEM(call) +#else +# define FMT_SYSTEM(call) ::call +# ifdef _WIN32 +// Fix warnings about deprecated symbols. +# define FMT_POSIX_CALL(call) ::_##call +# else +# define FMT_POSIX_CALL(call) ::call +# endif +#endif + +// Retries the expression while it evaluates to error_result and errno +// equals to EINTR. +#ifndef _WIN32 +# define FMT_RETRY_VAL(result, expression, error_result) \ + do { \ + (result) = (expression); \ + } while ((result) == (error_result) && errno == EINTR) +#else +# define FMT_RETRY_VAL(result, expression, error_result) result = (expression) +#endif + +#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) + +FMT_BEGIN_NAMESPACE +FMT_MODULE_EXPORT_BEGIN + +/** + \rst + A reference to a null-terminated string. It can be constructed from a C + string or ``std::string``. + + You can use one of the following type aliases for common character types: + + +---------------+-----------------------------+ + | Type | Definition | + +===============+=============================+ + | cstring_view | basic_cstring_view<char> | + +---------------+-----------------------------+ + | wcstring_view | basic_cstring_view<wchar_t> | + +---------------+-----------------------------+ + + This class is most useful as a parameter type to allow passing + different types of strings to a function, for example:: + + template <typename... Args> + std::string format(cstring_view format_str, const Args & ... args); + + format("{}", 42); + format(std::string("{}"), 42); + \endrst + */ +template <typename Char> class basic_cstring_view { + private: + const Char* data_; + + public: + /** Constructs a string reference object from a C string. */ + basic_cstring_view(const Char* s) : data_(s) {} + + /** + \rst + Constructs a string reference from an ``std::string`` object. + \endrst + */ + basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {} + + /** Returns the pointer to a C string. */ + const Char* c_str() const { return data_; } +}; + +using cstring_view = basic_cstring_view<char>; +using wcstring_view = basic_cstring_view<wchar_t>; + +template <typename Char> struct formatter<std::error_code, Char> { + template <typename ParseContext> + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template <typename FormatContext> + FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + out = detail::write_bytes(out, ec.category().name(), + basic_format_specs<Char>()); + out = detail::write<Char>(out, Char(':')); + out = detail::write<Char>(out, ec.value()); + return out; + } +}; + +#ifdef _WIN32 +FMT_API const std::error_category& system_category() noexcept; + +FMT_BEGIN_DETAIL_NAMESPACE +// A converter from UTF-16 to UTF-8. +// It is only provided for Windows since other systems support UTF-8 natively. +class utf16_to_utf8 { + private: + memory_buffer buffer_; + + public: + utf16_to_utf8() {} + FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s); + operator string_view() const { return string_view(&buffer_[0], size()); } + size_t size() const { return buffer_.size() - 1; } + const char* c_str() const { return &buffer_[0]; } + std::string str() const { return std::string(&buffer_[0], size()); } + + // Performs conversion returning a system error code instead of + // throwing exception on conversion error. This method may still throw + // in case of memory allocation error. + FMT_API int convert(basic_string_view<wchar_t> s); +}; + +FMT_API void format_windows_error(buffer<char>& out, int error_code, + const char* message) noexcept; +FMT_END_DETAIL_NAMESPACE + +FMT_API std::system_error vwindows_error(int error_code, string_view format_str, + format_args args); + +/** + \rst + Constructs a :class:`std::system_error` object with the description + of the form + + .. parsed-literal:: + *<message>*: *<system-message>* + + where *<message>* is the formatted message and *<system-message>* is the + system message corresponding to the error code. + *error_code* is a Windows error code as given by ``GetLastError``. + If *error_code* is not a valid error code such as -1, the system message + will look like "error -1". + + **Example**:: + + // This throws a system_error with the description + // cannot open file 'madeup': The system cannot find the file specified. + // or similar (system message may vary). + const char *filename = "madeup"; + LPOFSTRUCT of = LPOFSTRUCT(); + HFILE file = OpenFile(filename, &of, OF_READ); + if (file == HFILE_ERROR) { + throw fmt::windows_error(GetLastError(), + "cannot open file '{}'", filename); + } + \endrst +*/ +template <typename... Args> +std::system_error windows_error(int error_code, string_view message, + const Args&... args) { + return vwindows_error(error_code, message, fmt::make_format_args(args...)); +} + +// Reports a Windows error without throwing an exception. +// Can be used to report errors from destructors. +FMT_API void report_windows_error(int error_code, const char* message) noexcept; +#else +inline const std::error_category& system_category() noexcept { + return std::system_category(); +} +#endif // _WIN32 + +// std::system is not available on some platforms such as iOS (#2248). +#ifdef __OSX__ +template <typename S, typename... Args, typename Char = char_t<S>> +void say(const S& format_str, Args&&... args) { + std::system(format("say \"{}\"", format(format_str, args...)).c_str()); +} +#endif + +// A buffered file. +class buffered_file { + private: + FILE* file_; + + friend class file; + + explicit buffered_file(FILE* f) : file_(f) {} + + public: + buffered_file(const buffered_file&) = delete; + void operator=(const buffered_file&) = delete; + + // Constructs a buffered_file object which doesn't represent any file. + buffered_file() noexcept : file_(nullptr) {} + + // Destroys the object closing the file it represents if any. + FMT_API ~buffered_file() noexcept; + + public: + buffered_file(buffered_file&& other) noexcept : file_(other.file_) { + other.file_ = nullptr; + } + + buffered_file& operator=(buffered_file&& other) { + close(); + file_ = other.file_; + other.file_ = nullptr; + return *this; + } + + // Opens a file. + FMT_API buffered_file(cstring_view filename, cstring_view mode); + + // Closes the file. + FMT_API void close(); + + // Returns the pointer to a FILE object representing this file. + FILE* get() const noexcept { return file_; } + + FMT_API int descriptor() const; + + void vprint(string_view format_str, format_args args) { + fmt::vprint(file_, format_str, args); + } + + template <typename... Args> + inline void print(string_view format_str, const Args&... args) { + vprint(format_str, fmt::make_format_args(args...)); + } +}; + +#if FMT_USE_FCNTL +// A file. Closed file is represented by a file object with descriptor -1. +// Methods that are not declared with noexcept may throw +// fmt::system_error in case of failure. Note that some errors such as +// closing the file multiple times will cause a crash on Windows rather +// than an exception. You can get standard behavior by overriding the +// invalid parameter handler with _set_invalid_parameter_handler. +class FMT_API file { + private: + int fd_; // File descriptor. + + // Constructs a file object with a given descriptor. + explicit file(int fd) : fd_(fd) {} + + public: + // Possible values for the oflag argument to the constructor. + enum { + RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. + WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. + RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing. + CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist. + APPEND = FMT_POSIX(O_APPEND), // Open in append mode. + TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file. + }; + + // Constructs a file object which doesn't represent any file. + file() noexcept : fd_(-1) {} + + // Opens a file and constructs a file object representing this file. + file(cstring_view path, int oflag); + + public: + file(const file&) = delete; + void operator=(const file&) = delete; + + file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } + + // Move assignment is not noexcept because close may throw. + file& operator=(file&& other) { + close(); + fd_ = other.fd_; + other.fd_ = -1; + return *this; + } + + // Destroys the object closing the file it represents if any. + ~file() noexcept; + + // Returns the file descriptor. + int descriptor() const noexcept { return fd_; } + + // Closes the file. + void close(); + + // Returns the file size. The size has signed type for consistency with + // stat::st_size. + long long size() const; + + // Attempts to read count bytes from the file into the specified buffer. + size_t read(void* buffer, size_t count); + + // Attempts to write count bytes from the specified buffer to the file. + size_t write(const void* buffer, size_t count); + + // Duplicates a file descriptor with the dup function and returns + // the duplicate as a file object. + static file dup(int fd); + + // Makes fd be the copy of this file descriptor, closing fd first if + // necessary. + void dup2(int fd); + + // Makes fd be the copy of this file descriptor, closing fd first if + // necessary. + void dup2(int fd, std::error_code& ec) noexcept; + + // Creates a pipe setting up read_end and write_end file objects for reading + // and writing respectively. + static void pipe(file& read_end, file& write_end); + + // Creates a buffered_file object associated with this file and detaches + // this file object from the file. + buffered_file fdopen(const char* mode); +}; + +// Returns the memory page size. +long getpagesize(); + +FMT_BEGIN_DETAIL_NAMESPACE + +struct buffer_size { + buffer_size() = default; + size_t value = 0; + buffer_size operator=(size_t val) const { + auto bs = buffer_size(); + bs.value = val; + return bs; + } +}; + +struct ostream_params { + int oflag = file::WRONLY | file::CREATE | file::TRUNC; + size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; + + ostream_params() {} + + template <typename... T> + ostream_params(T... params, int new_oflag) : ostream_params(params...) { + oflag = new_oflag; + } + + template <typename... T> + ostream_params(T... params, detail::buffer_size bs) + : ostream_params(params...) { + this->buffer_size = bs.value; + } + +// Intel has a bug that results in failure to deduce a constructor +// for empty parameter packs. +# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000 + ostream_params(int new_oflag) : oflag(new_oflag) {} + ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {} +# endif +}; + +FMT_END_DETAIL_NAMESPACE + +// Added {} below to work around default constructor error known to +// occur in Xcode versions 7.2.1 and 8.2.1. +constexpr detail::buffer_size buffer_size{}; + +/** A fast output stream which is not thread-safe. */ +class FMT_API ostream final : private detail::buffer<char> { + private: + file file_; + + void grow(size_t) override; + + ostream(cstring_view path, const detail::ostream_params& params) + : file_(path, params.oflag) { + set(new char[params.buffer_size], params.buffer_size); + } + + public: + ostream(ostream&& other) + : detail::buffer<char>(other.data(), other.size(), other.capacity()), + file_(std::move(other.file_)) { + other.clear(); + other.set(nullptr, 0); + } + ~ostream() { + flush(); + delete[] data(); + } + + void flush() { + if (size() == 0) return; + file_.write(data(), size()); + clear(); + } + + template <typename... T> + friend ostream output_file(cstring_view path, T... params); + + void close() { + flush(); + file_.close(); + } + + /** + Formats ``args`` according to specifications in ``fmt`` and writes the + output to the file. + */ + template <typename... T> void print(format_string<T...> fmt, T&&... args) { + vformat_to(detail::buffer_appender<char>(*this), fmt, + fmt::make_format_args(args...)); + } +}; + +/** + \rst + Opens a file for writing. Supported parameters passed in *params*: + + * ``<integer>``: Flags passed to `open + <https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_ + (``file::WRONLY | file::CREATE | file::TRUNC`` by default) + * ``buffer_size=<integer>``: Output buffer size + + **Example**:: + + auto out = fmt::output_file("guide.txt"); + out.print("Don't {}", "Panic"); + \endrst + */ +template <typename... T> +inline ostream output_file(cstring_view path, T... params) { + return {path, detail::ostream_params(params...)}; +} +#endif // FMT_USE_FCNTL + +FMT_MODULE_EXPORT_END +FMT_END_NAMESPACE + +#endif // FMT_OS_H_ diff --git a/src/fmt/include/fmt/ostream.h b/src/fmt/include/fmt/ostream.h new file mode 100644 index 000000000..c3cdd4a61 --- /dev/null +++ b/src/fmt/include/fmt/ostream.h @@ -0,0 +1,237 @@ +// Formatting library for C++ - std::ostream support +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_OSTREAM_H_ +#define FMT_OSTREAM_H_ + +#include <fstream> +#include <ostream> +#if defined(_WIN32) && defined(__GLIBCXX__) +# include <ext/stdio_filebuf.h> +# include <ext/stdio_sync_filebuf.h> +#elif defined(_WIN32) && defined(_LIBCPP_VERSION) +# include <__std_stream> +#endif + +#include "format.h" + +FMT_BEGIN_NAMESPACE + +template <typename OutputIt, typename Char> class basic_printf_context; + +namespace detail { + +// Checks if T has a user-defined operator<<. +template <typename T, typename Char, typename Enable = void> +class is_streamable { + private: + template <typename U> + static auto test(int) + -> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>() + << std::declval<U>()) != 0>; + + template <typename> static auto test(...) -> std::false_type; + + using result = decltype(test<T>(0)); + + public: + is_streamable() = default; + + static const bool value = result::value; +}; + +// Formatting of built-in types and arrays is intentionally disabled because +// it's handled by standard (non-ostream) formatters. +template <typename T, typename Char> +struct is_streamable< + T, Char, + enable_if_t< + std::is_arithmetic<T>::value || std::is_array<T>::value || + std::is_pointer<T>::value || std::is_same<T, char8_type>::value || + std::is_convertible<T, fmt::basic_string_view<Char>>::value || + std::is_same<T, std_string_view<Char>>::value || + (std::is_convertible<T, int>::value && !std::is_enum<T>::value)>> + : std::false_type {}; + +// Generate a unique explicit instantion in every translation unit using a tag +// type in an anonymous namespace. +namespace { +struct file_access_tag {}; +} // namespace +template <class Tag, class BufType, FILE* BufType::*FileMemberPtr> +class file_access { + friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } +}; + +#if FMT_MSC_VERSION +template class file_access<file_access_tag, std::filebuf, + &std::filebuf::_Myfile>; +auto get_file(std::filebuf&) -> FILE*; +#elif defined(_WIN32) && defined(_LIBCPP_VERSION) +template class file_access<file_access_tag, std::__stdoutbuf<char>, + &std::__stdoutbuf<char>::__file_>; +auto get_file(std::__stdoutbuf<char>&) -> FILE*; +#endif + +inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) { +#if FMT_MSC_VERSION + if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf())) + if (FILE* f = get_file(*buf)) return write_console(f, data); +#elif defined(_WIN32) && defined(__GLIBCXX__) + auto* rdbuf = os.rdbuf(); + FILE* c_file; + if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf)) + c_file = fbuf->file(); + else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf)) + c_file = fbuf->file(); + else + return false; + if (c_file) return write_console(c_file, data); +#elif defined(_WIN32) && defined(_LIBCPP_VERSION) + if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf())) + if (FILE* f = get_file(*buf)) return write_console(f, data); +#else + ignore_unused(os, data); +#endif + return false; +} +inline bool write_ostream_unicode(std::wostream&, + fmt::basic_string_view<wchar_t>) { + return false; +} + +// Write the content of buf to os. +// It is a separate function rather than a part of vprint to simplify testing. +template <typename Char> +void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { + const Char* buf_data = buf.data(); + using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; + unsigned_streamsize size = buf.size(); + unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>()); + do { + unsigned_streamsize n = size <= max_size ? size : max_size; + os.write(buf_data, static_cast<std::streamsize>(n)); + buf_data += n; + size -= n; + } while (size != 0); +} + +template <typename Char, typename T> +void format_value(buffer<Char>& buf, const T& value, + locale_ref loc = locale_ref()) { + auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf); + auto&& output = std::basic_ostream<Char>(&format_buf); +#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) + if (loc) output.imbue(loc.get<std::locale>()); +#endif + output << value; + output.exceptions(std::ios_base::failbit | std::ios_base::badbit); +} + +template <typename T> struct streamed_view { const T& value; }; + +} // namespace detail + +// Formats an object of type T that has an overloaded ostream operator<<. +template <typename Char> +struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> { + void set_debug_format() = delete; + + template <typename T, typename OutputIt> + auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const + -> OutputIt { + auto buffer = basic_memory_buffer<Char>(); + format_value(buffer, value, ctx.locale()); + return formatter<basic_string_view<Char>, Char>::format( + {buffer.data(), buffer.size()}, ctx); + } +}; + +using ostream_formatter = basic_ostream_formatter<char>; + +template <typename T, typename Char> +struct formatter<detail::streamed_view<T>, Char> + : basic_ostream_formatter<Char> { + template <typename OutputIt> + auto format(detail::streamed_view<T> view, + basic_format_context<OutputIt, Char>& ctx) const -> OutputIt { + return basic_ostream_formatter<Char>::format(view.value, ctx); + } +}; + +/** + \rst + Returns a view that formats `value` via an ostream ``operator<<``. + + **Example**:: + + fmt::print("Current thread id: {}\n", + fmt::streamed(std::this_thread::get_id())); + \endrst + */ +template <typename T> +auto streamed(const T& value) -> detail::streamed_view<T> { + return {value}; +} + +namespace detail { + +// Formats an object of type T that has an overloaded ostream operator<<. +template <typename T, typename Char> +struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> + : basic_ostream_formatter<Char> { + using basic_ostream_formatter<Char>::format; +}; + +inline void vprint_directly(std::ostream& os, string_view format_str, + format_args args) { + auto buffer = memory_buffer(); + detail::vformat_to(buffer, format_str, args); + detail::write_buffer(os, buffer); +} + +} // namespace detail + +FMT_MODULE_EXPORT template <typename Char> +void vprint(std::basic_ostream<Char>& os, + basic_string_view<type_identity_t<Char>> format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) { + auto buffer = basic_memory_buffer<Char>(); + detail::vformat_to(buffer, format_str, args); + if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return; + detail::write_buffer(os, buffer); +} + +/** + \rst + Prints formatted data to the stream *os*. + + **Example**:: + + fmt::print(cerr, "Don't {}!", "panic"); + \endrst + */ +FMT_MODULE_EXPORT template <typename... T> +void print(std::ostream& os, format_string<T...> fmt, T&&... args) { + const auto& vargs = fmt::make_format_args(args...); + if (detail::is_utf8()) + vprint(os, fmt, vargs); + else + detail::vprint_directly(os, fmt, vargs); +} + +FMT_MODULE_EXPORT +template <typename... Args> +void print(std::wostream& os, + basic_format_string<wchar_t, type_identity_t<Args>...> fmt, + Args&&... args) { + vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...)); +} + +FMT_END_NAMESPACE + +#endif // FMT_OSTREAM_H_ diff --git a/src/fmt/include/fmt/printf.h b/src/fmt/include/fmt/printf.h new file mode 100644 index 000000000..70a592dc2 --- /dev/null +++ b/src/fmt/include/fmt/printf.h @@ -0,0 +1,640 @@ +// Formatting library for C++ - legacy printf implementation +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_PRINTF_H_ +#define FMT_PRINTF_H_ + +#include <algorithm> // std::max +#include <limits> // std::numeric_limits + +#include "format.h" + +FMT_BEGIN_NAMESPACE +FMT_MODULE_EXPORT_BEGIN + +template <typename T> struct printf_formatter { printf_formatter() = delete; }; + +template <typename Char> +class basic_printf_parse_context : public basic_format_parse_context<Char> { + using basic_format_parse_context<Char>::basic_format_parse_context; +}; + +template <typename OutputIt, typename Char> class basic_printf_context { + private: + OutputIt out_; + basic_format_args<basic_printf_context> args_; + + public: + using char_type = Char; + using format_arg = basic_format_arg<basic_printf_context>; + using parse_context_type = basic_printf_parse_context<Char>; + template <typename T> using formatter_type = printf_formatter<T>; + + /** + \rst + Constructs a ``printf_context`` object. References to the arguments are + stored in the context object so make sure they have appropriate lifetimes. + \endrst + */ + basic_printf_context(OutputIt out, + basic_format_args<basic_printf_context> args) + : out_(out), args_(args) {} + + OutputIt out() { return out_; } + void advance_to(OutputIt it) { out_ = it; } + + detail::locale_ref locale() { return {}; } + + format_arg arg(int id) const { return args_.get(id); } + + FMT_CONSTEXPR void on_error(const char* message) { + detail::error_handler().on_error(message); + } +}; + +FMT_BEGIN_DETAIL_NAMESPACE + +// Checks if a value fits in int - used to avoid warnings about comparing +// signed and unsigned integers. +template <bool IsSigned> struct int_checker { + template <typename T> static bool fits_in_int(T value) { + unsigned max = max_value<int>(); + return value <= max; + } + static bool fits_in_int(bool) { return true; } +}; + +template <> struct int_checker<true> { + template <typename T> static bool fits_in_int(T value) { + return value >= (std::numeric_limits<int>::min)() && + value <= max_value<int>(); + } + static bool fits_in_int(int) { return true; } +}; + +class printf_precision_handler { + public: + template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> + int operator()(T value) { + if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) + FMT_THROW(format_error("number is too big")); + return (std::max)(static_cast<int>(value), 0); + } + + template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> + int operator()(T) { + FMT_THROW(format_error("precision is not integer")); + return 0; + } +}; + +// An argument visitor that returns true iff arg is a zero integer. +class is_zero_int { + public: + template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> + bool operator()(T value) { + return value == 0; + } + + template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> + bool operator()(T) { + return false; + } +}; + +template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {}; + +template <> struct make_unsigned_or_bool<bool> { using type = bool; }; + +template <typename T, typename Context> class arg_converter { + private: + using char_type = typename Context::char_type; + + basic_format_arg<Context>& arg_; + char_type type_; + + public: + arg_converter(basic_format_arg<Context>& arg, char_type type) + : arg_(arg), type_(type) {} + + void operator()(bool value) { + if (type_ != 's') operator()<bool>(value); + } + + template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)> + void operator()(U value) { + bool is_signed = type_ == 'd' || type_ == 'i'; + using target_type = conditional_t<std::is_same<T, void>::value, U, T>; + if (const_check(sizeof(target_type) <= sizeof(int))) { + // Extra casts are used to silence warnings. + if (is_signed) { + arg_ = detail::make_arg<Context>( + static_cast<int>(static_cast<target_type>(value))); + } else { + using unsigned_type = typename make_unsigned_or_bool<target_type>::type; + arg_ = detail::make_arg<Context>( + static_cast<unsigned>(static_cast<unsigned_type>(value))); + } + } else { + if (is_signed) { + // glibc's printf doesn't sign extend arguments of smaller types: + // std::printf("%lld", -42); // prints "4294967254" + // but we don't have to do the same because it's a UB. + arg_ = detail::make_arg<Context>(static_cast<long long>(value)); + } else { + arg_ = detail::make_arg<Context>( + static_cast<typename make_unsigned_or_bool<U>::type>(value)); + } + } + } + + template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)> + void operator()(U) {} // No conversion needed for non-integral types. +}; + +// Converts an integer argument to T for printf, if T is an integral type. +// If T is void, the argument is converted to corresponding signed or unsigned +// type depending on the type specifier: 'd' and 'i' - signed, other - +// unsigned). +template <typename T, typename Context, typename Char> +void convert_arg(basic_format_arg<Context>& arg, Char type) { + visit_format_arg(arg_converter<T, Context>(arg, type), arg); +} + +// Converts an integer argument to char for printf. +template <typename Context> class char_converter { + private: + basic_format_arg<Context>& arg_; + + public: + explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {} + + template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> + void operator()(T value) { + arg_ = detail::make_arg<Context>( + static_cast<typename Context::char_type>(value)); + } + + template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> + void operator()(T) {} // No conversion needed for non-integral types. +}; + +// An argument visitor that return a pointer to a C string if argument is a +// string or null otherwise. +template <typename Char> struct get_cstring { + template <typename T> const Char* operator()(T) { return nullptr; } + const Char* operator()(const Char* s) { return s; } +}; + +// Checks if an argument is a valid printf width specifier and sets +// left alignment if it is negative. +template <typename Char> class printf_width_handler { + private: + using format_specs = basic_format_specs<Char>; + + format_specs& specs_; + + public: + explicit printf_width_handler(format_specs& specs) : specs_(specs) {} + + template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> + unsigned operator()(T value) { + auto width = static_cast<uint32_or_64_or_128_t<T>>(value); + if (detail::is_negative(value)) { + specs_.align = align::left; + width = 0 - width; + } + unsigned int_max = max_value<int>(); + if (width > int_max) FMT_THROW(format_error("number is too big")); + return static_cast<unsigned>(width); + } + + template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> + unsigned operator()(T) { + FMT_THROW(format_error("width is not integer")); + return 0; + } +}; + +// The ``printf`` argument formatter. +template <typename OutputIt, typename Char> +class printf_arg_formatter : public arg_formatter<Char> { + private: + using base = arg_formatter<Char>; + using context_type = basic_printf_context<OutputIt, Char>; + using format_specs = basic_format_specs<Char>; + + context_type& context_; + + OutputIt write_null_pointer(bool is_string = false) { + auto s = this->specs; + s.type = presentation_type::none; + return write_bytes(this->out, is_string ? "(null)" : "(nil)", s); + } + + public: + printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx) + : base{iter, s, locale_ref()}, context_(ctx) {} + + OutputIt operator()(monostate value) { return base::operator()(value); } + + template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)> + OutputIt operator()(T value) { + // MSVC2013 fails to compile separate overloads for bool and Char so use + // std::is_same instead. + if (std::is_same<T, Char>::value) { + format_specs fmt_specs = this->specs; + if (fmt_specs.type != presentation_type::none && + fmt_specs.type != presentation_type::chr) { + return (*this)(static_cast<int>(value)); + } + fmt_specs.sign = sign::none; + fmt_specs.alt = false; + fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types. + // align::numeric needs to be overwritten here since the '0' flag is + // ignored for non-numeric types + if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) + fmt_specs.align = align::right; + return write<Char>(this->out, static_cast<Char>(value), fmt_specs); + } + return base::operator()(value); + } + + template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> + OutputIt operator()(T value) { + return base::operator()(value); + } + + /** Formats a null-terminated C string. */ + OutputIt operator()(const char* value) { + if (value) return base::operator()(value); + return write_null_pointer(this->specs.type != presentation_type::pointer); + } + + /** Formats a null-terminated wide C string. */ + OutputIt operator()(const wchar_t* value) { + if (value) return base::operator()(value); + return write_null_pointer(this->specs.type != presentation_type::pointer); + } + + OutputIt operator()(basic_string_view<Char> value) { + return base::operator()(value); + } + + /** Formats a pointer. */ + OutputIt operator()(const void* value) { + return value ? base::operator()(value) : write_null_pointer(); + } + + /** Formats an argument of a custom (user-defined) type. */ + OutputIt operator()(typename basic_format_arg<context_type>::handle handle) { + auto parse_ctx = + basic_printf_parse_context<Char>(basic_string_view<Char>()); + handle.format(parse_ctx, context_); + return this->out; + } +}; + +template <typename Char> +void parse_flags(basic_format_specs<Char>& specs, const Char*& it, + const Char* end) { + for (; it != end; ++it) { + switch (*it) { + case '-': + specs.align = align::left; + break; + case '+': + specs.sign = sign::plus; + break; + case '0': + specs.fill[0] = '0'; + break; + case ' ': + if (specs.sign != sign::plus) { + specs.sign = sign::space; + } + break; + case '#': + specs.alt = true; + break; + default: + return; + } + } +} + +template <typename Char, typename GetArg> +int parse_header(const Char*& it, const Char* end, + basic_format_specs<Char>& specs, GetArg get_arg) { + int arg_index = -1; + Char c = *it; + if (c >= '0' && c <= '9') { + // Parse an argument index (if followed by '$') or a width possibly + // preceded with '0' flag(s). + int value = parse_nonnegative_int(it, end, -1); + if (it != end && *it == '$') { // value is an argument index + ++it; + arg_index = value != -1 ? value : max_value<int>(); + } else { + if (c == '0') specs.fill[0] = '0'; + if (value != 0) { + // Nonzero value means that we parsed width and don't need to + // parse it or flags again, so return now. + if (value == -1) FMT_THROW(format_error("number is too big")); + specs.width = value; + return arg_index; + } + } + } + parse_flags(specs, it, end); + // Parse width. + if (it != end) { + if (*it >= '0' && *it <= '9') { + specs.width = parse_nonnegative_int(it, end, -1); + if (specs.width == -1) FMT_THROW(format_error("number is too big")); + } else if (*it == '*') { + ++it; + specs.width = static_cast<int>(visit_format_arg( + detail::printf_width_handler<Char>(specs), get_arg(-1))); + } + } + return arg_index; +} + +template <typename Char, typename Context> +void vprintf(buffer<Char>& buf, basic_string_view<Char> format, + basic_format_args<Context> args) { + using OutputIt = buffer_appender<Char>; + auto out = OutputIt(buf); + auto context = basic_printf_context<OutputIt, Char>(out, args); + auto parse_ctx = basic_printf_parse_context<Char>(format); + + // Returns the argument with specified index or, if arg_index is -1, the next + // argument. + auto get_arg = [&](int arg_index) { + if (arg_index < 0) + arg_index = parse_ctx.next_arg_id(); + else + parse_ctx.check_arg_id(--arg_index); + return detail::get_arg(context, arg_index); + }; + + const Char* start = parse_ctx.begin(); + const Char* end = parse_ctx.end(); + auto it = start; + while (it != end) { + if (!detail::find<false, Char>(it, end, '%', it)) { + it = end; // detail::find leaves it == nullptr if it doesn't find '%' + break; + } + Char c = *it++; + if (it != end && *it == c) { + out = detail::write( + out, basic_string_view<Char>(start, detail::to_unsigned(it - start))); + start = ++it; + continue; + } + out = detail::write(out, basic_string_view<Char>( + start, detail::to_unsigned(it - 1 - start))); + + basic_format_specs<Char> specs; + specs.align = align::right; + + // Parse argument index, flags and width. + int arg_index = parse_header(it, end, specs, get_arg); + if (arg_index == 0) parse_ctx.on_error("argument not found"); + + // Parse precision. + if (it != end && *it == '.') { + ++it; + c = it != end ? *it : 0; + if ('0' <= c && c <= '9') { + specs.precision = parse_nonnegative_int(it, end, 0); + } else if (c == '*') { + ++it; + specs.precision = static_cast<int>( + visit_format_arg(detail::printf_precision_handler(), get_arg(-1))); + } else { + specs.precision = 0; + } + } + + auto arg = get_arg(arg_index); + // For d, i, o, u, x, and X conversion specifiers, if a precision is + // specified, the '0' flag is ignored + if (specs.precision >= 0 && arg.is_integral()) + specs.fill[0] = + ' '; // Ignore '0' flag for non-numeric types or if '-' present. + if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) { + auto str = visit_format_arg(detail::get_cstring<Char>(), arg); + auto str_end = str + specs.precision; + auto nul = std::find(str, str_end, Char()); + arg = detail::make_arg<basic_printf_context<OutputIt, Char>>( + basic_string_view<Char>( + str, detail::to_unsigned(nul != str_end ? nul - str + : specs.precision))); + } + if (specs.alt && visit_format_arg(detail::is_zero_int(), arg)) + specs.alt = false; + if (specs.fill[0] == '0') { + if (arg.is_arithmetic() && specs.align != align::left) + specs.align = align::numeric; + else + specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-' + // flag is also present. + } + + // Parse length and convert the argument to the required type. + c = it != end ? *it++ : 0; + Char t = it != end ? *it : 0; + using detail::convert_arg; + switch (c) { + case 'h': + if (t == 'h') { + ++it; + t = it != end ? *it : 0; + convert_arg<signed char>(arg, t); + } else { + convert_arg<short>(arg, t); + } + break; + case 'l': + if (t == 'l') { + ++it; + t = it != end ? *it : 0; + convert_arg<long long>(arg, t); + } else { + convert_arg<long>(arg, t); + } + break; + case 'j': + convert_arg<intmax_t>(arg, t); + break; + case 'z': + convert_arg<size_t>(arg, t); + break; + case 't': + convert_arg<std::ptrdiff_t>(arg, t); + break; + case 'L': + // printf produces garbage when 'L' is omitted for long double, no + // need to do the same. + break; + default: + --it; + convert_arg<void>(arg, c); + } + + // Parse type. + if (it == end) FMT_THROW(format_error("invalid format string")); + char type = static_cast<char>(*it++); + if (arg.is_integral()) { + // Normalize type. + switch (type) { + case 'i': + case 'u': + type = 'd'; + break; + case 'c': + visit_format_arg( + detail::char_converter<basic_printf_context<OutputIt, Char>>(arg), + arg); + break; + } + } + specs.type = parse_presentation_type(type); + if (specs.type == presentation_type::none) + parse_ctx.on_error("invalid type specifier"); + + start = it; + + // Format argument. + out = visit_format_arg( + detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg); + } + detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start))); +} +FMT_END_DETAIL_NAMESPACE + +template <typename Char> +using basic_printf_context_t = + basic_printf_context<detail::buffer_appender<Char>, Char>; + +using printf_context = basic_printf_context_t<char>; +using wprintf_context = basic_printf_context_t<wchar_t>; + +using printf_args = basic_format_args<printf_context>; +using wprintf_args = basic_format_args<wprintf_context>; + +/** + \rst + Constructs an `~fmt::format_arg_store` object that contains references to + arguments and can be implicitly converted to `~fmt::printf_args`. + \endrst + */ +template <typename... T> +inline auto make_printf_args(const T&... args) + -> format_arg_store<printf_context, T...> { + return {args...}; +} + +/** + \rst + Constructs an `~fmt::format_arg_store` object that contains references to + arguments and can be implicitly converted to `~fmt::wprintf_args`. + \endrst + */ +template <typename... T> +inline auto make_wprintf_args(const T&... args) + -> format_arg_store<wprintf_context, T...> { + return {args...}; +} + +template <typename S, typename Char = char_t<S>> +inline auto vsprintf( + const S& fmt, + basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) + -> std::basic_string<Char> { + basic_memory_buffer<Char> buffer; + vprintf(buffer, detail::to_string_view(fmt), args); + return to_string(buffer); +} + +/** + \rst + Formats arguments and returns the result as a string. + + **Example**:: + + std::string message = fmt::sprintf("The answer is %d", 42); + \endrst +*/ +template <typename S, typename... T, + typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> +inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> { + using context = basic_printf_context_t<Char>; + return vsprintf(detail::to_string_view(fmt), + fmt::make_format_args<context>(args...)); +} + +template <typename S, typename Char = char_t<S>> +inline auto vfprintf( + std::FILE* f, const S& fmt, + basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) + -> int { + basic_memory_buffer<Char> buffer; + vprintf(buffer, detail::to_string_view(fmt), args); + size_t size = buffer.size(); + return std::fwrite(buffer.data(), sizeof(Char), size, f) < size + ? -1 + : static_cast<int>(size); +} + +/** + \rst + Prints formatted data to the file *f*. + + **Example**:: + + fmt::fprintf(stderr, "Don't %s!", "panic"); + \endrst + */ +template <typename S, typename... T, typename Char = char_t<S>> +inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { + using context = basic_printf_context_t<Char>; + return vfprintf(f, detail::to_string_view(fmt), + fmt::make_format_args<context>(args...)); +} + +template <typename S, typename Char = char_t<S>> +inline auto vprintf( + const S& fmt, + basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) + -> int { + return vfprintf(stdout, detail::to_string_view(fmt), args); +} + +/** + \rst + Prints formatted data to ``stdout``. + + **Example**:: + + fmt::printf("Elapsed time: %.2f seconds", 1.23); + \endrst + */ +template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)> +inline auto printf(const S& fmt, const T&... args) -> int { + return vprintf( + detail::to_string_view(fmt), + fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...)); +} + +FMT_MODULE_EXPORT_END +FMT_END_NAMESPACE + +#endif // FMT_PRINTF_H_ diff --git a/src/fmt/include/fmt/ranges.h b/src/fmt/include/fmt/ranges.h new file mode 100644 index 000000000..dea7d60dd --- /dev/null +++ b/src/fmt/include/fmt/ranges.h @@ -0,0 +1,722 @@ +// Formatting library for C++ - experimental range support +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. +// +// Copyright (c) 2018 - present, Remotion (Igor Schulz) +// All Rights Reserved +// {fmt} support for ranges, containers and types tuple interface. + +#ifndef FMT_RANGES_H_ +#define FMT_RANGES_H_ + +#include <initializer_list> +#include <tuple> +#include <type_traits> + +#include "format.h" + +FMT_BEGIN_NAMESPACE + +namespace detail { + +template <typename RangeT, typename OutputIterator> +OutputIterator copy(const RangeT& range, OutputIterator out) { + for (auto it = range.begin(), end = range.end(); it != end; ++it) + *out++ = *it; + return out; +} + +template <typename OutputIterator> +OutputIterator copy(const char* str, OutputIterator out) { + while (*str) *out++ = *str++; + return out; +} + +template <typename OutputIterator> +OutputIterator copy(char ch, OutputIterator out) { + *out++ = ch; + return out; +} + +template <typename OutputIterator> +OutputIterator copy(wchar_t ch, OutputIterator out) { + *out++ = ch; + return out; +} + +// Returns true if T has a std::string-like interface, like std::string_view. +template <typename T> class is_std_string_like { + template <typename U> + static auto check(U* p) + -> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); + template <typename> static void check(...); + + public: + static constexpr const bool value = + is_string<T>::value || + std::is_convertible<T, std_string_view<char>>::value || + !std::is_void<decltype(check<T>(nullptr))>::value; +}; + +template <typename Char> +struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {}; + +template <typename T> class is_map { + template <typename U> static auto check(U*) -> typename U::mapped_type; + template <typename> static void check(...); + + public: +#ifdef FMT_FORMAT_MAP_AS_LIST + static constexpr const bool value = false; +#else + static constexpr const bool value = + !std::is_void<decltype(check<T>(nullptr))>::value; +#endif +}; + +template <typename T> class is_set { + template <typename U> static auto check(U*) -> typename U::key_type; + template <typename> static void check(...); + + public: +#ifdef FMT_FORMAT_SET_AS_LIST + static constexpr const bool value = false; +#else + static constexpr const bool value = + !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value; +#endif +}; + +template <typename... Ts> struct conditional_helper {}; + +template <typename T, typename _ = void> struct is_range_ : std::false_type {}; + +#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800 + +# define FMT_DECLTYPE_RETURN(val) \ + ->decltype(val) { return val; } \ + static_assert( \ + true, "") // This makes it so that a semicolon is required after the + // macro, which helps clang-format handle the formatting. + +// C array overload +template <typename T, std::size_t N> +auto range_begin(const T (&arr)[N]) -> const T* { + return arr; +} +template <typename T, std::size_t N> +auto range_end(const T (&arr)[N]) -> const T* { + return arr + N; +} + +template <typename T, typename Enable = void> +struct has_member_fn_begin_end_t : std::false_type {}; + +template <typename T> +struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()), + decltype(std::declval<T>().end())>> + : std::true_type {}; + +// Member function overload +template <typename T> +auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin()); +template <typename T> +auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end()); + +// ADL overload. Only participates in overload resolution if member functions +// are not found. +template <typename T> +auto range_begin(T&& rng) + -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value, + decltype(begin(static_cast<T&&>(rng)))> { + return begin(static_cast<T&&>(rng)); +} +template <typename T> +auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value, + decltype(end(static_cast<T&&>(rng)))> { + return end(static_cast<T&&>(rng)); +} + +template <typename T, typename Enable = void> +struct has_const_begin_end : std::false_type {}; +template <typename T, typename Enable = void> +struct has_mutable_begin_end : std::false_type {}; + +template <typename T> +struct has_const_begin_end< + T, + void_t< + decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())), + decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>> + : std::true_type {}; + +template <typename T> +struct has_mutable_begin_end< + T, void_t<decltype(detail::range_begin(std::declval<T>())), + decltype(detail::range_end(std::declval<T>())), + enable_if_t<std::is_copy_constructible<T>::value>>> + : std::true_type {}; + +template <typename T> +struct is_range_<T, void> + : std::integral_constant<bool, (has_const_begin_end<T>::value || + has_mutable_begin_end<T>::value)> {}; +# undef FMT_DECLTYPE_RETURN +#endif + +// tuple_size and tuple_element check. +template <typename T> class is_tuple_like_ { + template <typename U> + static auto check(U* p) -> decltype(std::tuple_size<U>::value, int()); + template <typename> static void check(...); + + public: + static constexpr const bool value = + !std::is_void<decltype(check<T>(nullptr))>::value; +}; + +// Check for integer_sequence +#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900 +template <typename T, T... N> +using integer_sequence = std::integer_sequence<T, N...>; +template <size_t... N> using index_sequence = std::index_sequence<N...>; +template <size_t N> using make_index_sequence = std::make_index_sequence<N>; +#else +template <typename T, T... N> struct integer_sequence { + using value_type = T; + + static FMT_CONSTEXPR size_t size() { return sizeof...(N); } +}; + +template <size_t... N> using index_sequence = integer_sequence<size_t, N...>; + +template <typename T, size_t N, T... Ns> +struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {}; +template <typename T, T... Ns> +struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {}; + +template <size_t N> +using make_index_sequence = make_integer_sequence<size_t, N>; +#endif + +template <typename T> +using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>; + +template <typename T, typename C, bool = is_tuple_like_<T>::value> +class is_tuple_formattable_ { + public: + static constexpr const bool value = false; +}; +template <typename T, typename C> class is_tuple_formattable_<T, C, true> { + template <std::size_t... I> + static std::true_type check2(index_sequence<I...>, + integer_sequence<bool, (I == I)...>); + static std::false_type check2(...); + template <std::size_t... I> + static decltype(check2( + index_sequence<I...>{}, + integer_sequence< + bool, (is_formattable<typename std::tuple_element<I, T>::type, + C>::value)...>{})) check(index_sequence<I...>); + + public: + static constexpr const bool value = + decltype(check(tuple_index_sequence<T>{}))::value; +}; + +template <class Tuple, class F, size_t... Is> +void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) noexcept { + using std::get; + // using free function get<I>(T) now. + const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; + (void)_; // blocks warnings +} + +template <class T> +FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes( + T const&) { + return {}; +} + +template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) { + const auto indexes = get_indexes(tup); + for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); +} + +#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920 +// Older MSVC doesn't get the reference type correctly for arrays. +template <typename R> struct range_reference_type_impl { + using type = decltype(*detail::range_begin(std::declval<R&>())); +}; + +template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> { + using type = T&; +}; + +template <typename T> +using range_reference_type = typename range_reference_type_impl<T>::type; +#else +template <typename Range> +using range_reference_type = + decltype(*detail::range_begin(std::declval<Range&>())); +#endif + +// We don't use the Range's value_type for anything, but we do need the Range's +// reference type, with cv-ref stripped. +template <typename Range> +using uncvref_type = remove_cvref_t<range_reference_type<Range>>; + +template <typename Range> +using uncvref_first_type = + remove_cvref_t<decltype(std::declval<range_reference_type<Range>>().first)>; + +template <typename Range> +using uncvref_second_type = remove_cvref_t< + decltype(std::declval<range_reference_type<Range>>().second)>; + +template <typename OutputIt> OutputIt write_delimiter(OutputIt out) { + *out++ = ','; + *out++ = ' '; + return out; +} + +template <typename Char, typename OutputIt> +auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt { + return write_escaped_string(out, str); +} + +template <typename Char, typename OutputIt, typename T, + FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)> +inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt { + auto sv = std_string_view<Char>(str); + return write_range_entry<Char>(out, basic_string_view<Char>(sv)); +} + +template <typename Char, typename OutputIt, typename Arg, + FMT_ENABLE_IF(std::is_same<Arg, Char>::value)> +OutputIt write_range_entry(OutputIt out, const Arg v) { + return write_escaped_char(out, v); +} + +template < + typename Char, typename OutputIt, typename Arg, + FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value && + !std::is_same<Arg, Char>::value)> +OutputIt write_range_entry(OutputIt out, const Arg& v) { + return write<Char>(out, v); +} + +} // namespace detail + +template <typename T> struct is_tuple_like { + static constexpr const bool value = + detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value; +}; + +template <typename T, typename C> struct is_tuple_formattable { + static constexpr const bool value = + detail::is_tuple_formattable_<T, C>::value; +}; + +template <typename TupleT, typename Char> +struct formatter<TupleT, Char, + enable_if_t<fmt::is_tuple_like<TupleT>::value && + fmt::is_tuple_formattable<TupleT, Char>::value>> { + private: + basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; + basic_string_view<Char> opening_bracket_ = + detail::string_literal<Char, '('>{}; + basic_string_view<Char> closing_bracket_ = + detail::string_literal<Char, ')'>{}; + + // C++11 generic lambda for format(). + template <typename FormatContext> struct format_each { + template <typename T> void operator()(const T& v) { + if (i > 0) out = detail::copy_str<Char>(separator, out); + out = detail::write_range_entry<Char>(out, v); + ++i; + } + int i; + typename FormatContext::iterator& out; + basic_string_view<Char> separator; + }; + + public: + FMT_CONSTEXPR formatter() {} + + FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { + separator_ = sep; + } + + FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, + basic_string_view<Char> close) { + opening_bracket_ = open; + closing_bracket_ = close; + } + + template <typename ParseContext> + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template <typename FormatContext = format_context> + auto format(const TupleT& values, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + out = detail::copy_str<Char>(opening_bracket_, out); + detail::for_each(values, format_each<FormatContext>{0, out, separator_}); + out = detail::copy_str<Char>(closing_bracket_, out); + return out; + } +}; + +template <typename T, typename Char> struct is_range { + static constexpr const bool value = + detail::is_range_<T>::value && !detail::is_std_string_like<T>::value && + !std::is_convertible<T, std::basic_string<Char>>::value && + !std::is_convertible<T, detail::std_string_view<Char>>::value; +}; + +namespace detail { +template <typename Context> struct range_mapper { + using mapper = arg_mapper<Context>; + + template <typename T, + FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)> + static auto map(T&& value) -> T&& { + return static_cast<T&&>(value); + } + template <typename T, + FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)> + static auto map(T&& value) + -> decltype(mapper().map(static_cast<T&&>(value))) { + return mapper().map(static_cast<T&&>(value)); + } +}; + +template <typename Char, typename Element> +using range_formatter_type = conditional_t< + is_formattable<Element, Char>::value, + formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map( + std::declval<Element>()))>, + Char>, + fallback_formatter<Element, Char>>; + +template <typename R> +using maybe_const_range = + conditional_t<has_const_begin_end<R>::value, const R, R>; + +// Workaround a bug in MSVC 2015 and earlier. +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 +template <typename R, typename Char> +struct is_formattable_delayed + : disjunction< + is_formattable<uncvref_type<maybe_const_range<R>>, Char>, + has_fallback_formatter<uncvref_type<maybe_const_range<R>>, Char>> {}; +#endif + +} // namespace detail + +template <typename T, typename Char, typename Enable = void> +struct range_formatter; + +template <typename T, typename Char> +struct range_formatter< + T, Char, + enable_if_t<conjunction< + std::is_same<T, remove_cvref_t<T>>, + disjunction<is_formattable<T, Char>, + detail::has_fallback_formatter<T, Char>>>::value>> { + private: + detail::range_formatter_type<Char, T> underlying_; + bool custom_specs_ = false; + basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; + basic_string_view<Char> opening_bracket_ = + detail::string_literal<Char, '['>{}; + basic_string_view<Char> closing_bracket_ = + detail::string_literal<Char, ']'>{}; + + template <class U> + FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, int) + -> decltype(u.set_debug_format()) { + u.set_debug_format(); + } + + template <class U> + FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {} + + FMT_CONSTEXPR void maybe_set_debug_format() { + maybe_set_debug_format(underlying_, 0); + } + + public: + FMT_CONSTEXPR range_formatter() {} + + FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& { + return underlying_; + } + + FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { + separator_ = sep; + } + + FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, + basic_string_view<Char> close) { + opening_bracket_ = open; + closing_bracket_ = close; + } + + template <typename ParseContext> + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + auto it = ctx.begin(); + auto end = ctx.end(); + if (it == end || *it == '}') { + maybe_set_debug_format(); + return it; + } + + if (*it == 'n') { + set_brackets({}, {}); + ++it; + } + + if (*it == '}') { + maybe_set_debug_format(); + return it; + } + + if (*it != ':') + FMT_THROW(format_error("no other top-level range formatters supported")); + + custom_specs_ = true; + ++it; + ctx.advance_to(it); + return underlying_.parse(ctx); + } + + template <typename R, class FormatContext> + auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { + detail::range_mapper<buffer_context<Char>> mapper; + auto out = ctx.out(); + out = detail::copy_str<Char>(opening_bracket_, out); + int i = 0; + auto it = detail::range_begin(range); + auto end = detail::range_end(range); + for (; it != end; ++it) { + if (i > 0) out = detail::copy_str<Char>(separator_, out); + ; + ctx.advance_to(out); + out = underlying_.format(mapper.map(*it), ctx); + ++i; + } + out = detail::copy_str<Char>(closing_bracket_, out); + return out; + } +}; + +enum class range_format { disabled, map, set, sequence, string, debug_string }; + +namespace detail { +template <typename T> struct range_format_kind_ { + static constexpr auto value = std::is_same<range_reference_type<T>, T>::value + ? range_format::disabled + : is_map<T>::value ? range_format::map + : is_set<T>::value ? range_format::set + : range_format::sequence; +}; + +template <range_format K, typename R, typename Char, typename Enable = void> +struct range_default_formatter; + +template <range_format K> +using range_format_constant = std::integral_constant<range_format, K>; + +template <range_format K, typename R, typename Char> +struct range_default_formatter< + K, R, Char, + enable_if_t<(K == range_format::sequence || K == range_format::map || + K == range_format::set)>> { + using range_type = detail::maybe_const_range<R>; + range_formatter<detail::uncvref_type<range_type>, Char> underlying_; + + FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); } + + FMT_CONSTEXPR void init(range_format_constant<range_format::set>) { + underlying_.set_brackets(detail::string_literal<Char, '{'>{}, + detail::string_literal<Char, '}'>{}); + } + + FMT_CONSTEXPR void init(range_format_constant<range_format::map>) { + underlying_.set_brackets(detail::string_literal<Char, '{'>{}, + detail::string_literal<Char, '}'>{}); + underlying_.underlying().set_brackets({}, {}); + underlying_.underlying().set_separator( + detail::string_literal<Char, ':', ' '>{}); + } + + FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {} + + template <typename ParseContext> + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return underlying_.parse(ctx); + } + + template <typename FormatContext> + auto format(range_type& range, FormatContext& ctx) const + -> decltype(ctx.out()) { + return underlying_.format(range, ctx); + } +}; +} // namespace detail + +template <typename T, typename Char, typename Enable = void> +struct range_format_kind + : conditional_t< + is_range<T, Char>::value, detail::range_format_kind_<T>, + std::integral_constant<range_format, range_format::disabled>> {}; + +template <typename R, typename Char> +struct formatter< + R, Char, + enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value != + range_format::disabled> +// Workaround a bug in MSVC 2015 and earlier. +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 + , + detail::is_formattable_delayed<R, Char> +#endif + >::value>> + : detail::range_default_formatter<range_format_kind<R, Char>::value, R, + Char> { +}; + +template <typename Char, typename... T> struct tuple_join_view : detail::view { + const std::tuple<T...>& tuple; + basic_string_view<Char> sep; + + tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s) + : tuple(t), sep{s} {} +}; + +template <typename Char, typename... T> +using tuple_arg_join = tuple_join_view<Char, T...>; + +// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers +// support in tuple_join. It is disabled by default because of issues with +// the dynamic width and precision. +#ifndef FMT_TUPLE_JOIN_SPECIFIERS +# define FMT_TUPLE_JOIN_SPECIFIERS 0 +#endif + +template <typename Char, typename... T> +struct formatter<tuple_join_view<Char, T...>, Char> { + template <typename ParseContext> + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>()); + } + + template <typename FormatContext> + auto format(const tuple_join_view<Char, T...>& value, + FormatContext& ctx) const -> typename FormatContext::iterator { + return do_format(value, ctx, + std::integral_constant<size_t, sizeof...(T)>()); + } + + private: + std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_; + + template <typename ParseContext> + FMT_CONSTEXPR auto do_parse(ParseContext& ctx, + std::integral_constant<size_t, 0>) + -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template <typename ParseContext, size_t N> + FMT_CONSTEXPR auto do_parse(ParseContext& ctx, + std::integral_constant<size_t, N>) + -> decltype(ctx.begin()) { + auto end = ctx.begin(); +#if FMT_TUPLE_JOIN_SPECIFIERS + end = std::get<sizeof...(T) - N>(formatters_).parse(ctx); + if (N > 1) { + auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>()); + if (end != end1) + FMT_THROW(format_error("incompatible format specs for tuple elements")); + } +#endif + return end; + } + + template <typename FormatContext> + auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx, + std::integral_constant<size_t, 0>) const -> + typename FormatContext::iterator { + return ctx.out(); + } + + template <typename FormatContext, size_t N> + auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx, + std::integral_constant<size_t, N>) const -> + typename FormatContext::iterator { + auto out = std::get<sizeof...(T) - N>(formatters_) + .format(std::get<sizeof...(T) - N>(value.tuple), ctx); + if (N > 1) { + out = std::copy(value.sep.begin(), value.sep.end(), out); + ctx.advance_to(out); + return do_format(value, ctx, std::integral_constant<size_t, N - 1>()); + } + return out; + } +}; + +FMT_MODULE_EXPORT_BEGIN + +/** + \rst + Returns an object that formats `tuple` with elements separated by `sep`. + + **Example**:: + + std::tuple<int, char> t = {1, 'a'}; + fmt::print("{}", fmt::join(t, ", ")); + // Output: "1, a" + \endrst + */ +template <typename... T> +FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep) + -> tuple_join_view<char, T...> { + return {tuple, sep}; +} + +template <typename... T> +FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, + basic_string_view<wchar_t> sep) + -> tuple_join_view<wchar_t, T...> { + return {tuple, sep}; +} + +/** + \rst + Returns an object that formats `initializer_list` with elements separated by + `sep`. + + **Example**:: + + fmt::print("{}", fmt::join({1, 2, 3}, ", ")); + // Output: "1, 2, 3" + \endrst + */ +template <typename T> +auto join(std::initializer_list<T> list, string_view sep) + -> join_view<const T*, const T*> { + return join(std::begin(list), std::end(list), sep); +} + +FMT_MODULE_EXPORT_END +FMT_END_NAMESPACE + +#endif // FMT_RANGES_H_ diff --git a/src/fmt/include/fmt/std.h b/src/fmt/include/fmt/std.h new file mode 100644 index 000000000..41d2b2838 --- /dev/null +++ b/src/fmt/include/fmt/std.h @@ -0,0 +1,171 @@ +// Formatting library for C++ - formatters for standard library types +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_STD_H_ +#define FMT_STD_H_ + +#include <thread> +#include <type_traits> +#include <utility> + +#include "ostream.h" + +#if FMT_HAS_INCLUDE(<version>) +# include <version> +#endif +// Checking FMT_CPLUSPLUS for warning suppression in MSVC. +#if FMT_CPLUSPLUS >= 201703L +# if FMT_HAS_INCLUDE(<filesystem>) +# include <filesystem> +# endif +# if FMT_HAS_INCLUDE(<variant>) +# include <variant> +# endif +#endif + +#ifdef __cpp_lib_filesystem +FMT_BEGIN_NAMESPACE + +namespace detail { + +template <typename Char> +void write_escaped_path(basic_memory_buffer<Char>& quoted, + const std::filesystem::path& p) { + write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>()); +} +# ifdef _WIN32 +template <> +inline void write_escaped_path<char>(basic_memory_buffer<char>& quoted, + const std::filesystem::path& p) { + auto s = p.u8string(); + write_escaped_string<char>( + std::back_inserter(quoted), + string_view(reinterpret_cast<const char*>(s.c_str()), s.size())); +} +# endif +template <> +inline void write_escaped_path<std::filesystem::path::value_type>( + basic_memory_buffer<std::filesystem::path::value_type>& quoted, + const std::filesystem::path& p) { + write_escaped_string<std::filesystem::path::value_type>( + std::back_inserter(quoted), p.native()); +} + +} // namespace detail + +template <typename Char> +struct formatter<std::filesystem::path, Char> + : formatter<basic_string_view<Char>> { + template <typename FormatContext> + auto format(const std::filesystem::path& p, FormatContext& ctx) const -> + typename FormatContext::iterator { + basic_memory_buffer<Char> quoted; + detail::write_escaped_path(quoted, p); + return formatter<basic_string_view<Char>>::format( + basic_string_view<Char>(quoted.data(), quoted.size()), ctx); + } +}; +FMT_END_NAMESPACE +#endif + +FMT_BEGIN_NAMESPACE +template <typename Char> +struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {}; +FMT_END_NAMESPACE + +#ifdef __cpp_lib_variant +FMT_BEGIN_NAMESPACE +template <typename Char> struct formatter<std::monostate, Char> { + template <typename ParseContext> + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template <typename FormatContext> + auto format(const std::monostate&, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + out = detail::write<Char>(out, "monostate"); + return out; + } +}; + +namespace detail { + +template <typename T> +using variant_index_sequence = + std::make_index_sequence<std::variant_size<T>::value>; + +// variant_size and variant_alternative check. +template <typename T, typename U = void> +struct is_variant_like_ : std::false_type {}; +template <typename T> +struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>> + : std::true_type {}; + +// formattable element check +template <typename T, typename C> class is_variant_formattable_ { + template <std::size_t... I> + static std::conjunction< + is_formattable<std::variant_alternative_t<I, T>, C>...> + check(std::index_sequence<I...>); + + public: + static constexpr const bool value = + decltype(check(variant_index_sequence<T>{}))::value; +}; + +template <typename Char, typename OutputIt, typename T> +auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt { + if constexpr (is_string<T>::value) + return write_escaped_string<Char>(out, detail::to_string_view(v)); + else if constexpr (std::is_same_v<T, Char>) + return write_escaped_char(out, v); + else + return write<Char>(out, v); +} + +} // namespace detail + +template <typename T> struct is_variant_like { + static constexpr const bool value = detail::is_variant_like_<T>::value; +}; + +template <typename T, typename C> struct is_variant_formattable { + static constexpr const bool value = + detail::is_variant_formattable_<T, C>::value; +}; + +template <typename Variant, typename Char> +struct formatter< + Variant, Char, + std::enable_if_t<std::conjunction_v< + is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> { + template <typename ParseContext> + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template <typename FormatContext> + auto format(const Variant& value, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + + out = detail::write<Char>(out, "variant("); + std::visit( + [&](const auto& v) { + out = detail::write_variant_alternative<Char>(out, v); + }, + value); + *out++ = ')'; + return out; + } +}; +FMT_END_NAMESPACE +#endif + +#endif // FMT_STD_H_ diff --git a/src/fmt/include/fmt/xchar.h b/src/fmt/include/fmt/xchar.h new file mode 100644 index 000000000..3b5bc15ca --- /dev/null +++ b/src/fmt/include/fmt/xchar.h @@ -0,0 +1,229 @@ +// Formatting library for C++ - optional wchar_t and exotic character support +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_XCHAR_H_ +#define FMT_XCHAR_H_ + +#include <cwchar> + +#include "format.h" + +FMT_BEGIN_NAMESPACE +namespace detail { +template <typename T> +using is_exotic_char = bool_constant<!std::is_same<T, char>::value>; +} + +FMT_MODULE_EXPORT_BEGIN + +using wstring_view = basic_string_view<wchar_t>; +using wformat_parse_context = basic_format_parse_context<wchar_t>; +using wformat_context = buffer_context<wchar_t>; +using wformat_args = basic_format_args<wformat_context>; +using wmemory_buffer = basic_memory_buffer<wchar_t>; + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +// Workaround broken conversion on older gcc. +template <typename... Args> using wformat_string = wstring_view; +inline auto runtime(wstring_view s) -> wstring_view { return s; } +#else +template <typename... Args> +using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>; +inline auto runtime(wstring_view s) -> basic_runtime<wchar_t> { return {{s}}; } +#endif + +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 {}; + +template <typename... Args> +constexpr format_arg_store<wformat_context, Args...> make_wformat_args( + const Args&... args) { + return {args...}; +} + +inline namespace literals { +#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS +constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) { + return {s}; +} +#endif +} // namespace literals + +template <typename It, typename Sentinel> +auto join(It begin, Sentinel end, wstring_view sep) + -> join_view<It, Sentinel, wchar_t> { + return {begin, end, sep}; +} + +template <typename Range> +auto join(Range&& range, wstring_view sep) + -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>, + wchar_t> { + return join(std::begin(range), std::end(range), sep); +} + +template <typename T> +auto join(std::initializer_list<T> list, wstring_view sep) + -> join_view<const T*, const T*, wchar_t> { + return join(std::begin(list), std::end(list), sep); +} + +template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)> +auto vformat(basic_string_view<Char> format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) + -> std::basic_string<Char> { + basic_memory_buffer<Char> buffer; + detail::vformat_to(buffer, format_str, args); + return to_string(buffer); +} + +template <typename... T> +auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring { + return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...)); +} + +// 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_ENABLE_IF(!std::is_same<Char, char>::value && + !std::is_same<Char, wchar_t>::value)> +auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> { + return vformat(detail::to_string_view(format_str), + fmt::make_format_args<buffer_context<Char>>(args...)); +} + +template <typename Locale, typename S, typename Char = char_t<S>, + FMT_ENABLE_IF(detail::is_locale<Locale>::value&& + detail::is_exotic_char<Char>::value)> +inline auto vformat( + const Locale& loc, const S& format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) + -> std::basic_string<Char> { + return detail::vformat(loc, detail::to_string_view(format_str), args); +} + +template <typename Locale, typename S, typename... Args, + typename Char = char_t<S>, + FMT_ENABLE_IF(detail::is_locale<Locale>::value&& + detail::is_exotic_char<Char>::value)> +inline auto format(const Locale& loc, const S& format_str, Args&&... args) + -> std::basic_string<Char> { + return detail::vformat(loc, detail::to_string_view(format_str), + fmt::make_format_args<buffer_context<Char>>(args...)); +} + +template <typename OutputIt, typename S, typename Char = char_t<S>, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& + detail::is_exotic_char<Char>::value)> +auto vformat_to(OutputIt out, const S& format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) + -> OutputIt { + auto&& buf = detail::get_buffer<Char>(out); + detail::vformat_to(buf, detail::to_string_view(format_str), args); + return detail::get_iterator(buf); +} + +template <typename OutputIt, typename S, typename... Args, + typename Char = char_t<S>, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& + detail::is_exotic_char<Char>::value)> +inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { + return vformat_to(out, detail::to_string_view(fmt), + fmt::make_format_args<buffer_context<Char>>(args...)); +} + +template <typename Locale, typename S, typename OutputIt, typename... Args, + typename Char = char_t<S>, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& + detail::is_locale<Locale>::value&& + detail::is_exotic_char<Char>::value)> +inline auto vformat_to( + OutputIt out, const Locale& loc, const S& format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt { + auto&& buf = detail::get_buffer<Char>(out); + vformat_to(buf, detail::to_string_view(format_str), args, + detail::locale_ref(loc)); + return detail::get_iterator(buf); +} + +template < + typename OutputIt, typename Locale, typename S, typename... Args, + typename Char = char_t<S>, + bool enable = detail::is_output_iterator<OutputIt, Char>::value&& + detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value> +inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, + Args&&... args) -> + typename std::enable_if<enable, OutputIt>::type { + return vformat_to(out, loc, to_string_view(format_str), + fmt::make_format_args<buffer_context<Char>>(args...)); +} + +template <typename OutputIt, typename Char, typename... Args, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& + detail::is_exotic_char<Char>::value)> +inline auto vformat_to_n( + OutputIt out, size_t n, basic_string_view<Char> format_str, + basic_format_args<buffer_context<type_identity_t<Char>>> args) + -> format_to_n_result<OutputIt> { + detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out, + n); + detail::vformat_to(buf, format_str, args); + return {buf.out(), buf.count()}; +} + +template <typename OutputIt, typename S, typename... Args, + typename Char = char_t<S>, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& + detail::is_exotic_char<Char>::value)> +inline auto format_to_n(OutputIt out, size_t n, const S& fmt, + const Args&... args) -> format_to_n_result<OutputIt> { + return vformat_to_n(out, n, detail::to_string_view(fmt), + fmt::make_format_args<buffer_context<Char>>(args...)); +} + +template <typename S, typename... Args, typename Char = char_t<S>, + FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> +inline auto formatted_size(const S& fmt, Args&&... args) -> size_t { + detail::counting_buffer<Char> buf; + detail::vformat_to(buf, detail::to_string_view(fmt), + fmt::make_format_args<buffer_context<Char>>(args...)); + return buf.count(); +} + +inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { + wmemory_buffer buffer; + detail::vformat_to(buffer, fmt, args); + buffer.push_back(L'\0'); + if (std::fputws(buffer.data(), f) == -1) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); +} + +inline void vprint(wstring_view fmt, wformat_args args) { + vprint(stdout, fmt, args); +} + +template <typename... T> +void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) { + return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...)); +} + +template <typename... T> void print(wformat_string<T...> fmt, T&&... args) { + return vprint(wstring_view(fmt), fmt::make_wformat_args(args...)); +} + +/** + Converts *value* to ``std::wstring`` using the default format for type *T*. + */ +template <typename T> inline auto to_wstring(const T& value) -> std::wstring { + return format(FMT_STRING(L"{}"), value); +} +FMT_MODULE_EXPORT_END +FMT_END_NAMESPACE + +#endif // FMT_XCHAR_H_ diff --git a/src/fmt/src/fmt.cc b/src/fmt/src/fmt.cc new file mode 100644 index 000000000..80e77e26a --- /dev/null +++ b/src/fmt/src/fmt.cc @@ -0,0 +1,99 @@ +module; +#ifndef __cpp_modules +# error Module not supported. +#endif + +// put all implementation-provided headers into the global module fragment +// to prevent attachment to this module +#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) +# define _CRT_SECURE_NO_WARNINGS +#endif +#if !defined(WIN32_LEAN_AND_MEAN) && defined(_WIN32) +# define WIN32_LEAN_AND_MEAN +#endif + +#include <algorithm> +#include <cctype> +#include <cerrno> +#include <chrono> +#include <climits> +#include <clocale> +#include <cmath> +#include <cstdarg> +#include <cstddef> +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <ctime> +#include <cwchar> +#include <exception> +#include <functional> +#include <iterator> +#include <limits> +#include <locale> +#include <memory> +#include <ostream> +#include <sstream> +#include <stdexcept> +#include <string> +#include <string_view> +#include <system_error> +#include <type_traits> +#include <utility> +#include <vector> + +#if _MSC_VER +# include <intrin.h> +#endif +#if defined __APPLE__ || defined(__FreeBSD__) +# include <xlocale.h> +#endif +#if __has_include(<winapifamily.h>) +# include <winapifamily.h> +#endif +#if (__has_include(<fcntl.h>) || defined(__APPLE__) || \ + defined(__linux__)) && \ + (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) +# include <fcntl.h> +# include <sys/stat.h> +# include <sys/types.h> +# ifndef _WIN32 +# include <unistd.h> +# else +# include <io.h> +# endif +#endif +#ifdef _WIN32 +# include <windows.h> +#endif + +export module fmt; + +#define FMT_MODULE_EXPORT export +#define FMT_MODULE_EXPORT_BEGIN export { +#define FMT_MODULE_EXPORT_END } +#define FMT_BEGIN_DETAIL_NAMESPACE \ + } \ + namespace detail { +#define FMT_END_DETAIL_NAMESPACE \ + } \ + export { +// all library-provided declarations and definitions +// must be in the module purview to be exported +#include "fmt/args.h" +#include "fmt/chrono.h" +#include "fmt/color.h" +#include "fmt/compile.h" +#include "fmt/format.h" +#include "fmt/os.h" +#include "fmt/printf.h" +#include "fmt/xchar.h" + +// gcc doesn't yet implement private module fragments +#if !FMT_GCC_VERSION +module : private; +#endif + +#include "format.cc" +#include "os.cc" diff --git a/src/fmt/src/format.cc b/src/fmt/src/format.cc new file mode 100644 index 000000000..99b7e9dd4 --- /dev/null +++ b/src/fmt/src/format.cc @@ -0,0 +1,47 @@ +// 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 FMT_API auto dragonbox::to_decimal(float x) noexcept + -> dragonbox::decimal_fp<float>; +template FMT_API auto dragonbox::to_decimal(double x) noexcept + -> dragonbox::decimal_fp<double>; + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +template FMT_API locale_ref::locale_ref(const std::locale& loc); +template FMT_API auto locale_ref::get<std::locale>() const -> std::locale; +#endif + +// Explicit instantiations for char. + +template FMT_API auto thousands_sep_impl(locale_ref) + -> thousands_sep_result<char>; +template FMT_API auto decimal_point_impl(locale_ref) -> char; + +template FMT_API void buffer<char>::append(const char*, const char*); + +// DEPRECATED! +// There is no correspondent extern template in format.h because of +// incompatibility between clang and gcc (#2377). +template FMT_API void vformat_to(buffer<char>&, string_view, + basic_format_args<FMT_BUFFER_CONTEXT(char)>, + locale_ref); + +// Explicit instantiations for wchar_t. + +template FMT_API auto thousands_sep_impl(locale_ref) + -> thousands_sep_result<wchar_t>; +template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; + +template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*); + +} // namespace detail +FMT_END_NAMESPACE diff --git a/src/fmt/src/os.cc b/src/fmt/src/os.cc new file mode 100644 index 000000000..f388ead01 --- /dev/null +++ b/src/fmt/src/os.cc @@ -0,0 +1,361 @@ +// Formatting library for C++ - optional OS-specific functionality +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +// Disable bogus MSVC warnings. +#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) +# define _CRT_SECURE_NO_WARNINGS +#endif + +#include "fmt/os.h" + +#include <climits> + +#if FMT_USE_FCNTL +# include <sys/stat.h> +# include <sys/types.h> + +# ifndef _WIN32 +# include <unistd.h> +# else +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include <io.h> + +# ifndef S_IRUSR +# define S_IRUSR _S_IREAD +# endif +# ifndef S_IWUSR +# define S_IWUSR _S_IWRITE +# endif +# ifndef S_IRGRP +# define S_IRGRP 0 +# endif +# ifndef S_IWGRP +# define S_IWGRP 0 +# endif +# ifndef S_IROTH +# define S_IROTH 0 +# endif +# ifndef S_IWOTH +# define S_IWOTH 0 +# endif +# endif // _WIN32 +#endif // FMT_USE_FCNTL + +#ifdef _WIN32 +# include <windows.h> +#endif + +namespace { +#ifdef _WIN32 +// Return type of read and write functions. +using rwresult = int; + +// On Windows the count argument to read and write is unsigned, so convert +// it from size_t preventing integer overflow. +inline unsigned convert_rwcount(std::size_t count) { + return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX; +} +#elif FMT_USE_FCNTL +// Return type of read and write functions. +using rwresult = ssize_t; + +inline std::size_t convert_rwcount(std::size_t count) { return count; } +#endif +} // namespace + +FMT_BEGIN_NAMESPACE + +#ifdef _WIN32 +detail::utf16_to_utf8::utf16_to_utf8(basic_string_view<wchar_t> s) { + if (int error_code = convert(s)) { + FMT_THROW(windows_error(error_code, + "cannot convert string from UTF-16 to UTF-8")); + } +} + +int detail::utf16_to_utf8::convert(basic_string_view<wchar_t> s) { + if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; + int s_size = static_cast<int>(s.size()); + if (s_size == 0) { + // WideCharToMultiByte does not support zero length, handle separately. + buffer_.resize(1); + buffer_[0] = 0; + return 0; + } + + int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0, + nullptr, nullptr); + if (length == 0) return GetLastError(); + buffer_.resize(length + 1); + length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0], + length, nullptr, nullptr); + if (length == 0) return GetLastError(); + buffer_[length] = 0; + return 0; +} + +namespace detail { + +class system_message { + system_message(const system_message&) = delete; + void operator=(const system_message&) = delete; + + unsigned long result_; + wchar_t* message_; + + static bool is_whitespace(wchar_t c) noexcept { + return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0'; + } + + public: + explicit system_message(unsigned long error_code) + : result_(0), message_(nullptr) { + result_ = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast<wchar_t*>(&message_), 0, nullptr); + if (result_ != 0) { + while (result_ != 0 && is_whitespace(message_[result_ - 1])) { + --result_; + } + } + } + ~system_message() { LocalFree(message_); } + explicit operator bool() const noexcept { return result_ != 0; } + operator basic_string_view<wchar_t>() const noexcept { + return basic_string_view<wchar_t>(message_, result_); + } +}; + +class utf8_system_category final : public std::error_category { + public: + const char* name() const noexcept override { return "system"; } + std::string message(int error_code) const override { + system_message msg(error_code); + if (msg) { + utf16_to_utf8 utf8_message; + if (utf8_message.convert(msg) == ERROR_SUCCESS) { + return utf8_message.str(); + } + } + return "unknown error"; + } +}; + +} // namespace detail + +FMT_API const std::error_category& system_category() noexcept { + static const detail::utf8_system_category category; + return category; +} + +std::system_error vwindows_error(int err_code, string_view format_str, + format_args args) { + auto ec = std::error_code(err_code, system_category()); + return std::system_error(ec, vformat(format_str, args)); +} + +void detail::format_windows_error(detail::buffer<char>& out, int error_code, + const char* message) noexcept { + FMT_TRY { + system_message msg(error_code); + if (msg) { + utf16_to_utf8 utf8_message; + if (utf8_message.convert(msg) == ERROR_SUCCESS) { + fmt::format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message); + return; + } + } + } + FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +void report_windows_error(int error_code, const char* message) noexcept { + report_error(detail::format_windows_error, error_code, message); +} +#endif // _WIN32 + +buffered_file::~buffered_file() noexcept { + if (file_ && FMT_SYSTEM(fclose(file_)) != 0) + report_system_error(errno, "cannot close file"); +} + +buffered_file::buffered_file(cstring_view filename, cstring_view mode) { + FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), + nullptr); + if (!file_) + FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str())); +} + +void buffered_file::close() { + if (!file_) return; + int result = FMT_SYSTEM(fclose(file_)); + file_ = nullptr; + if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); +} + +int buffered_file::descriptor() const { + int fd = FMT_POSIX_CALL(fileno(file_)); + if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor")); + return fd; +} + +#if FMT_USE_FCNTL +file::file(cstring_view path, int oflag) { +# ifdef _WIN32 + using mode_t = int; +# endif + constexpr mode_t mode = + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; +# if defined(_WIN32) && !defined(__MINGW32__) + fd_ = -1; + FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); +# else + FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); +# endif + if (fd_ == -1) + FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); +} + +file::~file() noexcept { + // Don't retry close in case of EINTR! + // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html + if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) + report_system_error(errno, "cannot close file"); +} + +void file::close() { + if (fd_ == -1) return; + // Don't retry close in case of EINTR! + // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html + int result = FMT_POSIX_CALL(close(fd_)); + fd_ = -1; + if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); +} + +long long file::size() const { +# ifdef _WIN32 + // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT + // is less than 0x0500 as is the case with some default MinGW builds. + // Both functions support large file sizes. + DWORD size_upper = 0; + HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_)); + DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); + if (size_lower == INVALID_FILE_SIZE) { + DWORD error = GetLastError(); + if (error != NO_ERROR) + FMT_THROW(windows_error(GetLastError(), "cannot get file size")); + } + unsigned long long long_size = size_upper; + return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; +# else + using Stat = struct stat; + Stat file_stat = Stat(); + if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) + FMT_THROW(system_error(errno, "cannot get file attributes")); + static_assert(sizeof(long long) >= sizeof(file_stat.st_size), + "return type of file::size is not large enough"); + return file_stat.st_size; +# endif +} + +std::size_t file::read(void* buffer, std::size_t count) { + rwresult result = 0; + FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); + if (result < 0) FMT_THROW(system_error(errno, "cannot read from file")); + return detail::to_unsigned(result); +} + +std::size_t file::write(const void* buffer, std::size_t count) { + rwresult result = 0; + FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); + if (result < 0) FMT_THROW(system_error(errno, "cannot write to file")); + return detail::to_unsigned(result); +} + +file file::dup(int fd) { + // Don't retry as dup doesn't return EINTR. + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html + int new_fd = FMT_POSIX_CALL(dup(fd)); + if (new_fd == -1) + FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd)); + return file(new_fd); +} + +void file::dup2(int fd) { + int result = 0; + FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); + if (result == -1) { + FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}", + fd_, fd)); + } +} + +void file::dup2(int fd, std::error_code& ec) noexcept { + int result = 0; + FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); + if (result == -1) ec = std::error_code(errno, std::generic_category()); +} + +void file::pipe(file& read_end, file& write_end) { + // Close the descriptors first to make sure that assignments don't throw + // and there are no leaks. + read_end.close(); + write_end.close(); + int fds[2] = {}; +# ifdef _WIN32 + // Make the default pipe capacity same as on Linux 2.6.11+. + enum { DEFAULT_CAPACITY = 65536 }; + int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); +# else + // Don't retry as the pipe function doesn't return EINTR. + // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html + int result = FMT_POSIX_CALL(pipe(fds)); +# endif + if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe")); + // The following assignments don't throw because read_fd and write_fd + // are closed. + read_end = file(fds[0]); + write_end = file(fds[1]); +} + +buffered_file file::fdopen(const char* mode) { +// Don't retry as fdopen doesn't return EINTR. +# if defined(__MINGW32__) && defined(_POSIX_) + FILE* f = ::fdopen(fd_, mode); +# else + FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode)); +# endif + if (!f) + FMT_THROW( + system_error(errno, "cannot associate stream with file descriptor")); + buffered_file bf(f); + fd_ = -1; + return bf; +} + +long getpagesize() { +# ifdef _WIN32 + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwPageSize; +# else + long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); + if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size")); + return size; +# endif +} + +FMT_API void ostream::grow(size_t) { + if (this->size() == this->capacity()) flush(); +} +#endif // FMT_USE_FCNTL +FMT_END_NAMESPACE diff --git a/src/fmt/support/Android.mk b/src/fmt/support/Android.mk new file mode 100644 index 000000000..84a3e32f0 --- /dev/null +++ b/src/fmt/support/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := fmt_static +LOCAL_MODULE_FILENAME := libfmt + +LOCAL_SRC_FILES := ../src/format.cc + +LOCAL_C_INCLUDES := $(LOCAL_PATH) +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_CFLAGS += -std=c++11 -fexceptions + +include $(BUILD_STATIC_LIBRARY) + diff --git a/src/fmt/support/AndroidManifest.xml b/src/fmt/support/AndroidManifest.xml new file mode 100644 index 000000000..b5281fee1 --- /dev/null +++ b/src/fmt/support/AndroidManifest.xml @@ -0,0 +1 @@ +<manifest package="net.fmtlib" /> diff --git a/src/fmt/support/C++.sublime-syntax b/src/fmt/support/C++.sublime-syntax new file mode 100644 index 000000000..9dfb5cbe4 --- /dev/null +++ b/src/fmt/support/C++.sublime-syntax @@ -0,0 +1,2061 @@ +%YAML 1.2 +--- +# http://www.sublimetext.com/docs/3/syntax.html +name: C++ (fmt) +comment: I don't think anyone uses .hp. .cp tends to be paired with .h. (I could be wrong. :) -- chris +file_extensions: + - cpp + - cc + - cp + - cxx + - c++ + - C + - h + - hh + - hpp + - hxx + - h++ + - inl + - ipp +first_line_match: '-\*- C\+\+ -\*-' +scope: source.c++ +variables: + identifier: \b[[:alpha:]_][[:alnum:]_]*\b # upper and lowercase + macro_identifier: \b[[:upper:]_][[:upper:][:digit:]_]{2,}\b # only uppercase, at least 3 chars + path_lookahead: '(?:::\s*)?(?:{{identifier}}\s*::\s*)*(?:template\s+)?{{identifier}}' + operator_method_name: '\boperator\s*(?:[-+*/%^&|~!=<>]|[-+*/%^&|=!<>]=|<<=?|>>=?|&&|\|\||\+\+|--|,|->\*?|\(\)|\[\]|""\s*{{identifier}})' + casts: 'const_cast|dynamic_cast|reinterpret_cast|static_cast' + operator_keywords: 'and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|xor|xor_eq|noexcept' + control_keywords: 'break|case|catch|continue|default|do|else|for|goto|if|_Pragma|return|switch|throw|try|while' + memory_operators: 'new|delete' + basic_types: 'asm|__asm__|auto|bool|_Bool|char|_Complex|double|float|_Imaginary|int|long|short|signed|unsigned|void' + before_tag: 'struct|union|enum\s+class|enum\s+struct|enum|class' + declspec: '__declspec\(\s*\w+(?:\([^)]+\))?\s*\)' + storage_classes: 'static|export|extern|friend|explicit|virtual|register|thread_local' + type_qualifier: 'const|constexpr|mutable|typename|volatile' + compiler_directive: 'inline|restrict|__restrict__|__restrict' + visibility_modifiers: 'private|protected|public' + other_keywords: 'typedef|nullptr|{{visibility_modifiers}}|static_assert|sizeof|using|typeid|alignof|alignas|namespace|template' + modifiers: '{{storage_classes}}|{{type_qualifier}}|{{compiler_directive}}' + non_angle_brackets: '(?=<<|<=)' + + regular: '[^(){}&;*^%=<>-]*' + paren_open: (?:\( + paren_close: '\))?' + generic_open: (?:< + generic_close: '>)?' + balance_parentheses: '{{regular}}{{paren_open}}{{regular}}{{paren_close}}{{regular}}' + generic_lookahead: <{{regular}}{{generic_open}}{{regular}}{{generic_open}}{{regular}}{{generic_close}}\s*{{generic_close}}{{balance_parentheses}}> + + data_structures_forward_decl_lookahead: '(\s+{{macro_identifier}})*\s*(:\s*({{path_lookahead}}|{{visibility_modifiers}}|,|\s|<[^;]*>)+)?;' + non_func_keywords: 'if|for|switch|while|decltype|sizeof|__declspec|__attribute__|typeid|alignof|alignas|static_assert' + + format_spec: |- + (?x: + (?:.? [<>=^])? # fill align + [ +-]? # sign + \#? # alternate form + # technically, octal and hexadecimal integers are also supported as 'width', but rarely used + \d* # width + ,? # thousands separator + (?:\.\d+)? # precision + [bcdeEfFgGnosxX%]? # type + ) + +contexts: + main: + - include: preprocessor-global + - include: global + + ############################################################################# + # Reusable contexts + # + # The follow contexts are currently constructed to be reused in the + # Objetive-C++ syntax. They are specifically constructed to not push into + # sub-contexts, which ensures that Objective-C++ code isn't accidentally + # lexed as plain C++. + # + # The "unique-*" contexts are additions that C++ makes over C, and thus can + # be directly reused in Objective-C++ along with contexts from Objective-C + # and C. + ############################################################################# + + unique-late-expressions: + # This is highlighted after all of the other control keywords + # to allow operator overloading to be lexed properly + - match: \boperator\b + scope: keyword.control.c++ + + unique-modifiers: + - match: \b({{modifiers}})\b + scope: storage.modifier.c++ + + unique-variables: + - match: \bthis\b + scope: variable.language.c++ + # common C++ instance var naming idiom -- fMemberName + - match: '\b(f|m)[[:upper:]]\w*\b' + scope: variable.other.readwrite.member.c++ + # common C++ instance var naming idiom -- m_member_name + - match: '\bm_[[:alnum:]_]+\b' + scope: variable.other.readwrite.member.c++ + + unique-constants: + - match: \bnullptr\b + scope: constant.language.c++ + + unique-keywords: + - match: \busing\b + scope: keyword.control.c++ + - match: \bbreak\b + scope: keyword.control.flow.break.c++ + - match: \bcontinue\b + scope: keyword.control.flow.continue.c++ + - match: \bgoto\b + scope: keyword.control.flow.goto.c++ + - match: \breturn\b + scope: keyword.control.flow.return.c++ + - match: \bthrow\b + scope: keyword.control.flow.throw.c++ + - match: \b({{control_keywords}})\b + scope: keyword.control.c++ + - match: '\bdelete\b(\s*\[\])?|\bnew\b(?!])' + scope: keyword.control.c++ + - match: \b({{operator_keywords}})\b + scope: keyword.operator.word.c++ + + unique-types: + - match: \b(char16_t|char32_t|wchar_t|nullptr_t)\b + scope: storage.type.c++ + - match: \bclass\b + scope: storage.type.c++ + + unique-strings: + - match: '((?:L|u8|u|U)?R)("([^\(\)\\ ]{0,16})\()' + captures: + 1: storage.type.string.c++ + 2: punctuation.definition.string.begin.c++ + push: + - meta_scope: string.quoted.double.c++ + - match: '\)\3"' + scope: punctuation.definition.string.end.c++ + pop: true + - match: '\{\{|\}\}' + scope: constant.character.escape.c++ + - include: formatting-syntax + + unique-numbers: + - match: |- + (?x) + (?: + # floats + (?: + (?:\b\d(?:[\d']*\d)?\.\d(?:[\d']*\d)?|\B\.\d(?:[\d']*\d)?)(?:[Ee][+-]?\d(?:[\d']*\d)?)?(?:[fFlL]|(?:i[fl]?|h|min|[mun]?s|_\w*))?\b + | + (?:\b\d(?:[\d']*\d)?\.)(?:\B|(?:[fFlL]|(?:i[fl]?|h|min|[mun]?s|_\w*))\b|(?:[Ee][+-]?\d(?:[\d']*\d)?)(?:[fFlL]|(?:i[fl]?|h|min|[mun]?s|_\w*))?\b) + | + \b\d(?:[\d']*\d)?(?:[Ee][+-]?\d(?:[\d']*\d)?)(?:[fFlL]|(?:i[fl]?|h|min|[mun]?s|_\w*))?\b + ) + | + # ints + \b(?: + (?: + # dec + [1-9](?:[\d']*\d)? + | + # oct + 0(?:[0-7']*[0-7])? + | + # hex + 0[Xx][\da-fA-F](?:[\da-fA-F']*[\da-fA-F])? + | + # bin + 0[Bb][01](?:[01']*[01])? + ) + # int suffixes + (?:(?:l{1,2}|L{1,2})[uU]?|[uU](?:l{0,2}|L{0,2})|(?:i[fl]?|h|min|[mun]?s|_\w*))?)\b + ) + (?!\.) # Number must not be followed by a decimal point + scope: constant.numeric.c++ + + identifiers: + - match: '{{identifier}}\s*(::)\s*' + captures: + 1: punctuation.accessor.c++ + - match: '(?:(::)\s*)?{{identifier}}' + captures: + 1: punctuation.accessor.c++ + + function-specifiers: + - match: \b(const|final|noexcept|override)\b + scope: storage.modifier.c++ + + ############################################################################# + # The following are C++-specific contexts that should not be reused. This is + # because they push into subcontexts and use variables that are C++-specific. + ############################################################################# + + ## Common context layout + + global: + - match: '(?=\btemplate\b)' + push: + - include: template + - match: (?=\S) + set: global-modifier + - include: namespace + - include: keywords-angle-brackets + - match: '(?={{path_lookahead}}\s*<)' + push: global-modifier + # Take care of comments just before a function definition. + - match: /\* + scope: punctuation.definition.comment.c + push: + - - match: \s*(?=\w) + set: global-modifier + - match: "" + pop: true + - - meta_scope: comment.block.c + - match: \*/ + scope: punctuation.definition.comment.c + pop: true + - include: early-expressions + - match: ^\s*\b(extern)(?=\s+"C(\+\+)?") + scope: storage.modifier.c++ + push: + - include: comments + - include: strings + - match: '\{' + scope: punctuation.section.block.begin.c++ + set: + - meta_scope: meta.extern-c.c++ + - match: '^\s*(#\s*ifdef)\s*__cplusplus\s*' + scope: meta.preprocessor.c++ + captures: + 1: keyword.control.import.c++ + set: + - match: '\}' + scope: punctuation.section.block.end.c++ + pop: true + - include: preprocessor-global + - include: global + - match: '\}' + scope: punctuation.section.block.end.c++ + pop: true + - include: preprocessor-global + - include: global + - match: (?=\S) + set: global-modifier + - match: ^\s*(?=\w) + push: global-modifier + - include: late-expressions + + statements: + - include: preprocessor-statements + - include: scope:source.c#label + - include: expressions + + expressions: + - include: early-expressions + - include: late-expressions + + early-expressions: + - include: early-expressions-before-generic-type + - include: generic-type + - include: early-expressions-after-generic-type + + early-expressions-before-generic-type: + - include: preprocessor-expressions + - include: comments + - include: case-default + - include: typedef + - include: keywords-angle-brackets + - include: keywords-parens + - include: keywords + - include: numbers + # Prevent a '<' from getting scoped as the start of another template + # parameter list, if in reality a less-than-or-equals sign is meant. + - match: <= + scope: keyword.operator.comparison.c + + early-expressions-after-generic-type: + - include: members-arrow + - include: operators + - include: members-dot + - include: strings + - include: parens + - include: brackets + - include: block + - include: variables + - include: constants + - match: ',' + scope: punctuation.separator.c++ + - match: '\)|\}' + scope: invalid.illegal.stray-bracket-end.c++ + + expressions-minus-generic-type: + - include: early-expressions-before-generic-type + - include: angle-brackets + - include: early-expressions-after-generic-type + - include: late-expressions + + expressions-minus-generic-type-function-call: + - include: early-expressions-before-generic-type + - include: angle-brackets + - include: early-expressions-after-generic-type + - include: late-expressions-before-function-call + - include: identifiers + - match: ';' + scope: punctuation.terminator.c++ + + late-expressions: + - include: late-expressions-before-function-call + - include: function-call + - include: identifiers + - match: ';' + scope: punctuation.terminator.c++ + + late-expressions-before-function-call: + - include: unique-late-expressions + - include: modifiers-parens + - include: modifiers + - include: types + + expressions-minus-function-call: + - include: early-expressions + - include: late-expressions-before-function-call + - include: identifiers + - match: ';' + scope: punctuation.terminator.c++ + + comments: + - include: scope:source.c#comments + + operators: + - include: scope:source.c#operators + + modifiers: + - include: unique-modifiers + - include: scope:source.c#modifiers + + variables: + - include: unique-variables + - include: scope:source.c#variables + + constants: + - include: unique-constants + - include: scope:source.c#constants + + keywords: + - include: unique-keywords + - include: scope:source.c#keywords + + types: + - include: unique-types + - include: types-parens + - include: scope:source.c#types + + strings: + - include: unique-strings + - match: '(L|u8|u|U)?(")' + captures: + 1: storage.type.string.c++ + 2: punctuation.definition.string.begin.c++ + push: + - meta_scope: string.quoted.double.c++ + - match: '"' + scope: punctuation.definition.string.end.c++ + pop: true + - include: scope:source.c#string_escaped_char + - match: |- + (?x)% + (\d+\$)? # field (argument #) + [#0\- +']* # flags + [,;:_]? # separator character (AltiVec) + ((-?\d+)|\*(-?\d+\$)?)? # minimum field width + (\.((-?\d+)|\*(-?\d+\$)?)?)? # precision + (hh|h|ll|l|j|t|z|q|L|vh|vl|v|hv|hl)? # length modifier + (\[[^\]]+\]|[am]s|[diouxXDOUeEfFgGaACcSspn%]) # conversion type + scope: constant.other.placeholder.c++ + - match: '\{\{|\}\}' + scope: constant.character.escape.c++ + - include: formatting-syntax + - include: scope:source.c#strings + + formatting-syntax: + # https://docs.python.org/3.6/library/string.html#formatstrings + - match: |- # simple form + (?x) + (\{) + (?: [\w.\[\]]+)? # field_name + ( ! [ars])? # conversion + ( : (?:{{format_spec}}| # format_spec OR + [^}%]*%.[^}]*) # any format-like string + )? + (\}) + scope: constant.other.placeholder.c++ + captures: + 1: punctuation.definition.placeholder.begin.c++ + 2: storage.modifier.c++onversion.c++ + 3: constant.other.format-spec.c++ + 4: punctuation.definition.placeholder.end.c++ + - match: \{(?=[^\}"']+\{[^"']*\}) # complex (nested) form + scope: punctuation.definition.placeholder.begin.c++ + push: + - meta_scope: constant.other.placeholder.c++ + - match: \} + scope: punctuation.definition.placeholder.end.c++ + pop: true + - match: '[\w.\[\]]+' + - match: '![ars]' + scope: storage.modifier.conversion.c++ + - match: ':' + push: + - meta_scope: meta.format-spec.c++ constant.other.format-spec.c++ + - match: (?=\}) + pop: true + - include: formatting-syntax + + numbers: + - include: unique-numbers + - include: scope:source.c#numbers + + ## C++-specific contexts + + case-default: + - match: '\b(default|case)\b' + scope: keyword.control.c++ + push: + - match: (?=[);,]) + pop: true + - match: ':' + scope: punctuation.separator.c++ + pop: true + - include: expressions + + modifiers-parens: + - match: '\b(alignas)\b\s*(\()' + captures: + 1: storage.modifier.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push: + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + - match: \b(__attribute__)\s*(\(\() + captures: + 1: storage.modifier.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push : + - meta_scope: meta.attribute.c++ + - meta_content_scope: meta.group.c++ + - include: parens + - include: strings + - match: \)\) + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - match: \b(__declspec)(\() + captures: + 1: storage.modifier.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push: + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - match: '\b(align|allocate|code_seg|deprecated|property|uuid)\b\s*(\()' + captures: + 1: storage.modifier.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push: + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: numbers + - include: strings + - match: \b(get|put)\b + scope: variable.parameter.c++ + - match: ',' + scope: punctuation.separator.c++ + - match: '=' + scope: keyword.operator.assignment.c++ + - match: '\b(appdomain|deprecated|dllimport|dllexport|jintrinsic|naked|noalias|noinline|noreturn|nothrow|novtable|process|restrict|safebuffers|selectany|thread)\b' + scope: constant.other.c++ + + types-parens: + - match: '\b(decltype)\b\s*(\()' + captures: + 1: storage.type.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push: + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + + keywords-angle-brackets: + - match: \b({{casts}})\b\s* + scope: keyword.operator.word.cast.c++ + push: + - match: '>' + scope: punctuation.section.generic.end.c++ + pop: true + - match: '<' + scope: punctuation.section.generic.begin.c++ + push: + - match: '(?=>)' + pop: true + - include: expressions-minus-generic-type-function-call + + keywords-parens: + - match: '\b(alignof|typeid|static_assert|sizeof)\b\s*(\()' + captures: + 1: keyword.operator.word.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push: + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + + namespace: + - match: '\b(using)\s+(namespace)\s+(?={{path_lookahead}})' + captures: + 1: keyword.control.c++ + 2: keyword.control.c++ + push: + - include: identifiers + - match: '' + pop: true + - match: '\b(namespace)\s+(?=({{path_lookahead}})?(?!\s*[;,]))' + scope: meta.namespace.c++ + captures: + 1: keyword.control.c++ + push: + - meta_content_scope: meta.namespace.c++ entity.name.namespace.c++ + - include: identifiers + - match: '' + set: + - meta_scope: meta.namespace.c++ + - include: comments + - match: '=' + scope: keyword.operator.alias.c++ + - match: '(?=;)' + pop: true + - match: '\}' + scope: meta.block.c++ punctuation.section.block.end.c++ + pop: true + - match: '\{' + scope: punctuation.section.block.begin.c++ + push: + - meta_scope: meta.block.c++ + - match: '(?=\})' + pop: true + - include: preprocessor-global + - include: global + - include: expressions + + template-common: + # Exit the template scope if we hit some basic invalid characters. This + # helps when a user is in the middle of typing their template types and + # prevents re-highlighting the whole file until the next > is found. + - match: (?=[{};]) + pop: true + - include: expressions + + template: + - match: \btemplate\b + scope: storage.type.template.c++ + push: + - meta_scope: meta.template.c++ + # Explicitly include comments here at the top, in order to NOT match the + # \S lookahead in the case of comments. + - include: comments + - match: < + scope: punctuation.section.generic.begin.c++ + set: + - meta_content_scope: meta.template.c++ + - match: '>' + scope: meta.template.c++ punctuation.section.generic.end.c++ + pop: true + - match: \.{3} + scope: keyword.operator.variadic.c++ + - match: \b(typename|{{before_tag}})\b + scope: storage.type.c++ + - include: template # include template here for nested templates + - include: template-common + - match: (?=\S) + set: + - meta_content_scope: meta.template.c++ + - match: \b({{before_tag}})\b + scope: storage.type.c++ + - include: template-common + + generic-type: + - match: '(?=(?!template){{path_lookahead}}\s*{{generic_lookahead}}\s*\()' + push: + - meta_scope: meta.function-call.c++ + - match: \btemplate\b + scope: storage.type.template.c++ + - match: '(?:(::)\s*)?{{identifier}}\s*(::)\s*' + captures: + 1: punctuation.accessor.double-colon.c++ + 2: punctuation.accessor.double-colon.c++ + - match: (?:(::)\s*)?({{identifier}})\s*(<) + captures: + 1: punctuation.accessor.double-colon.c++ + 2: variable.function.c++ + 3: punctuation.section.generic.begin.c++ + push: + - match: '>' + scope: punctuation.section.generic.end.c++ + pop: true + - include: expressions-minus-generic-type-function-call + - match: (?:(::)\s*)?({{identifier}})\s*(\() + captures: + 1: punctuation.accessor.double-colon.c++ + 2: variable.function.c++ + 3: punctuation.section.group.begin.c++ + set: + - meta_scope: meta.function-call.c++ + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + - include: angle-brackets + - match: '\(' + scope: meta.group.c++ punctuation.section.group.begin.c++ + set: + - meta_scope: meta.function-call.c++ + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + - match: '(?=(?!template){{path_lookahead}}\s*{{generic_lookahead}})' + push: + - include: identifiers + - match: '<' + scope: punctuation.section.generic.begin.c++ + set: + - match: '>' + scope: punctuation.section.generic.end.c++ + pop: true + - include: expressions-minus-generic-type-function-call + + angle-brackets: + - match: '<(?!<)' + scope: punctuation.section.generic.begin.c++ + push: + - match: '>' + scope: punctuation.section.generic.end.c++ + pop: true + - include: expressions-minus-generic-type-function-call + + block: + - match: '\{' + scope: punctuation.section.block.begin.c++ + push: + - meta_scope: meta.block.c++ + - match: (?=^\s*#\s*(elif|else|endif)\b) + pop: true + - match: '\}' + scope: punctuation.section.block.end.c++ + pop: true + - include: statements + + function-call: + - match: (?={{path_lookahead}}\s*\() + push: + - meta_scope: meta.function-call.c++ + - include: scope:source.c#c99 + - match: '(?:(::)\s*)?{{identifier}}\s*(::)\s*' + scope: variable.function.c++ + captures: + 1: punctuation.accessor.c++ + 2: punctuation.accessor.c++ + - match: '(?:(::)\s*)?{{identifier}}' + scope: variable.function.c++ + captures: + 1: punctuation.accessor.c++ + - match: '\(' + scope: meta.group.c++ punctuation.section.group.begin.c++ + set: + - meta_content_scope: meta.function-call.c++ meta.group.c++ + - match: '\)' + scope: meta.function-call.c++ meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + + members-inside-function-call: + - meta_content_scope: meta.method-call.c++ meta.group.c++ + - match: \) + scope: meta.method-call.c++ meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + + members-after-accessor-junction: + # After we've seen an accessor (dot or arrow), this context decides what + # kind of entity we're accessing. + - include: comments + - match: \btemplate\b + scope: meta.method-call.c++ storage.type.template.c++ + # Guaranteed to be a template member function call after we match this + set: + - meta_content_scope: meta.method-call.c++ + - include: comments + - match: '{{identifier}}' + scope: variable.function.member.c++ + set: + - meta_content_scope: meta.method-call.c++ + - match: \( + scope: meta.group.c++ punctuation.section.group.begin.c++ + set: members-inside-function-call + - include: comments + - include: angle-brackets + - match: (?=\S) # safety pop + pop: true + - match: (?=\S) # safety pop + pop: true + # Operator overloading + - match: '({{operator_method_name}})\s*(\()' + captures: + 0: meta.method-call.c++ + 1: variable.function.member.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + set: members-inside-function-call + # Non-templated member function call + - match: (~?{{identifier}})\s*(\() + captures: + 0: meta.method-call.c++ + 1: variable.function.member.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + set: members-inside-function-call + # Templated member function call + - match: (~?{{identifier}})\s*(?={{generic_lookahead}}) + captures: + 1: variable.function.member.c++ + set: + - meta_scope: meta.method-call.c++ + - match: < + scope: punctuation.section.generic.begin.c++ + set: + - meta_content_scope: meta.method-call.c++ + - match: '>' + scope: punctuation.section.generic.end.c++ + set: + - meta_content_scope: meta.method-call.c++ + - include: comments + - match: \( + scope: punctuation.section.group.begin.c++ + set: members-inside-function-call + - match: (?=\S) # safety pop + pop: true + - include: expressions + # Explicit base-class access + - match: ({{identifier}})\s*(::) + captures: + 1: variable.other.base-class.c++ + 2: punctuation.accessor.double-colon.c++ + set: members-after-accessor-junction # reset + # Just a regular member variable + - match: '{{identifier}}' + scope: variable.other.readwrite.member.c++ + pop: true + + members-dot: + - include: scope:source.c#access-illegal + # No lookahead required because members-dot goes after operators in the + # early-expressions-after-generic-type context. This means triple dots + # (i.e. "..." or "variadic") is attempted first. + - match: \. + scope: punctuation.accessor.dot.c++ + push: members-after-accessor-junction + + members-arrow: + # This needs to be before operators in the + # early-expressions-after-generic-type context because otherwise the "->" + # from the C language will match. + - match: -> + scope: punctuation.accessor.arrow.c++ + push: members-after-accessor-junction + + typedef: + - match: \btypedef\b + scope: storage.type.c++ + push: + - match: ({{identifier}})?\s*(?=;) + captures: + 1: entity.name.type.typedef.c++ + pop: true + - match: \b(struct)\s+({{identifier}})\b + captures: + 1: storage.type.c++ + - include: expressions-minus-generic-type + + parens: + - match: \( + scope: punctuation.section.group.begin.c++ + push: + - meta_scope: meta.group.c++ + - match: \) + scope: punctuation.section.group.end.c++ + pop: true + - include: expressions + + brackets: + - match: \[ + scope: punctuation.section.brackets.begin.c++ + push: + - meta_scope: meta.brackets.c++ + - match: \] + scope: punctuation.section.brackets.end.c++ + pop: true + - include: expressions + + function-trailing-return-type: + - match: '{{non_angle_brackets}}' + pop: true + - include: angle-brackets + - include: types + - include: modifiers-parens + - include: modifiers + - include: identifiers + - match: \*|& + scope: keyword.operator.c++ + - include: function-trailing-return-type-parens + - match: '(?=\S)' + pop: true + + function-trailing-return-type-parens: + - match: \( + scope: punctuation.section.group.begin.c++ + push: + - meta_scope: meta.group.c++ + - match: \) + scope: punctuation.section.group.end.c++ + pop: true + - include: function-trailing-return-type + + ## Detection of function and data structure definitions at the global level + + global-modifier: + - include: comments + - include: modifiers-parens + - include: modifiers + # Constructors and destructors don't have a type + - match: '(?={{path_lookahead}}\s*::\s*{{identifier}}\s*(\(|$))' + set: + - meta_content_scope: meta.function.c++ entity.name.function.constructor.c++ + - include: identifiers + - match: '(?=[^\w\s])' + set: function-definition-params + - match: '(?={{path_lookahead}}\s*::\s*~{{identifier}}\s*(\(|$))' + set: + - meta_content_scope: meta.function.c++ entity.name.function.destructor.c++ + - include: identifiers + - match: '~{{identifier}}' + - match: '(?=[^\w\s])' + set: function-definition-params + # If we see a path ending in :: before a newline, we don't know if it is + # a constructor or destructor, or a long return type, so we are just going + # to treat it like a regular function. Most likely it is a constructor, + # since it doesn't seem most developers would create such a long typename. + - match: '(?={{path_lookahead}}\s*::\s*$)' + set: + - meta_content_scope: meta.function.c++ entity.name.function.c++ + - include: identifiers + - match: '~{{identifier}}' + - match: '(?=[^\w\s])' + set: function-definition-params + - include: unique-strings + - match: '(?=\S)' + set: global-type + + global-type: + - include: comments + - match: \*|& + scope: keyword.operator.c++ + - match: '(?=\b({{control_keywords}}|{{operator_keywords}}|{{casts}}|{{memory_operators}}|{{other_keywords}}|operator)\b)' + pop: true + - match: '(?=\s)' + set: global-maybe-function + # If a class/struct/enum followed by a name that is not a macro or declspec + # then this is likely a return type of a function. This is uncommon. + - match: |- + (?x: + ({{before_tag}}) + \s+ + (?= + (?![[:upper:][:digit:]_]+\b|__declspec|{{before_tag}}) + {{path_lookahead}} + (\s+{{identifier}}\s*\(|\s*[*&]) + ) + ) + captures: + 1: storage.type.c++ + set: + - include: identifiers + - match: '' + set: global-maybe-function + # The previous match handles return types of struct/enum/etc from a func, + # there this one exits the context to allow matching an actual struct/class + - match: '(?=\b({{before_tag}})\b)' + set: data-structures + - match: '(?=\b({{casts}})\b\s*<)' + pop: true + - match: '{{non_angle_brackets}}' + pop: true + - include: angle-brackets + - include: types + # Allow a macro call + - match: '({{identifier}})\s*(\()(?=[^\)]+\))' + captures: + 1: variable.function.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push: + - meta_scope: meta.function-call.c++ + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + - match: '(?={{path_lookahead}}\s*\()' + set: + - include: function-call + - match: '' + pop: true + - include: variables + - include: constants + - include: identifiers + - match: (?=\W) + pop: true + + global-maybe-function: + - include: comments + # Consume pointer info, macros and any type info that was offset by macros + - match: \*|& + scope: keyword.operator.c++ + - match: '(?=\b({{control_keywords}}|{{operator_keywords}}|{{casts}}|{{memory_operators}}|{{other_keywords}})\b)' + pop: true + - match: '\b({{type_qualifier}})\b' + scope: storage.modifier.c++ + - match: '{{non_angle_brackets}}' + pop: true + - include: angle-brackets + - include: types + - include: modifiers-parens + - include: modifiers + # All uppercase identifier just before a newline is most likely a macro + - match: '[[:upper:][:digit:]_]+\s*$' + # Operator overloading + - match: '(?=({{path_lookahead}}\s*(?:{{generic_lookahead}})?::\s*)?{{operator_method_name}}\s*(\(|$))' + set: + - meta_content_scope: meta.function.c++ entity.name.function.c++ + - include: identifiers + - match: '(?=\s*(\(|$))' + set: function-definition-params + # Identifier that is not the function name - likely a macro or type + - match: '(?={{path_lookahead}}([ \t]+|[*&])(?!\s*(<|::|\(|$)))' + push: + - include: identifiers + - match: '' + pop: true + # Real function definition + - match: '(?={{path_lookahead}}({{generic_lookahead}}({{path_lookahead}})?)\s*(\(|$))' + set: [function-definition-params, global-function-identifier-generic] + - match: '(?={{path_lookahead}}\s*(\(|$))' + set: [function-definition-params, global-function-identifier] + - match: '(?={{path_lookahead}}\s*::\s*$)' + set: [function-definition-params, global-function-identifier] + - match: '(?=\S)' + pop: true + + global-function-identifier-generic: + - include: angle-brackets + - match: '::' + scope: punctuation.accessor.c++ + - match: '(?={{identifier}}<.*>\s*\()' + push: + - meta_content_scope: entity.name.function.c++ + - include: identifiers + - match: '(?=<)' + pop: true + - match: '(?={{identifier}}\s*\()' + push: + - meta_content_scope: entity.name.function.c++ + - include: identifiers + - match: '' + pop: true + - match: '(?=\()' + pop: true + + global-function-identifier: + - meta_content_scope: entity.name.function.c++ + - include: identifiers + - match: '(?=\S)' + pop: true + + function-definition-params: + - meta_content_scope: meta.function.c++ + - include: comments + - match: '(?=\()' + set: + - match: \( + scope: meta.function.parameters.c++ meta.group.c++ punctuation.section.group.begin.c++ + set: + - meta_content_scope: meta.function.parameters.c++ meta.group.c++ + - match : \) + scope: punctuation.section.group.end.c++ + set: function-definition-continue + - match: '\bvoid\b' + scope: storage.type.c++ + - match: '{{identifier}}(?=\s*(\[|,|\)|=))' + scope: variable.parameter.c++ + - match: '=' + scope: keyword.operator.assignment.c++ + push: + - match: '(?=,|\))' + pop: true + - include: expressions-minus-generic-type + - include: scope:source.c#preprocessor-line-continuation + - include: expressions-minus-generic-type + - include: scope:source.c#preprocessor-line-continuation + - match: (?=\S) + pop: true + + function-definition-continue: + - meta_content_scope: meta.function.c++ + - include: comments + - match: '(?=;)' + pop: true + - match: '->' + scope: punctuation.separator.c++ + set: function-definition-trailing-return + - include: function-specifiers + - match: '=' + scope: keyword.operator.assignment.c++ + - match: '&' + scope: keyword.operator.c++ + - match: \b0\b + scope: constant.numeric.c++ + - match: \b(default|delete)\b + scope: storage.modifier.c++ + - match: '(?=\{)' + set: function-definition-body + - match: '(?=\S)' + pop: true + + function-definition-trailing-return: + - include: comments + - match: '(?=;)' + pop: true + - match: '(?=\{)' + set: function-definition-body + - include: function-specifiers + - include: function-trailing-return-type + + function-definition-body: + - meta_content_scope: meta.function.c++ meta.block.c++ + - match: '\{' + scope: punctuation.section.block.begin.c++ + set: + - meta_content_scope: meta.function.c++ meta.block.c++ + - match: '\}' + scope: meta.function.c++ meta.block.c++ punctuation.section.block.end.c++ + pop: true + - match: (?=^\s*#\s*(elif|else|endif)\b) + pop: true + - match: '(?=({{before_tag}})([^(;]+$|.*\{))' + push: data-structures + - include: statements + + ## Data structures including classes, structs, unions and enums + + data-structures: + - match: '\bclass\b' + scope: storage.type.c++ + set: data-structures-class-definition + # Detect variable type definitions using struct/enum/union followed by a tag + - match: '\b({{before_tag}})(?=\s+{{path_lookahead}}\s+{{path_lookahead}}\s*[=;\[])' + scope: storage.type.c++ + - match: '\bstruct\b' + scope: storage.type.c++ + set: data-structures-struct-definition + - match: '\benum(\s+(class|struct))?\b' + scope: storage.type.c++ + set: data-structures-enum-definition + - match: '\bunion\b' + scope: storage.type.c++ + set: data-structures-union-definition + - match: '(?=\S)' + pop: true + + preprocessor-workaround-eat-macro-before-identifier: + # Handle macros so they aren't matched as the class name + - match: ({{macro_identifier}})(?=\s+~?{{identifier}}) + captures: + 1: meta.assumed-macro.c + + data-structures-class-definition: + - meta_scope: meta.class.c++ + - include: data-structures-definition-common-begin + - match: '{{identifier}}(?={{data_structures_forward_decl_lookahead}})' + scope: entity.name.class.forward-decl.c++ + set: data-structures-class-definition-after-identifier + - match: '{{identifier}}' + scope: entity.name.class.c++ + set: data-structures-class-definition-after-identifier + - match: '(?=[:{])' + set: data-structures-class-definition-after-identifier + - match: '(?=;)' + pop: true + + data-structures-class-definition-after-identifier: + - meta_content_scope: meta.class.c++ + - include: data-structures-definition-common-begin + # No matching of identifiers since they should all be macros at this point + - include: data-structures-definition-common-end + - match: '\{' + scope: meta.block.c++ punctuation.section.block.begin.c++ + set: + - meta_content_scope: meta.class.c++ meta.block.c++ + - match: '\}' + scope: meta.class.c++ meta.block.c++ punctuation.section.block.end.c++ + pop: true + - include: data-structures-body + + data-structures-struct-definition: + - meta_scope: meta.struct.c++ + - include: data-structures-definition-common-begin + - match: '{{identifier}}(?={{data_structures_forward_decl_lookahead}})' + scope: entity.name.struct.forward-decl.c++ + set: data-structures-struct-definition-after-identifier + - match: '{{identifier}}' + scope: entity.name.struct.c++ + set: data-structures-struct-definition-after-identifier + - match: '(?=[:{])' + set: data-structures-struct-definition-after-identifier + - match: '(?=;)' + pop: true + + data-structures-struct-definition-after-identifier: + - meta_content_scope: meta.struct.c++ + - include: data-structures-definition-common-begin + # No matching of identifiers since they should all be macros at this point + - include: data-structures-definition-common-end + - match: '\{' + scope: meta.block.c++ punctuation.section.block.begin.c++ + set: + - meta_content_scope: meta.struct.c++ meta.block.c++ + - match: '\}' + scope: meta.struct.c++ meta.block.c++ punctuation.section.block.end.c++ + pop: true + - include: data-structures-body + + data-structures-enum-definition: + - meta_scope: meta.enum.c++ + - include: data-structures-definition-common-begin + - match: '{{identifier}}(?={{data_structures_forward_decl_lookahead}})' + scope: entity.name.enum.forward-decl.c++ + set: data-structures-enum-definition-after-identifier + - match: '{{identifier}}' + scope: entity.name.enum.c++ + set: data-structures-enum-definition-after-identifier + - match: '(?=[:{])' + set: data-structures-enum-definition-after-identifier + - match: '(?=;)' + pop: true + + data-structures-enum-definition-after-identifier: + - meta_content_scope: meta.enum.c++ + - include: data-structures-definition-common-begin + # No matching of identifiers since they should all be macros at this point + - include: data-structures-definition-common-end + - match: '\{' + scope: meta.block.c++ punctuation.section.block.begin.c++ + set: + - meta_content_scope: meta.enum.c++ meta.block.c++ + # Enums don't support methods so we have a simplified body + - match: '\}' + scope: meta.enum.c++ meta.block.c++ punctuation.section.block.end.c++ + pop: true + - include: statements + + data-structures-union-definition: + - meta_scope: meta.union.c++ + - include: data-structures-definition-common-begin + - match: '{{identifier}}(?={{data_structures_forward_decl_lookahead}})' + scope: entity.name.union.forward-decl.c++ + set: data-structures-union-definition-after-identifier + - match: '{{identifier}}' + scope: entity.name.union.c++ + set: data-structures-union-definition-after-identifier + - match: '(?=[{])' + set: data-structures-union-definition-after-identifier + - match: '(?=;)' + pop: true + + data-structures-union-definition-after-identifier: + - meta_content_scope: meta.union.c++ + - include: data-structures-definition-common-begin + # No matching of identifiers since they should all be macros at this point + # Unions don't support base classes + - include: angle-brackets + - match: '\{' + scope: meta.block.c++ punctuation.section.block.begin.c++ + set: + - meta_content_scope: meta.union.c++ meta.block.c++ + - match: '\}' + scope: meta.union.c++ meta.block.c++ punctuation.section.block.end.c++ + pop: true + - include: data-structures-body + - match: '(?=;)' + pop: true + + data-structures-definition-common-begin: + - include: comments + - match: '(?=\b(?:{{before_tag}}|{{control_keywords}})\b)' + pop: true + - include: preprocessor-other + - include: modifiers-parens + - include: modifiers + - include: preprocessor-workaround-eat-macro-before-identifier + + data-structures-definition-common-end: + - include: angle-brackets + - match: \bfinal\b + scope: storage.modifier.c++ + - match: ':' + scope: punctuation.separator.c++ + push: + - include: comments + - include: preprocessor-other + - include: modifiers-parens + - include: modifiers + - match: '\b(virtual|{{visibility_modifiers}})\b' + scope: storage.modifier.c++ + - match: (?={{path_lookahead}}) + push: + - meta_scope: entity.other.inherited-class.c++ + - include: identifiers + - match: '' + pop: true + - include: angle-brackets + - match: ',' + scope: punctuation.separator.c++ + - match: (?=\{|;) + pop: true + - match: '(?=;)' + pop: true + + data-structures-body: + - include: preprocessor-data-structures + - match: '(?=\btemplate\b)' + push: + - include: template + - match: (?=\S) + set: data-structures-modifier + - include: typedef + - match: \b({{visibility_modifiers}})\s*(:)(?!:) + captures: + 1: storage.modifier.c++ + 2: punctuation.section.class.c++ + - match: '^\s*(?=(?:~?\w+|::))' + push: data-structures-modifier + - include: expressions-minus-generic-type + + data-structures-modifier: + - match: '\bfriend\b' + scope: storage.modifier.c++ + push: + - match: (?=;) + pop: true + - match: '\{' + scope: punctuation.section.block.begin.c++ + set: + - meta_scope: meta.block.c++ + - match: '\}' + scope: punctuation.section.block.end.c++ + pop: true + - include: statements + - match: '\b({{before_tag}})\b' + scope: storage.type.c++ + - include: expressions-minus-function-call + - include: comments + - include: modifiers-parens + - include: modifiers + - match: '\bstatic_assert(?=\s*\()' + scope: meta.static-assert.c++ keyword.operator.word.c++ + push: + - match: '\(' + scope: meta.group.c++ punctuation.section.group.begin.c++ + set: + - meta_content_scope: meta.function-call.c++ meta.group.c++ + - match: '\)' + scope: meta.function-call.c++ meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + # Destructor + - match: '(?:{{identifier}}\s*(::)\s*)?~{{identifier}}(?=\s*(\(|$))' + scope: meta.method.destructor.c++ entity.name.function.destructor.c++ + captures: + 1: punctuation.accessor.c++ + set: method-definition-params + # It's a macro, not a constructor if there is no type in the first param + - match: '({{identifier}})\s*(\()(?=\s*(?!void){{identifier}}\s*[),])' + captures: + 1: variable.function.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push: + - meta_scope: meta.function-call.c++ + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + # Constructor + - include: preprocessor-workaround-eat-macro-before-identifier + - match: '((?!{{before_tag}}|template){{identifier}})(?=\s*\()' + scope: meta.method.constructor.c++ entity.name.function.constructor.c++ + set: method-definition-params + # Long form constructor + - match: '({{identifier}}\s*(::)\s*{{identifier}})(?=\s*\()' + captures: + 1: meta.method.constructor.c++ entity.name.function.constructor.c++ + 2: punctuation.accessor.c++ + push: method-definition-params + - match: '(?=\S)' + set: data-structures-type + + data-structures-type: + - include: comments + - match: \*|& + scope: keyword.operator.c++ + # Cast methods + - match: '(operator)\s+({{identifier}})(?=\s*(\(|$))' + captures: + 1: keyword.control.c++ + 2: meta.method.c++ entity.name.function.c++ + set: method-definition-params + - match: '(?=\b({{control_keywords}}|{{operator_keywords}}|{{casts}}|{{memory_operators}}|{{other_keywords}}|operator)\b)' + pop: true + - match: '(?=\s)' + set: data-structures-maybe-method + # If a class/struct/enum followed by a name that is not a macro or declspec + # then this is likely a return type of a function. This is uncommon. + - match: |- + (?x: + ({{before_tag}}) + \s+ + (?= + (?![[:upper:][:digit:]_]+\b|__declspec|{{before_tag}}) + {{path_lookahead}} + (\s+{{identifier}}\s*\(|\s*[*&]) + ) + ) + captures: + 1: storage.type.c++ + set: + - include: identifiers + - match: '' + set: data-structures-maybe-method + # The previous match handles return types of struct/enum/etc from a func, + # there this one exits the context to allow matching an actual struct/class + - match: '(?=\b({{before_tag}})\b)' + set: data-structures + - match: '(?=\b({{casts}})\b\s*<)' + pop: true + - match: '{{non_angle_brackets}}' + pop: true + - include: angle-brackets + - include: types + - include: variables + - include: constants + - include: identifiers + - match: (?=[&*]) + set: data-structures-maybe-method + - match: (?=\W) + pop: true + + data-structures-maybe-method: + - include: comments + # Consume pointer info, macros and any type info that was offset by macros + - match: \*|& + scope: keyword.operator.c++ + - match: '(?=\b({{control_keywords}}|{{operator_keywords}}|{{casts}}|{{memory_operators}}|{{other_keywords}})\b)' + pop: true + - match: '\b({{type_qualifier}})\b' + scope: storage.modifier.c++ + - match: '{{non_angle_brackets}}' + pop: true + - include: angle-brackets + - include: types + - include: modifiers-parens + - include: modifiers + # Operator overloading + - match: '{{operator_method_name}}(?=\s*(\(|$))' + scope: meta.method.c++ entity.name.function.c++ + set: method-definition-params + # Identifier that is not the function name - likely a macro or type + - match: '(?={{path_lookahead}}([ \t]+|[*&])(?!\s*(<|::|\()))' + push: + - include: identifiers + - match: '' + pop: true + # Real function definition + - match: '(?={{path_lookahead}}({{generic_lookahead}})\s*(\())' + set: [method-definition-params, data-structures-function-identifier-generic] + - match: '(?={{path_lookahead}}\s*(\())' + set: [method-definition-params, data-structures-function-identifier] + - match: '(?={{path_lookahead}}\s*::\s*$)' + set: [method-definition-params, data-structures-function-identifier] + - match: '(?=\S)' + pop: true + + data-structures-function-identifier-generic: + - include: angle-brackets + - match: '(?={{identifier}})' + push: + - meta_content_scope: entity.name.function.c++ + - include: identifiers + - match: '(?=<)' + pop: true + - match: '(?=\()' + pop: true + + data-structures-function-identifier: + - meta_content_scope: entity.name.function.c++ + - include: identifiers + - match: '(?=\S)' + pop: true + + method-definition-params: + - meta_content_scope: meta.method.c++ + - include: comments + - match: '(?=\()' + set: + - match: \( + scope: meta.method.parameters.c++ meta.group.c++ punctuation.section.group.begin.c++ + set: + - meta_content_scope: meta.method.parameters.c++ meta.group.c++ + - match : \) + scope: punctuation.section.group.end.c++ + set: method-definition-continue + - match: '\bvoid\b' + scope: storage.type.c++ + - match: '{{identifier}}(?=\s*(\[|,|\)|=))' + scope: variable.parameter.c++ + - match: '=' + scope: keyword.operator.assignment.c++ + push: + - match: '(?=,|\))' + pop: true + - include: expressions-minus-generic-type + - include: expressions-minus-generic-type + - match: '(?=\S)' + pop: true + + method-definition-continue: + - meta_content_scope: meta.method.c++ + - include: comments + - match: '(?=;)' + pop: true + - match: '->' + scope: punctuation.separator.c++ + set: method-definition-trailing-return + - include: function-specifiers + - match: '=' + scope: keyword.operator.assignment.c++ + - match: '&' + scope: keyword.operator.c++ + - match: \b0\b + scope: constant.numeric.c++ + - match: \b(default|delete)\b + scope: storage.modifier.c++ + - match: '(?=:)' + set: + - match: ':' + scope: punctuation.separator.initializer-list.c++ + set: + - meta_scope: meta.method.constructor.initializer-list.c++ + - match: '{{identifier}}' + scope: variable.other.readwrite.member.c++ + push: + - match: \( + scope: meta.group.c++ punctuation.section.group.begin.c++ + set: + - meta_content_scope: meta.group.c++ + - match: \) + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + - match: \{ + scope: meta.group.c++ punctuation.section.group.begin.c++ + set: + - meta_content_scope: meta.group.c++ + - match: \} + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + - include: comments + - match: (?=\{|;) + set: method-definition-continue + - include: expressions + - match: '(?=\{)' + set: method-definition-body + - match: '(?=\S)' + pop: true + + method-definition-trailing-return: + - include: comments + - match: '(?=;)' + pop: true + - match: '(?=\{)' + set: method-definition-body + - include: function-specifiers + - include: function-trailing-return-type + + method-definition-body: + - meta_content_scope: meta.method.c++ meta.block.c++ + - match: '\{' + scope: punctuation.section.block.begin.c++ + set: + - meta_content_scope: meta.method.c++ meta.block.c++ + - match: '\}' + scope: meta.method.c++ meta.block.c++ punctuation.section.block.end.c++ + pop: true + - match: (?=^\s*#\s*(elif|else|endif)\b) + pop: true + - match: '(?=({{before_tag}})([^(;]+$|.*\{))' + push: data-structures + - include: statements + + ## Preprocessor for data-structures + + preprocessor-data-structures: + - include: preprocessor-rule-enabled-data-structures + - include: preprocessor-rule-disabled-data-structures + - include: preprocessor-practical-workarounds + + preprocessor-rule-disabled-data-structures: + - match: ^\s*((#if)\s+(0))\b + captures: + 1: meta.preprocessor.c++ + 2: keyword.control.import.c++ + 3: constant.numeric.preprocessor.c++ + push: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: ^\s*(#\s*else)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.else.c++ + push: + - match: (?=^\s*#\s*endif\b) + pop: true + - include: negated-block + - include: data-structures-body + - match: "" + push: + - meta_scope: comment.block.preprocessor.if-branch.c++ + - match: (?=^\s*#\s*(else|endif)\b) + pop: true + - include: scope:source.c#preprocessor-disabled + + preprocessor-rule-enabled-data-structures: + - match: ^\s*((#if)\s+(0*1))\b + captures: + 1: meta.preprocessor.c++ + 2: keyword.control.import.c++ + 3: constant.numeric.preprocessor.c++ + push: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: ^\s*(#\s*else)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.else.c++ + push: + - meta_content_scope: comment.block.preprocessor.else-branch.c++ + - match: (?=^\s*#\s*endif\b) + pop: true + - include: scope:source.c#preprocessor-disabled + - match: "" + push: + - match: (?=^\s*#\s*(else|endif)\b) + pop: true + - include: negated-block + - include: data-structures-body + + ## Preprocessor for global + + preprocessor-global: + - include: preprocessor-rule-enabled-global + - include: preprocessor-rule-disabled-global + - include: preprocessor-rule-other-global + + preprocessor-statements: + - include: preprocessor-rule-enabled-statements + - include: preprocessor-rule-disabled-statements + - include: preprocessor-rule-other-statements + + preprocessor-expressions: + - include: scope:source.c#incomplete-inc + - include: preprocessor-macro-define + - include: scope:source.c#pragma-mark + - include: preprocessor-other + + preprocessor-rule-disabled-global: + - match: ^\s*((#if)\s+(0))\b + captures: + 1: meta.preprocessor.c++ + 2: keyword.control.import.c++ + 3: constant.numeric.preprocessor.c++ + push: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: ^\s*(#\s*else)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.else.c++ + push: + - match: (?=^\s*#\s*endif\b) + pop: true + - include: preprocessor-global + - include: negated-block + - include: global + - match: "" + push: + - meta_scope: comment.block.preprocessor.if-branch.c++ + - match: (?=^\s*#\s*(else|endif)\b) + pop: true + - include: scope:source.c#preprocessor-disabled + + preprocessor-rule-enabled-global: + - match: ^\s*((#if)\s+(0*1))\b + captures: + 1: meta.preprocessor.c++ + 2: keyword.control.import.c++ + 3: constant.numeric.preprocessor.c++ + push: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: ^\s*(#\s*else)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.else.c++ + push: + - meta_content_scope: comment.block.preprocessor.else-branch.c++ + - match: (?=^\s*#\s*endif\b) + pop: true + - include: scope:source.c#preprocessor-disabled + - match: "" + push: + - match: (?=^\s*#\s*(else|endif)\b) + pop: true + - include: preprocessor-global + - include: negated-block + - include: global + + preprocessor-rule-other-global: + - match: ^\s*(#\s*(?:if|ifdef|ifndef))\b + captures: + 1: keyword.control.import.c++ + push: + - meta_scope: meta.preprocessor.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-comments + - match: \bdefined\b + scope: keyword.control.c++ + # Enter a new scope where all elif/else branches have their + # contexts popped by a subsequent elif/else/endif. This ensures that + # preprocessor branches don't push multiple meta.block scopes on + # the stack, thus messing up the "global" context's detection of + # functions. + - match: $\n + set: preprocessor-if-branch-global + + # These gymnastics here ensure that we are properly handling scope even + # when the preprocessor is used to create different scope beginnings, such + # as a different if/while condition + preprocessor-if-branch-global: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: (?=^\s*#\s*(elif|else)\b) + push: preprocessor-elif-else-branch-global + - match: \{ + scope: punctuation.section.block.begin.c++ + set: preprocessor-block-if-branch-global + - include: preprocessor-global + - include: negated-block + - include: global + + preprocessor-block-if-branch-global: + - meta_scope: meta.block.c++ + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + set: preprocessor-block-finish-global + - match: (?=^\s*#\s*(elif|else)\b) + push: preprocessor-elif-else-branch-global + - match: \} + scope: punctuation.section.block.end.c++ + set: preprocessor-if-branch-global + - include: statements + + preprocessor-block-finish-global: + - meta_scope: meta.block.c++ + - match: ^\s*(#\s*(?:if|ifdef|ifndef))\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + set: preprocessor-block-finish-if-branch-global + - match: \} + scope: punctuation.section.block.end.c++ + pop: true + - include: statements + + preprocessor-block-finish-if-branch-global: + - match: ^\s*(#\s*endif)\b + captures: + 1: keyword.control.import.c++ + pop: true + - match: \} + scope: punctuation.section.block.end.c++ + set: preprocessor-if-branch-global + - include: statements + + preprocessor-elif-else-branch-global: + - match: (?=^\s*#\s*(endif)\b) + pop: true + - include: preprocessor-global + - include: negated-block + - include: global + + ## Preprocessor for statements + + preprocessor-rule-disabled-statements: + - match: ^\s*((#if)\s+(0))\b + captures: + 1: meta.preprocessor.c++ + 2: keyword.control.import.c++ + 3: constant.numeric.preprocessor.c++ + push: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: ^\s*(#\s*else)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.else.c++ + push: + - match: (?=^\s*#\s*endif\b) + pop: true + - include: negated-block + - include: statements + - match: "" + push: + - meta_scope: comment.block.preprocessor.if-branch.c++ + - match: (?=^\s*#\s*(else|endif)\b) + pop: true + - include: scope:source.c#preprocessor-disabled + + preprocessor-rule-enabled-statements: + - match: ^\s*((#if)\s+(0*1))\b + captures: + 1: meta.preprocessor.c++ + 2: keyword.control.import.c++ + 3: constant.numeric.preprocessor.c++ + push: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: ^\s*(#\s*else)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.else.c++ + push: + - meta_content_scope: comment.block.preprocessor.else-branch.c++ + - match: (?=^\s*#\s*endif\b) + pop: true + - include: scope:source.c#preprocessor-disabled + - match: "" + push: + - match: (?=^\s*#\s*(else|endif)\b) + pop: true + - include: negated-block + - include: statements + + preprocessor-rule-other-statements: + - match: ^\s*(#\s*(?:if|ifdef|ifndef))\b + captures: + 1: keyword.control.import.c++ + push: + - meta_scope: meta.preprocessor.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-comments + - match: \bdefined\b + scope: keyword.control.c++ + # Enter a new scope where all elif/else branches have their + # contexts popped by a subsequent elif/else/endif. This ensures that + # preprocessor branches don't push multiple meta.block scopes on + # the stack, thus messing up the "global" context's detection of + # functions. + - match: $\n + set: preprocessor-if-branch-statements + + # These gymnastics here ensure that we are properly handling scope even + # when the preprocessor is used to create different scope beginnings, such + # as a different if/while condition + preprocessor-if-branch-statements: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: (?=^\s*#\s*(elif|else)\b) + push: preprocessor-elif-else-branch-statements + - match: \{ + scope: punctuation.section.block.begin.c++ + set: preprocessor-block-if-branch-statements + - match: (?=(?!{{non_func_keywords}}){{path_lookahead}}\s*\() + set: preprocessor-if-branch-function-call + - include: negated-block + - include: statements + + preprocessor-if-branch-function-call: + - meta_content_scope: meta.function-call.c++ + - include: scope:source.c#c99 + - match: '(?:(::)\s*)?{{identifier}}\s*(::)\s*' + scope: variable.function.c++ + captures: + 1: punctuation.accessor.c++ + 2: punctuation.accessor.c++ + - match: '(?:(::)\s*)?{{identifier}}' + scope: variable.function.c++ + captures: + 1: punctuation.accessor.c++ + - match: '\(' + scope: meta.group.c++ punctuation.section.group.begin.c++ + set: preprocessor-if-branch-function-call-arguments + + preprocessor-if-branch-function-call-arguments: + - meta_content_scope: meta.function-call.c++ meta.group.c++ + - match : \) + scope: meta.function-call.c++ meta.group.c++ punctuation.section.group.end.c++ + set: preprocessor-if-branch-statements + - match: ^\s*(#\s*(?:elif|else))\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + set: preprocessor-if-branch-statements + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + set: preprocessor-if-branch-function-call-arguments-finish + - include: expressions + + preprocessor-if-branch-function-call-arguments-finish: + - meta_content_scope: meta.function-call.c++ meta.group.c++ + - match: \) + scope: meta.function-call.c++ meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + + preprocessor-block-if-branch-statements: + - meta_scope: meta.block.c++ + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + set: preprocessor-block-finish-statements + - match: (?=^\s*#\s*(elif|else)\b) + push: preprocessor-elif-else-branch-statements + - match: \} + scope: punctuation.section.block.end.c++ + set: preprocessor-if-branch-statements + - include: statements + + preprocessor-block-finish-statements: + - meta_scope: meta.block.c++ + - match: ^\s*(#\s*(?:if|ifdef|ifndef))\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + set: preprocessor-block-finish-if-branch-statements + - match: \} + scope: punctuation.section.block.end.c++ + pop: true + - include: statements + + preprocessor-block-finish-if-branch-statements: + - match: ^\s*(#\s*endif)\b + captures: + 1: keyword.control.import.c++ + pop: true + - match: \} + scope: meta.block.c++ punctuation.section.block.end.c++ + set: preprocessor-if-branch-statements + - include: statements + + preprocessor-elif-else-branch-statements: + - match: (?=^\s*#\s*endif\b) + pop: true + - include: negated-block + - include: statements + + ## Preprocessor other + + negated-block: + - match: '\}' + scope: punctuation.section.block.end.c++ + push: + - match: '\{' + scope: punctuation.section.block.begin.c++ + pop: true + - match: (?=^\s*#\s*(elif|else|endif)\b) + pop: true + - include: statements + + preprocessor-macro-define: + - match: ^\s*(\#\s*define)\b + captures: + 1: meta.preprocessor.macro.c++ keyword.control.import.define.c++ + push: + - meta_content_scope: meta.preprocessor.macro.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-line-ending + - include: scope:source.c#preprocessor-comments + - match: '({{identifier}})(?=\()' + scope: entity.name.function.preprocessor.c++ + set: + - match: '\(' + scope: punctuation.section.group.begin.c++ + set: preprocessor-macro-params + - match: '{{identifier}}' + scope: entity.name.constant.preprocessor.c++ + set: preprocessor-macro-definition + + preprocessor-macro-params: + - meta_scope: meta.preprocessor.macro.parameters.c++ meta.group.c++ + - match: '{{identifier}}' + scope: variable.parameter.c++ + - match: \) + scope: punctuation.section.group.end.c++ + set: preprocessor-macro-definition + - match: ',' + scope: punctuation.separator.c++ + push: + - match: '{{identifier}}' + scope: variable.parameter.c++ + pop: true + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-comments + - match: '\.\.\.' + scope: keyword.operator.variadic.c++ + - match: '(?=\))' + pop: true + - match: (/\*).*(\*/) + scope: comment.block.c++ + captures: + 1: punctuation.definition.comment.c++ + 2: punctuation.definition.comment.c++ + - match: '\S+' + scope: invalid.illegal.unexpected-character.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-comments + - match: '\.\.\.' + scope: keyword.operator.variadic.c++ + - match: (/\*).*(\*/) + scope: comment.block.c++ + captures: + 1: punctuation.definition.comment.c++ + 2: punctuation.definition.comment.c++ + - match: $\n + scope: invalid.illegal.unexpected-end-of-line.c++ + + preprocessor-macro-definition: + - meta_content_scope: meta.preprocessor.macro.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-line-ending + - include: scope:source.c#preprocessor-comments + # Don't define blocks in define statements + - match: '\{' + scope: punctuation.section.block.begin.c++ + - match: '\}' + scope: punctuation.section.block.end.c++ + - include: expressions + + preprocessor-practical-workarounds: + - include: preprocessor-convention-ignore-uppercase-ident-lines + - include: scope:source.c#preprocessor-convention-ignore-uppercase-calls-without-semicolon + + preprocessor-convention-ignore-uppercase-ident-lines: + - match: ^(\s*{{macro_identifier}})+\s*$ + scope: meta.assumed-macro.c++ + push: + # It's possible that we are dealing with a function return type on its own line, and the + # name of the function is on the subsequent line. + - match: '(?={{path_lookahead}}({{generic_lookahead}}({{path_lookahead}})?)\s*\()' + set: [function-definition-params, global-function-identifier-generic] + - match: '(?={{path_lookahead}}\s*\()' + set: [function-definition-params, global-function-identifier] + - match: ^ + pop: true + + preprocessor-other: + - match: ^\s*(#\s*(?:if|ifdef|ifndef|elif|else|line|pragma|undef))\b + captures: + 1: keyword.control.import.c++ + push: + - meta_scope: meta.preprocessor.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-line-ending + - include: scope:source.c#preprocessor-comments + - match: \bdefined\b + scope: keyword.control.c++ + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + - match: ^\s*(#\s*(?:error|warning))\b + captures: + 1: keyword.control.import.error.c++ + push: + - meta_scope: meta.preprocessor.diagnostic.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-line-ending + - include: scope:source.c#preprocessor-comments + - include: strings + - match: '\S+' + scope: string.unquoted.c++ + - match: ^\s*(#\s*(?:include|include_next|import))\b + captures: + 1: keyword.control.import.include.c++ + push: + - meta_scope: meta.preprocessor.include.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-line-ending + - include: scope:source.c#preprocessor-comments + - match: '"' + scope: punctuation.definition.string.begin.c++ + push: + - meta_scope: string.quoted.double.include.c++ + - match: '"' + scope: punctuation.definition.string.end.c++ + pop: true + - match: < + scope: punctuation.definition.string.begin.c++ + push: + - meta_scope: string.quoted.other.lt-gt.include.c++ + - match: '>' + scope: punctuation.definition.string.end.c++ + pop: true + - include: preprocessor-practical-workarounds diff --git a/src/fmt/support/README b/src/fmt/support/README new file mode 100644 index 000000000..468f5485d --- /dev/null +++ b/src/fmt/support/README @@ -0,0 +1,4 @@ +This directory contains build support files such as + +* CMake modules +* Build scripts diff --git a/src/fmt/support/Vagrantfile b/src/fmt/support/Vagrantfile new file mode 100644 index 000000000..f6b5f9366 --- /dev/null +++ b/src/fmt/support/Vagrantfile @@ -0,0 +1,20 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# A vagrant config for testing against gcc-4.8. +Vagrant.configure("2") do |config| + config.vm.box = "ubuntu/xenial64" + config.disksize.size = '15GB' + + config.vm.provider "virtualbox" do |vb| + vb.memory = "4096" + end + + config.vm.provision "shell", inline: <<-SHELL + apt-get update + apt-get install -y g++ make wget git + wget -q https://github.com/Kitware/CMake/releases/download/v3.14.4/cmake-3.14.4-Linux-x86_64.tar.gz + tar xzf cmake-3.14.4-Linux-x86_64.tar.gz + ln -s `pwd`/cmake-3.14.4-Linux-x86_64/bin/cmake /usr/local/bin + SHELL +end diff --git a/src/fmt/support/bazel/.bazelrc b/src/fmt/support/bazel/.bazelrc new file mode 100644 index 000000000..583cbbd22 --- /dev/null +++ b/src/fmt/support/bazel/.bazelrc @@ -0,0 +1 @@ +build --symlink_prefix=/ # Out of source build diff --git a/src/fmt/support/bazel/.bazelversion b/src/fmt/support/bazel/.bazelversion new file mode 100644 index 000000000..ac14c3dfa --- /dev/null +++ b/src/fmt/support/bazel/.bazelversion @@ -0,0 +1 @@ +5.1.1 diff --git a/src/fmt/support/bazel/BUILD.bazel b/src/fmt/support/bazel/BUILD.bazel new file mode 100644 index 000000000..085765890 --- /dev/null +++ b/src/fmt/support/bazel/BUILD.bazel @@ -0,0 +1,28 @@ +cc_library( + name = "fmt", + srcs = [ + #"src/fmt.cc", # No C++ module support + "src/format.cc", + "src/os.cc", + ], + hdrs = [ + "include/fmt/args.h", + "include/fmt/chrono.h", + "include/fmt/color.h", + "include/fmt/compile.h", + "include/fmt/core.h", + "include/fmt/format-inl.h", + "include/fmt/format.h", + "include/fmt/os.h", + "include/fmt/ostream.h", + "include/fmt/printf.h", + "include/fmt/ranges.h", + "include/fmt/std.h", + "include/fmt/xchar.h", + ], + includes = [ + "include", + ], + strip_include_prefix = "include", + visibility = ["//visibility:public"], +) diff --git a/src/fmt/support/bazel/README.md b/src/fmt/support/bazel/README.md new file mode 100644 index 000000000..44af620c1 --- /dev/null +++ b/src/fmt/support/bazel/README.md @@ -0,0 +1,73 @@ +# Bazel support + +To get [Bazel](https://bazel.build/) working with {fmt} you can copy the files `BUILD.bazel`, `WORKSPACE.bazel`, `.bazelrc`, and `.bazelversion` from this folder (`support/bazel`) to the root folder of this project. This way {fmt} gets bazelized and can be used with Bazel (e.g. doing a `bazel build //...` on {fmt}). + +## Using {fmt} as a dependency + +The following minimal example shows how to use {fmt} as a dependency within a Bazel project. + +The following file structure is assumed: + +``` +example +├── BUILD.bazel +├── main.cpp +└── WORKSPACE.bazel +``` + +*main.cpp*: + +```c++ +#include "fmt/core.h" + +int main() { + fmt::print("The answer is {}\n", 42); +} +``` + +The expected output of this example is `The answer is 42`. + +*WORKSPACE.bazel*: + +```python +load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") + +git_repository( + name = "fmt", + branch = "master", + remote = "https://github.com/fmtlib/fmt", + patch_cmds = [ + "mv support/bazel/.bazelrc .bazelrc", + "mv support/bazel/.bazelversion .bazelversion", + "mv support/bazel/BUILD.bazel BUILD.bazel", + "mv support/bazel/WORKSPACE.bazel WORKSPACE.bazel", + ], + # Windows-related patch commands are only needed in the case MSYS2 is not installed. + # More details about the installation process of MSYS2 on Windows systems can be found here: + # https://docs.bazel.build/versions/main/install-windows.html#installing-compilers-and-language-runtimes + # Even if MSYS2 is installed the Windows related patch commands can still be used. + patch_cmds_win = [ + "Move-Item -Path support/bazel/.bazelrc -Destination .bazelrc", + "Move-Item -Path support/bazel/.bazelversion -Destination .bazelversion", + "Move-Item -Path support/bazel/BUILD.bazel -Destination BUILD.bazel", + "Move-Item -Path support/bazel/WORKSPACE.bazel -Destination WORKSPACE.bazel", + ], +) +``` + +In the *WORKSPACE* file, the {fmt} GitHub repository is fetched. Using the attribute `patch_cmds` the files `BUILD.bazel`, `WORKSPACE.bazel`, `.bazelrc`, and `.bazelversion` are moved to the root of the {fmt} repository. This way the {fmt} repository is recognized as a bazelized workspace. + +*BUILD.bazel*: + +```python +cc_binary( + name = "Demo", + srcs = ["main.cpp"], + deps = ["@fmt"], +) +``` + +The *BUILD* file defines a binary named `Demo` that has a dependency to {fmt}. + +To execute the binary you can run `bazel run //:Demo`. + diff --git a/src/fmt/support/bazel/WORKSPACE.bazel b/src/fmt/support/bazel/WORKSPACE.bazel new file mode 100644 index 000000000..5be77811b --- /dev/null +++ b/src/fmt/support/bazel/WORKSPACE.bazel @@ -0,0 +1 @@ +workspace(name = "fmt") diff --git a/src/fmt/support/build-docs.py b/src/fmt/support/build-docs.py new file mode 100755 index 000000000..f1395cd6c --- /dev/null +++ b/src/fmt/support/build-docs.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# Build the documentation in CI. + +from __future__ import print_function +import errno, os, shutil, subprocess, sys, urllib +from subprocess import call, check_call, Popen, PIPE, STDOUT + +def rmtree_if_exists(dir): + try: + shutil.rmtree(dir) + except OSError as e: + if e.errno == errno.ENOENT: + pass + +# Build the docs. +fmt_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +sys.path.insert(0, os.path.join(fmt_dir, 'doc')) +import build +build.create_build_env() +html_dir = build.build_docs() + +repo = 'fmtlib.github.io' +branch = os.environ['GITHUB_REF'] +is_ci = 'CI' in os.environ +if is_ci and branch != 'refs/heads/master': + print('Branch: ' + branch) + exit(0) # Ignore non-master branches +if is_ci and 'KEY' not in os.environ: + # Don't update the repo if building in CI from an account that doesn't have + # push access. + print('Skipping update of ' + repo) + exit(0) + +# Clone the fmtlib.github.io repo. +rmtree_if_exists(repo) +git_url = 'https://github.com/' if is_ci else 'git@github.com:' +check_call(['git', 'clone', git_url + 'fmtlib/{}.git'.format(repo)]) + +# Copy docs to the repo. +target_dir = os.path.join(repo, 'dev') +rmtree_if_exists(target_dir) +shutil.copytree(html_dir, target_dir, ignore=shutil.ignore_patterns('.*')) +if is_ci: + check_call(['git', 'config', '--global', 'user.name', 'fmtbot']) + check_call(['git', 'config', '--global', 'user.email', 'viz@fmt.dev']) + +# Push docs to GitHub pages. +check_call(['git', 'add', '--all'], cwd=repo) +if call(['git', 'diff-index', '--quiet', 'HEAD'], cwd=repo): + check_call(['git', 'commit', '-m', 'Update documentation'], cwd=repo) + cmd = 'git push' + if is_ci: + cmd += ' https://$KEY@github.com/fmtlib/fmtlib.github.io.git master' + p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd=repo) + # Print the output without the key. + print(p.communicate()[0].decode('utf-8').replace(os.environ['KEY'], '$KEY')) + if p.returncode != 0: + raise subprocess.CalledProcessError(p.returncode, cmd) diff --git a/src/fmt/support/build.gradle b/src/fmt/support/build.gradle new file mode 100644 index 000000000..8d18da6bc --- /dev/null +++ b/src/fmt/support/build.gradle @@ -0,0 +1,132 @@ +import java.nio.file.Paths + +// General gradle arguments for root project +buildscript { + repositories { + google() + jcenter() + } + dependencies { + // + // https://developer.android.com/studio/releases/gradle-plugin#updating-gradle + // + // Notice that 4.0.0 here is the version of [Android Gradle Plugin] + // Accroding to URL above you will need Gradle 6.1 or higher + // + classpath "com.android.tools.build:gradle:4.1.1" + } +} +repositories { + google() + jcenter() +} + +// Project's root where CMakeLists.txt exists: rootDir/support/.cxx -> rootDir +def rootDir = Paths.get(project.buildDir.getParent()).getParent() +println("rootDir: ${rootDir}") + +// Output: Shared library (.so) for Android +apply plugin: "com.android.library" +android { + compileSdkVersion 25 // Android 7.0 + + // Target ABI + // - This option controls target platform of module + // - The platform might be limited by compiler's support + // some can work with Clang(default), but some can work only with GCC... + // if bad, both toolchains might not support it + splits { + abi { + enable true + // Specify platforms for Application + reset() + include "arm64-v8a", "armeabi-v7a", "x86_64" + } + } + ndkVersion "21.3.6528147" // ANDROID_NDK_HOME is deprecated. Be explicit + + defaultConfig { + minSdkVersion 21 // Android 5.0+ + targetSdkVersion 25 // Follow Compile SDK + versionCode 34 // Follow release count + versionName "7.1.2" // Follow Official version + + externalNativeBuild { + cmake { + arguments "-DANDROID_STL=c++_shared" // Specify Android STL + arguments "-DBUILD_SHARED_LIBS=true" // Build shared object + arguments "-DFMT_TEST=false" // Skip test + arguments "-DFMT_DOC=false" // Skip document + cppFlags "-std=c++17" + targets "fmt" + } + } + println(externalNativeBuild.cmake.cppFlags) + println(externalNativeBuild.cmake.arguments) + } + + // External Native build + // - Use existing CMakeList.txt + // - Give path to CMake. This gradle file should be + // neighbor of the top level cmake + externalNativeBuild { + cmake { + version "3.10.0+" + path "${rootDir}/CMakeLists.txt" + // buildStagingDirectory "./build" // Custom path for cmake output + } + } + + sourceSets{ + // Android Manifest for Gradle + main { + manifest.srcFile "AndroidManifest.xml" + } + } + + // https://developer.android.com/studio/build/native-dependencies#build_system_configuration + buildFeatures { + prefab true + prefabPublishing true + } + prefab { + fmt { + headers "${rootDir}/include" + } + } +} + +assemble.doLast +{ + // Instead of `ninja install`, Gradle will deploy the files. + // We are doing this since FMT is dependent to the ANDROID_STL after build + copy { + from "build/intermediates/cmake" + into "${rootDir}/libs" + } + // Copy debug binaries + copy { + from "${rootDir}/libs/debug/obj" + into "${rootDir}/libs/debug" + } + // Copy Release binaries + copy { + from "${rootDir}/libs/release/obj" + into "${rootDir}/libs/release" + } + // Remove empty directory + delete "${rootDir}/libs/debug/obj" + delete "${rootDir}/libs/release/obj" + + // Copy AAR files. Notice that the aar is named after the folder of this script. + copy { + from "build/outputs/aar/support-release.aar" + into "${rootDir}/libs" + rename "support-release.aar", "fmt-release.aar" + } + copy { + from "build/outputs/aar/support-debug.aar" + into "${rootDir}/libs" + rename "support-debug.aar", "fmt-debug.aar" + } +} diff --git a/src/fmt/support/cmake/FindSetEnv.cmake b/src/fmt/support/cmake/FindSetEnv.cmake new file mode 100644 index 000000000..4e2da5408 --- /dev/null +++ b/src/fmt/support/cmake/FindSetEnv.cmake @@ -0,0 +1,7 @@ +# A CMake script to find SetEnv.cmd. + +find_program(WINSDK_SETENV NAMES SetEnv.cmd + PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]/bin") +if (WINSDK_SETENV AND PRINT_PATH) + execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${WINSDK_SETENV}") +endif () diff --git a/src/fmt/support/cmake/JoinPaths.cmake b/src/fmt/support/cmake/JoinPaths.cmake new file mode 100644 index 000000000..32d6d6685 --- /dev/null +++ b/src/fmt/support/cmake/JoinPaths.cmake @@ -0,0 +1,26 @@ +# This module provides function for joining paths +# known from from most languages +# +# Original license: +# SPDX-License-Identifier: (MIT OR CC0-1.0) +# Explicit permission given to distribute this module under +# the terms of the project as described in /LICENSE.rst. +# Copyright 2020 Jan Tojnar +# https://github.com/jtojnar/cmake-snips +# +# Modelled after Python’s os.path.join +# https://docs.python.org/3.7/library/os.path.html#os.path.join +# Windows not supported +function(join_paths joined_path first_path_segment) + set(temp_path "${first_path_segment}") + foreach(current_segment IN LISTS ARGN) + if(NOT ("${current_segment}" STREQUAL "")) + if(IS_ABSOLUTE "${current_segment}") + set(temp_path "${current_segment}") + else() + set(temp_path "${temp_path}/${current_segment}") + endif() + endif() + endforeach() + set(${joined_path} "${temp_path}" PARENT_SCOPE) +endfunction() diff --git a/src/fmt/support/cmake/cxx14.cmake b/src/fmt/support/cmake/cxx14.cmake new file mode 100644 index 000000000..deb1e26fb --- /dev/null +++ b/src/fmt/support/cmake/cxx14.cmake @@ -0,0 +1,54 @@ +# C++14 feature support detection + +include(CheckCXXCompilerFlag) +function (fmt_check_cxx_compiler_flag flag result) + if (NOT MSVC) + check_cxx_compiler_flag("${flag}" ${result}) + endif () +endfunction () + +if (NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 11) +endif() +message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}") + +if (CMAKE_CXX_STANDARD EQUAL 20) + fmt_check_cxx_compiler_flag(-std=c++20 has_std_20_flag) + fmt_check_cxx_compiler_flag(-std=c++2a has_std_2a_flag) + + if (has_std_20_flag) + set(CXX_STANDARD_FLAG -std=c++20) + elseif (has_std_2a_flag) + set(CXX_STANDARD_FLAG -std=c++2a) + endif () + +elseif (CMAKE_CXX_STANDARD EQUAL 17) + fmt_check_cxx_compiler_flag(-std=c++17 has_std_17_flag) + fmt_check_cxx_compiler_flag(-std=c++1z has_std_1z_flag) + + if (has_std_17_flag) + set(CXX_STANDARD_FLAG -std=c++17) + elseif (has_std_1z_flag) + set(CXX_STANDARD_FLAG -std=c++1z) + endif () + +elseif (CMAKE_CXX_STANDARD EQUAL 14) + fmt_check_cxx_compiler_flag(-std=c++14 has_std_14_flag) + fmt_check_cxx_compiler_flag(-std=c++1y has_std_1y_flag) + + if (has_std_14_flag) + set(CXX_STANDARD_FLAG -std=c++14) + elseif (has_std_1y_flag) + set(CXX_STANDARD_FLAG -std=c++1y) + endif () + +elseif (CMAKE_CXX_STANDARD EQUAL 11) + fmt_check_cxx_compiler_flag(-std=c++11 has_std_11_flag) + fmt_check_cxx_compiler_flag(-std=c++0x has_std_0x_flag) + + if (has_std_11_flag) + set(CXX_STANDARD_FLAG -std=c++11) + elseif (has_std_0x_flag) + set(CXX_STANDARD_FLAG -std=c++0x) + endif () +endif () diff --git a/src/fmt/support/cmake/fmt-config.cmake.in b/src/fmt/support/cmake/fmt-config.cmake.in new file mode 100644 index 000000000..bc1684f24 --- /dev/null +++ b/src/fmt/support/cmake/fmt-config.cmake.in @@ -0,0 +1,7 @@ +@PACKAGE_INIT@ + +if (NOT TARGET fmt::fmt) + include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake) +endif () + +check_required_components(fmt) diff --git a/src/fmt/support/cmake/fmt.pc.in b/src/fmt/support/cmake/fmt.pc.in new file mode 100644 index 000000000..29976a8af --- /dev/null +++ b/src/fmt/support/cmake/fmt.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@libdir_for_pc_file@ +includedir=@includedir_for_pc_file@ + +Name: fmt +Description: A modern formatting library +Version: @FMT_VERSION@ +Libs: -L${libdir} -l@FMT_LIB_NAME@ +Cflags: -I${includedir} + diff --git a/src/fmt/support/compute-powers.py b/src/fmt/support/compute-powers.py new file mode 100755 index 000000000..601063d42 --- /dev/null +++ b/src/fmt/support/compute-powers.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# Compute 10 ** exp with exp in the range [min_exponent, max_exponent] and print +# normalized (with most-significant bit equal to 1) significands in hexadecimal. + +from __future__ import print_function + +min_exponent = -348 +max_exponent = 340 +step = 8 +significand_size = 64 +exp_offset = 2000 + +class fp: + pass + +powers = [] +for i, exp in enumerate(range(min_exponent, max_exponent + 1, step)): + result = fp() + n = 10 ** exp if exp >= 0 else 2 ** exp_offset / 10 ** -exp + k = significand_size + 1 + # Convert to binary and round. + binary = '{:b}'.format(n) + result.f = (int('{:0<{}}'.format(binary[:k], k), 2) + 1) / 2 + result.e = len(binary) - (exp_offset if exp < 0 else 0) - significand_size + powers.append(result) + # Sanity check. + exp_offset10 = 400 + actual = result.f * 10 ** exp_offset10 + if result.e > 0: + actual *= 2 ** result.e + else: + for j in range(-result.e): + actual /= 2 + expected = 10 ** (exp_offset10 + exp) + precision = len('{}'.format(expected)) - len('{}'.format(actual - expected)) + if precision < 19: + print('low precision:', precision) + exit(1) + +print('Significands:', end='') +for i, fp in enumerate(powers): + if i % 3 == 0: + print(end='\n ') + print(' {:0<#16x}'.format(fp.f, ), end=',') + +print('\n\nExponents:', end='') +for i, fp in enumerate(powers): + if i % 11 == 0: + print(end='\n ') + print(' {:5}'.format(fp.e), end=',') + +print('\n\nMax exponent difference:', + max([x.e - powers[i - 1].e for i, x in enumerate(powers)][1:])) diff --git a/src/fmt/support/docopt.py b/src/fmt/support/docopt.py new file mode 100644 index 000000000..2e43f7cef --- /dev/null +++ b/src/fmt/support/docopt.py @@ -0,0 +1,581 @@ +"""Pythonic command-line interface parser that will make you smile. + + * http://docopt.org + * Repository and issue-tracker: https://github.com/docopt/docopt + * Licensed under terms of MIT license (see LICENSE-MIT) + * Copyright (c) 2013 Vladimir Keleshev, vladimir@keleshev.com + +""" +import sys +import re + + +__all__ = ['docopt'] +__version__ = '0.6.1' + + +class DocoptLanguageError(Exception): + + """Error in construction of usage-message by developer.""" + + +class DocoptExit(SystemExit): + + """Exit in case user invoked program with incorrect arguments.""" + + usage = '' + + def __init__(self, message=''): + SystemExit.__init__(self, (message + '\n' + self.usage).strip()) + + +class Pattern(object): + + def __eq__(self, other): + return repr(self) == repr(other) + + def __hash__(self): + return hash(repr(self)) + + def fix(self): + self.fix_identities() + self.fix_repeating_arguments() + return self + + def fix_identities(self, uniq=None): + """Make pattern-tree tips point to same object if they are equal.""" + if not hasattr(self, 'children'): + return self + uniq = list(set(self.flat())) if uniq is None else uniq + for i, child in enumerate(self.children): + if not hasattr(child, 'children'): + assert child in uniq + self.children[i] = uniq[uniq.index(child)] + else: + child.fix_identities(uniq) + + def fix_repeating_arguments(self): + """Fix elements that should accumulate/increment values.""" + either = [list(child.children) for child in transform(self).children] + for case in either: + for e in [child for child in case if case.count(child) > 1]: + if type(e) is Argument or type(e) is Option and e.argcount: + if e.value is None: + e.value = [] + elif type(e.value) is not list: + e.value = e.value.split() + if type(e) is Command or type(e) is Option and e.argcount == 0: + e.value = 0 + return self + + +def transform(pattern): + """Expand pattern into an (almost) equivalent one, but with single Either. + + Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d) + Quirks: [-a] => (-a), (-a...) => (-a -a) + + """ + result = [] + groups = [[pattern]] + while groups: + children = groups.pop(0) + parents = [Required, Optional, OptionsShortcut, Either, OneOrMore] + if any(t in map(type, children) for t in parents): + child = [c for c in children if type(c) in parents][0] + children.remove(child) + if type(child) is Either: + for c in child.children: + groups.append([c] + children) + elif type(child) is OneOrMore: + groups.append(child.children * 2 + children) + else: + groups.append(child.children + children) + else: + result.append(children) + return Either(*[Required(*e) for e in result]) + + +class LeafPattern(Pattern): + + """Leaf/terminal node of a pattern tree.""" + + def __init__(self, name, value=None): + self.name, self.value = name, value + + def __repr__(self): + return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value) + + def flat(self, *types): + return [self] if not types or type(self) in types else [] + + def match(self, left, collected=None): + collected = [] if collected is None else collected + pos, match = self.single_match(left) + if match is None: + return False, left, collected + left_ = left[:pos] + left[pos + 1:] + same_name = [a for a in collected if a.name == self.name] + if type(self.value) in (int, list): + if type(self.value) is int: + increment = 1 + else: + increment = ([match.value] if type(match.value) is str + else match.value) + if not same_name: + match.value = increment + return True, left_, collected + [match] + same_name[0].value += increment + return True, left_, collected + return True, left_, collected + [match] + + +class BranchPattern(Pattern): + + """Branch/inner node of a pattern tree.""" + + def __init__(self, *children): + self.children = list(children) + + def __repr__(self): + return '%s(%s)' % (self.__class__.__name__, + ', '.join(repr(a) for a in self.children)) + + def flat(self, *types): + if type(self) in types: + return [self] + return sum([child.flat(*types) for child in self.children], []) + + +class Argument(LeafPattern): + + def single_match(self, left): + for n, pattern in enumerate(left): + if type(pattern) is Argument: + return n, Argument(self.name, pattern.value) + return None, None + + @classmethod + def parse(class_, source): + name = re.findall('(<\S*?>)', source)[0] + value = re.findall('\[default: (.*)\]', source, flags=re.I) + return class_(name, value[0] if value else None) + + +class Command(Argument): + + def __init__(self, name, value=False): + self.name, self.value = name, value + + def single_match(self, left): + for n, pattern in enumerate(left): + if type(pattern) is Argument: + if pattern.value == self.name: + return n, Command(self.name, True) + else: + break + return None, None + + +class Option(LeafPattern): + + def __init__(self, short=None, long=None, argcount=0, value=False): + assert argcount in (0, 1) + self.short, self.long, self.argcount = short, long, argcount + self.value = None if value is False and argcount else value + + @classmethod + def parse(class_, option_description): + short, long, argcount, value = None, None, 0, False + options, _, description = option_description.strip().partition(' ') + options = options.replace(',', ' ').replace('=', ' ') + for s in options.split(): + if s.startswith('--'): + long = s + elif s.startswith('-'): + short = s + else: + argcount = 1 + if argcount: + matched = re.findall('\[default: (.*)\]', description, flags=re.I) + value = matched[0] if matched else None + return class_(short, long, argcount, value) + + def single_match(self, left): + for n, pattern in enumerate(left): + if self.name == pattern.name: + return n, pattern + return None, None + + @property + def name(self): + return self.long or self.short + + def __repr__(self): + return 'Option(%r, %r, %r, %r)' % (self.short, self.long, + self.argcount, self.value) + + +class Required(BranchPattern): + + def match(self, left, collected=None): + collected = [] if collected is None else collected + l = left + c = collected + for pattern in self.children: + matched, l, c = pattern.match(l, c) + if not matched: + return False, left, collected + return True, l, c + + +class Optional(BranchPattern): + + def match(self, left, collected=None): + collected = [] if collected is None else collected + for pattern in self.children: + m, left, collected = pattern.match(left, collected) + return True, left, collected + + +class OptionsShortcut(Optional): + + """Marker/placeholder for [options] shortcut.""" + + +class OneOrMore(BranchPattern): + + def match(self, left, collected=None): + assert len(self.children) == 1 + collected = [] if collected is None else collected + l = left + c = collected + l_ = None + matched = True + times = 0 + while matched: + # could it be that something didn't match but changed l or c? + matched, l, c = self.children[0].match(l, c) + times += 1 if matched else 0 + if l_ == l: + break + l_ = l + if times >= 1: + return True, l, c + return False, left, collected + + +class Either(BranchPattern): + + def match(self, left, collected=None): + collected = [] if collected is None else collected + outcomes = [] + for pattern in self.children: + matched, _, _ = outcome = pattern.match(left, collected) + if matched: + outcomes.append(outcome) + if outcomes: + return min(outcomes, key=lambda outcome: len(outcome[1])) + return False, left, collected + + +class Tokens(list): + + def __init__(self, source, error=DocoptExit): + self += source.split() if hasattr(source, 'split') else source + self.error = error + + @staticmethod + def from_pattern(source): + source = re.sub(r'([\[\]\(\)\|]|\.\.\.)', r' \1 ', source) + source = [s for s in re.split('\s+|(\S*<.*?>)', source) if s] + return Tokens(source, error=DocoptLanguageError) + + def move(self): + return self.pop(0) if len(self) else None + + def current(self): + return self[0] if len(self) else None + + +def parse_long(tokens, options): + """long ::= '--' chars [ ( ' ' | '=' ) chars ] ;""" + long, eq, value = tokens.move().partition('=') + assert long.startswith('--') + value = None if eq == value == '' else value + similar = [o for o in options if o.long == long] + if tokens.error is DocoptExit and similar == []: # if no exact match + similar = [o for o in options if o.long and o.long.startswith(long)] + if len(similar) > 1: # might be simply specified ambiguously 2+ times? + raise tokens.error('%s is not a unique prefix: %s?' % + (long, ', '.join(o.long for o in similar))) + elif len(similar) < 1: + argcount = 1 if eq == '=' else 0 + o = Option(None, long, argcount) + options.append(o) + if tokens.error is DocoptExit: + o = Option(None, long, argcount, value if argcount else True) + else: + o = Option(similar[0].short, similar[0].long, + similar[0].argcount, similar[0].value) + if o.argcount == 0: + if value is not None: + raise tokens.error('%s must not have an argument' % o.long) + else: + if value is None: + if tokens.current() in [None, '--']: + raise tokens.error('%s requires argument' % o.long) + value = tokens.move() + if tokens.error is DocoptExit: + o.value = value if value is not None else True + return [o] + + +def parse_shorts(tokens, options): + """shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;""" + token = tokens.move() + assert token.startswith('-') and not token.startswith('--') + left = token.lstrip('-') + parsed = [] + while left != '': + short, left = '-' + left[0], left[1:] + similar = [o for o in options if o.short == short] + if len(similar) > 1: + raise tokens.error('%s is specified ambiguously %d times' % + (short, len(similar))) + elif len(similar) < 1: + o = Option(short, None, 0) + options.append(o) + if tokens.error is DocoptExit: + o = Option(short, None, 0, True) + else: # why copying is necessary here? + o = Option(short, similar[0].long, + similar[0].argcount, similar[0].value) + value = None + if o.argcount != 0: + if left == '': + if tokens.current() in [None, '--']: + raise tokens.error('%s requires argument' % short) + value = tokens.move() + else: + value = left + left = '' + if tokens.error is DocoptExit: + o.value = value if value is not None else True + parsed.append(o) + return parsed + + +def parse_pattern(source, options): + tokens = Tokens.from_pattern(source) + result = parse_expr(tokens, options) + if tokens.current() is not None: + raise tokens.error('unexpected ending: %r' % ' '.join(tokens)) + return Required(*result) + + +def parse_expr(tokens, options): + """expr ::= seq ( '|' seq )* ;""" + seq = parse_seq(tokens, options) + if tokens.current() != '|': + return seq + result = [Required(*seq)] if len(seq) > 1 else seq + while tokens.current() == '|': + tokens.move() + seq = parse_seq(tokens, options) + result += [Required(*seq)] if len(seq) > 1 else seq + return [Either(*result)] if len(result) > 1 else result + + +def parse_seq(tokens, options): + """seq ::= ( atom [ '...' ] )* ;""" + result = [] + while tokens.current() not in [None, ']', ')', '|']: + atom = parse_atom(tokens, options) + if tokens.current() == '...': + atom = [OneOrMore(*atom)] + tokens.move() + result += atom + return result + + +def parse_atom(tokens, options): + """atom ::= '(' expr ')' | '[' expr ']' | 'options' + | long | shorts | argument | command ; + """ + token = tokens.current() + result = [] + if token in '([': + tokens.move() + matching, pattern = {'(': [')', Required], '[': [']', Optional]}[token] + result = pattern(*parse_expr(tokens, options)) + if tokens.move() != matching: + raise tokens.error("unmatched '%s'" % token) + return [result] + elif token == 'options': + tokens.move() + return [OptionsShortcut()] + elif token.startswith('--') and token != '--': + return parse_long(tokens, options) + elif token.startswith('-') and token not in ('-', '--'): + return parse_shorts(tokens, options) + elif token.startswith('<') and token.endswith('>') or token.isupper(): + return [Argument(tokens.move())] + else: + return [Command(tokens.move())] + + +def parse_argv(tokens, options, options_first=False): + """Parse command-line argument vector. + + If options_first: + argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ; + else: + argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ; + + """ + parsed = [] + while tokens.current() is not None: + if tokens.current() == '--': + return parsed + [Argument(None, v) for v in tokens] + elif tokens.current().startswith('--'): + parsed += parse_long(tokens, options) + elif tokens.current().startswith('-') and tokens.current() != '-': + parsed += parse_shorts(tokens, options) + elif options_first: + return parsed + [Argument(None, v) for v in tokens] + else: + parsed.append(Argument(None, tokens.move())) + return parsed + + +def parse_defaults(doc): + defaults = [] + for s in parse_section('options:', doc): + # FIXME corner case "bla: options: --foo" + _, _, s = s.partition(':') # get rid of "options:" + split = re.split('\n[ \t]*(-\S+?)', '\n' + s)[1:] + split = [s1 + s2 for s1, s2 in zip(split[::2], split[1::2])] + options = [Option.parse(s) for s in split if s.startswith('-')] + defaults += options + return defaults + + +def parse_section(name, source): + pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)', + re.IGNORECASE | re.MULTILINE) + return [s.strip() for s in pattern.findall(source)] + + +def formal_usage(section): + _, _, section = section.partition(':') # drop "usage:" + pu = section.split() + return '( ' + ' '.join(') | (' if s == pu[0] else s for s in pu[1:]) + ' )' + + +def extras(help, version, options, doc): + if help and any((o.name in ('-h', '--help')) and o.value for o in options): + print(doc.strip("\n")) + sys.exit() + if version and any(o.name == '--version' and o.value for o in options): + print(version) + sys.exit() + + +class Dict(dict): + def __repr__(self): + return '{%s}' % ',\n '.join('%r: %r' % i for i in sorted(self.items())) + + +def docopt(doc, argv=None, help=True, version=None, options_first=False): + """Parse `argv` based on command-line interface described in `doc`. + + `docopt` creates your command-line interface based on its + description that you pass as `doc`. Such description can contain + --options, <positional-argument>, commands, which could be + [optional], (required), (mutually | exclusive) or repeated... + + Parameters + ---------- + doc : str + Description of your command-line interface. + argv : list of str, optional + Argument vector to be parsed. sys.argv[1:] is used if not + provided. + help : bool (default: True) + Set to False to disable automatic help on -h or --help + options. + version : any object + If passed, the object will be printed if --version is in + `argv`. + options_first : bool (default: False) + Set to True to require options precede positional arguments, + i.e. to forbid options and positional arguments intermix. + + Returns + ------- + args : dict + A dictionary, where keys are names of command-line elements + such as e.g. "--verbose" and "<path>", and values are the + parsed values of those elements. + + Example + ------- + >>> from docopt import docopt + >>> doc = ''' + ... Usage: + ... my_program tcp <host> <port> [--timeout=<seconds>] + ... my_program serial <port> [--baud=<n>] [--timeout=<seconds>] + ... my_program (-h | --help | --version) + ... + ... Options: + ... -h, --help Show this screen and exit. + ... --baud=<n> Baudrate [default: 9600] + ... ''' + >>> argv = ['tcp', '127.0.0.1', '80', '--timeout', '30'] + >>> docopt(doc, argv) + {'--baud': '9600', + '--help': False, + '--timeout': '30', + '--version': False, + '<host>': '127.0.0.1', + '<port>': '80', + 'serial': False, + 'tcp': True} + + See also + -------- + * For video introduction see http://docopt.org + * Full documentation is available in README.rst as well as online + at https://github.com/docopt/docopt#readme + + """ + argv = sys.argv[1:] if argv is None else argv + + usage_sections = parse_section('usage:', doc) + if len(usage_sections) == 0: + raise DocoptLanguageError('"usage:" (case-insensitive) not found.') + if len(usage_sections) > 1: + raise DocoptLanguageError('More than one "usage:" (case-insensitive).') + DocoptExit.usage = usage_sections[0] + + options = parse_defaults(doc) + pattern = parse_pattern(formal_usage(DocoptExit.usage), options) + # [default] syntax for argument is disabled + #for a in pattern.flat(Argument): + # same_name = [d for d in arguments if d.name == a.name] + # if same_name: + # a.value = same_name[0].value + argv = parse_argv(Tokens(argv), list(options), options_first) + pattern_options = set(pattern.flat(Option)) + for options_shortcut in pattern.flat(OptionsShortcut): + doc_options = parse_defaults(doc) + options_shortcut.children = list(set(doc_options) - pattern_options) + #if any_options: + # options_shortcut.children += [Option(o.short, o.long, o.argcount) + # for o in argv if type(o) is Option] + extras(help, version, argv, doc) + matched, left, collected = pattern.fix().match(argv) + if matched and left == []: # better error message if left? + return Dict((a.name, a.value) for a in (pattern.flat() + collected)) + raise DocoptExit() diff --git a/src/fmt/support/manage.py b/src/fmt/support/manage.py new file mode 100755 index 000000000..d2afcee92 --- /dev/null +++ b/src/fmt/support/manage.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python3 + +"""Manage site and releases. + +Usage: + manage.py release [<branch>] + manage.py site + +For the release command $FMT_TOKEN should contain a GitHub personal access token +obtained from https://github.com/settings/tokens. +""" + +from __future__ import print_function +import datetime, docopt, errno, fileinput, json, os +import re, requests, shutil, sys, tempfile +from contextlib import contextmanager +from distutils.version import LooseVersion +from subprocess import check_call + + +class Git: + def __init__(self, dir): + self.dir = dir + + def call(self, method, args, **kwargs): + return check_call(['git', method] + list(args), **kwargs) + + def add(self, *args): + return self.call('add', args, cwd=self.dir) + + def checkout(self, *args): + return self.call('checkout', args, cwd=self.dir) + + def clean(self, *args): + return self.call('clean', args, cwd=self.dir) + + def clone(self, *args): + return self.call('clone', list(args) + [self.dir]) + + def commit(self, *args): + return self.call('commit', args, cwd=self.dir) + + def pull(self, *args): + return self.call('pull', args, cwd=self.dir) + + def push(self, *args): + return self.call('push', args, cwd=self.dir) + + def reset(self, *args): + return self.call('reset', args, cwd=self.dir) + + def update(self, *args): + clone = not os.path.exists(self.dir) + if clone: + self.clone(*args) + return clone + + +def clean_checkout(repo, branch): + repo.clean('-f', '-d') + repo.reset('--hard') + repo.checkout(branch) + + +class Runner: + def __init__(self, cwd): + self.cwd = cwd + + def __call__(self, *args, **kwargs): + kwargs['cwd'] = kwargs.get('cwd', self.cwd) + check_call(args, **kwargs) + + +def create_build_env(): + """Create a build environment.""" + class Env: + pass + env = Env() + + # Import the documentation build module. + env.fmt_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + sys.path.insert(0, os.path.join(env.fmt_dir, 'doc')) + import build + + env.build_dir = 'build' + env.versions = build.versions + + # Virtualenv and repos are cached to speed up builds. + build.create_build_env(os.path.join(env.build_dir, 'virtualenv')) + + env.fmt_repo = Git(os.path.join(env.build_dir, 'fmt')) + return env + + +@contextmanager +def rewrite(filename): + class Buffer: + pass + buffer = Buffer() + if not os.path.exists(filename): + buffer.data = '' + yield buffer + return + with open(filename) as f: + buffer.data = f.read() + yield buffer + with open(filename, 'w') as f: + f.write(buffer.data) + + +fmt_repo_url = 'git@github.com:fmtlib/fmt' + + +def update_site(env): + env.fmt_repo.update(fmt_repo_url) + + doc_repo = Git(os.path.join(env.build_dir, 'fmtlib.github.io')) + doc_repo.update('git@github.com:fmtlib/fmtlib.github.io') + + for version in env.versions: + clean_checkout(env.fmt_repo, version) + target_doc_dir = os.path.join(env.fmt_repo.dir, 'doc') + # Remove the old theme. + for entry in os.listdir(target_doc_dir): + path = os.path.join(target_doc_dir, entry) + if os.path.isdir(path): + shutil.rmtree(path) + # Copy the new theme. + for entry in ['_static', '_templates', 'basic-bootstrap', 'bootstrap', + 'conf.py', 'fmt.less']: + src = os.path.join(env.fmt_dir, 'doc', entry) + dst = os.path.join(target_doc_dir, entry) + copy = shutil.copytree if os.path.isdir(src) else shutil.copyfile + copy(src, dst) + # Rename index to contents. + contents = os.path.join(target_doc_dir, 'contents.rst') + if not os.path.exists(contents): + os.rename(os.path.join(target_doc_dir, 'index.rst'), contents) + # Fix issues in reference.rst/api.rst. + for filename in ['reference.rst', 'api.rst', 'index.rst']: + pattern = re.compile('doxygenfunction.. (bin|oct|hexu|hex)$', re.M) + with rewrite(os.path.join(target_doc_dir, filename)) as b: + b.data = b.data.replace('std::ostream &', 'std::ostream&') + b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data) + b.data = b.data.replace('std::FILE*', 'std::FILE *') + b.data = b.data.replace('unsigned int', 'unsigned') + #b.data = b.data.replace('operator""_', 'operator"" _') + b.data = b.data.replace( + 'format_to_n(OutputIt, size_t, string_view, Args&&', + 'format_to_n(OutputIt, size_t, const S&, const Args&') + b.data = b.data.replace( + 'format_to_n(OutputIt, std::size_t, string_view, Args&&', + 'format_to_n(OutputIt, std::size_t, const S&, const Args&') + if version == ('3.0.2'): + b.data = b.data.replace( + 'fprintf(std::ostream&', 'fprintf(std::ostream &') + if version == ('5.3.0'): + b.data = b.data.replace( + 'format_to(OutputIt, const S&, const Args&...)', + 'format_to(OutputIt, const S &, const Args &...)') + if version.startswith('5.') or version.startswith('6.'): + b.data = b.data.replace(', size_t', ', std::size_t') + if version.startswith('7.'): + b.data = b.data.replace(', std::size_t', ', size_t') + b.data = b.data.replace('join(It, It', 'join(It, Sentinel') + if version.startswith('7.1.'): + b.data = b.data.replace(', std::size_t', ', size_t') + b.data = b.data.replace('join(It, It', 'join(It, Sentinel') + b.data = b.data.replace( + 'fmt::format_to(OutputIt, const S&, Args&&...)', + 'fmt::format_to(OutputIt, const S&, Args&&...) -> ' + + 'typename std::enable_if<enable, OutputIt>::type') + b.data = b.data.replace('aa long', 'a long') + b.data = b.data.replace('serveral', 'several') + if version.startswith('6.2.'): + b.data = b.data.replace( + 'vformat(const S&, basic_format_args<' + + 'buffer_context<Char>>)', + 'vformat(const S&, basic_format_args<' + + 'buffer_context<type_identity_t<Char>>>)') + # Fix a broken link in index.rst. + index = os.path.join(target_doc_dir, 'index.rst') + with rewrite(index) as b: + b.data = b.data.replace( + 'doc/latest/index.html#format-string-syntax', 'syntax.html') + # Fix issues in syntax.rst. + index = os.path.join(target_doc_dir, 'syntax.rst') + with rewrite(index) as b: + b.data = b.data.replace( + '..productionlist:: sf\n', '.. productionlist:: sf\n ') + b.data = b.data.replace('Examples:\n', 'Examples::\n') + # Build the docs. + html_dir = os.path.join(env.build_dir, 'html') + if os.path.exists(html_dir): + shutil.rmtree(html_dir) + include_dir = env.fmt_repo.dir + if LooseVersion(version) >= LooseVersion('5.0.0'): + include_dir = os.path.join(include_dir, 'include', 'fmt') + elif LooseVersion(version) >= LooseVersion('3.0.0'): + include_dir = os.path.join(include_dir, 'fmt') + import build + build.build_docs(version, doc_dir=target_doc_dir, + include_dir=include_dir, work_dir=env.build_dir) + shutil.rmtree(os.path.join(html_dir, '.doctrees')) + # Create symlinks for older versions. + for link, target in {'index': 'contents', 'api': 'reference'}.items(): + link = os.path.join(html_dir, link) + '.html' + target += '.html' + if os.path.exists(os.path.join(html_dir, target)) and \ + not os.path.exists(link): + os.symlink(target, link) + # Copy docs to the website. + version_doc_dir = os.path.join(doc_repo.dir, version) + try: + shutil.rmtree(version_doc_dir) + except OSError as e: + if e.errno != errno.ENOENT: + raise + shutil.move(html_dir, version_doc_dir) + + +def release(args): + env = create_build_env() + fmt_repo = env.fmt_repo + + branch = args.get('<branch>') + if branch is None: + branch = 'master' + if not fmt_repo.update('-b', branch, fmt_repo_url): + clean_checkout(fmt_repo, branch) + + # Convert changelog from RST to GitHub-flavored Markdown and get the + # version. + changelog = 'ChangeLog.rst' + changelog_path = os.path.join(fmt_repo.dir, changelog) + import rst2md + changes, version = rst2md.convert(changelog_path) + cmakelists = 'CMakeLists.txt' + for line in fileinput.input(os.path.join(fmt_repo.dir, cmakelists), + inplace=True): + prefix = 'set(FMT_VERSION ' + if line.startswith(prefix): + line = prefix + version + ')\n' + sys.stdout.write(line) + + # Update the version in the changelog. + title_len = 0 + for line in fileinput.input(changelog_path, inplace=True): + if line.startswith(version + ' - TBD'): + line = version + ' - ' + datetime.date.today().isoformat() + title_len = len(line) + line += '\n' + elif title_len: + line = '-' * title_len + '\n' + title_len = 0 + sys.stdout.write(line) + + # Add the version to the build script. + script = os.path.join('doc', 'build.py') + script_path = os.path.join(fmt_repo.dir, script) + for line in fileinput.input(script_path, inplace=True): + m = re.match(r'( *versions = )\[(.+)\]', line) + if m: + line = '{}[{}, \'{}\']\n'.format(m.group(1), m.group(2), version) + sys.stdout.write(line) + + fmt_repo.checkout('-B', 'release') + fmt_repo.add(changelog, cmakelists, script) + fmt_repo.commit('-m', 'Update version') + + # Build the docs and package. + run = Runner(fmt_repo.dir) + run('cmake', '.') + run('make', 'doc', 'package_source') + update_site(env) + + # Create a release on GitHub. + fmt_repo.push('origin', 'release') + auth_headers = {'Authorization': 'token ' + os.getenv('FMT_TOKEN')} + r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases', + headers=auth_headers, + data=json.dumps({'tag_name': version, + 'target_commitish': 'release', + 'body': changes, 'draft': True})) + if r.status_code != 201: + raise Exception('Failed to create a release ' + str(r)) + id = r.json()['id'] + uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases' + package = 'fmt-{}.zip'.format(version) + r = requests.post( + '{}/{}/assets?name={}'.format(uploads_url, id, package), + headers={'Content-Type': 'application/zip'} | auth_headers, + data=open('build/fmt/' + package, 'rb')) + if r.status_code != 201: + raise Exception('Failed to upload an asset ' + str(r)) + + +if __name__ == '__main__': + args = docopt.docopt(__doc__) + if args.get('release'): + release(args) + elif args.get('site'): + update_site(create_build_env()) diff --git a/src/fmt/support/printable.py b/src/fmt/support/printable.py new file mode 100755 index 000000000..8fa86b300 --- /dev/null +++ b/src/fmt/support/printable.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 + +# This script is based on +# https://github.com/rust-lang/rust/blob/master/library/core/src/unicode/printable.py +# distributed under https://github.com/rust-lang/rust/blob/master/LICENSE-MIT. + +# This script uses the following Unicode tables: +# - UnicodeData.txt + + +from collections import namedtuple +import csv +import os +import subprocess + +NUM_CODEPOINTS=0x110000 + +def to_ranges(iter): + current = None + for i in iter: + if current is None or i != current[1] or i in (0x10000, 0x20000): + if current is not None: + yield tuple(current) + current = [i, i + 1] + else: + current[1] += 1 + if current is not None: + yield tuple(current) + +def get_escaped(codepoints): + for c in codepoints: + if (c.class_ or "Cn") in "Cc Cf Cs Co Cn Zl Zp Zs".split() and c.value != ord(' '): + yield c.value + +def get_file(f): + try: + return open(os.path.basename(f)) + except FileNotFoundError: + subprocess.run(["curl", "-O", f], check=True) + return open(os.path.basename(f)) + +Codepoint = namedtuple('Codepoint', 'value class_') + +def get_codepoints(f): + r = csv.reader(f, delimiter=";") + prev_codepoint = 0 + class_first = None + for row in r: + codepoint = int(row[0], 16) + name = row[1] + class_ = row[2] + + if class_first is not None: + if not name.endswith("Last>"): + raise ValueError("Missing Last after First") + + for c in range(prev_codepoint + 1, codepoint): + yield Codepoint(c, class_first) + + class_first = None + if name.endswith("First>"): + class_first = class_ + + yield Codepoint(codepoint, class_) + prev_codepoint = codepoint + + if class_first is not None: + raise ValueError("Missing Last after First") + + for c in range(prev_codepoint + 1, NUM_CODEPOINTS): + yield Codepoint(c, None) + +def compress_singletons(singletons): + uppers = [] # (upper, # items in lowers) + lowers = [] + + for i in singletons: + upper = i >> 8 + lower = i & 0xff + if len(uppers) == 0 or uppers[-1][0] != upper: + uppers.append((upper, 1)) + else: + upper, count = uppers[-1] + uppers[-1] = upper, count + 1 + lowers.append(lower) + + return uppers, lowers + +def compress_normal(normal): + # lengths 0x00..0x7f are encoded as 00, 01, ..., 7e, 7f + # lengths 0x80..0x7fff are encoded as 80 80, 80 81, ..., ff fe, ff ff + compressed = [] # [truelen, (truelenaux), falselen, (falselenaux)] + + prev_start = 0 + for start, count in normal: + truelen = start - prev_start + falselen = count + prev_start = start + count + + assert truelen < 0x8000 and falselen < 0x8000 + entry = [] + if truelen > 0x7f: + entry.append(0x80 | (truelen >> 8)) + entry.append(truelen & 0xff) + else: + entry.append(truelen & 0x7f) + if falselen > 0x7f: + entry.append(0x80 | (falselen >> 8)) + entry.append(falselen & 0xff) + else: + entry.append(falselen & 0x7f) + + compressed.append(entry) + + return compressed + +def print_singletons(uppers, lowers, uppersname, lowersname): + print(" static constexpr singleton {}[] = {{".format(uppersname)) + for u, c in uppers: + print(" {{{:#04x}, {}}},".format(u, c)) + print(" };") + print(" static constexpr unsigned char {}[] = {{".format(lowersname)) + for i in range(0, len(lowers), 8): + print(" {}".format(" ".join("{:#04x},".format(l) for l in lowers[i:i+8]))) + print(" };") + +def print_normal(normal, normalname): + print(" static constexpr unsigned char {}[] = {{".format(normalname)) + for v in normal: + print(" {}".format(" ".join("{:#04x},".format(i) for i in v))) + print(" };") + +def main(): + file = get_file("https://www.unicode.org/Public/UNIDATA/UnicodeData.txt") + + codepoints = get_codepoints(file) + + CUTOFF=0x10000 + singletons0 = [] + singletons1 = [] + normal0 = [] + normal1 = [] + extra = [] + + for a, b in to_ranges(get_escaped(codepoints)): + if a > 2 * CUTOFF: + extra.append((a, b - a)) + elif a == b - 1: + if a & CUTOFF: + singletons1.append(a & ~CUTOFF) + else: + singletons0.append(a) + elif a == b - 2: + if a & CUTOFF: + singletons1.append(a & ~CUTOFF) + singletons1.append((a + 1) & ~CUTOFF) + else: + singletons0.append(a) + singletons0.append(a + 1) + else: + if a >= 2 * CUTOFF: + extra.append((a, b - a)) + elif a & CUTOFF: + normal1.append((a & ~CUTOFF, b - a)) + else: + normal0.append((a, b - a)) + + singletons0u, singletons0l = compress_singletons(singletons0) + singletons1u, singletons1l = compress_singletons(singletons1) + normal0 = compress_normal(normal0) + normal1 = compress_normal(normal1) + + print("""\ +FMT_FUNC auto is_printable(uint32_t cp) -> bool {\ +""") + print_singletons(singletons0u, singletons0l, 'singletons0', 'singletons0_lower') + print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower') + print_normal(normal0, 'normal0') + print_normal(normal1, 'normal1') + print("""\ + auto lower = static_cast<uint16_t>(cp); + if (cp < 0x10000) { + return is_printable(lower, singletons0, + sizeof(singletons0) / sizeof(*singletons0), + singletons0_lower, normal0, sizeof(normal0)); + } + if (cp < 0x20000) { + return is_printable(lower, singletons1, + sizeof(singletons1) / sizeof(*singletons1), + singletons1_lower, normal1, sizeof(normal1)); + }\ +""") + for a, b in extra: + print(" if (0x{:x} <= cp && cp < 0x{:x}) return false;".format(a, a + b)) + print("""\ + return cp < 0x{:x}; +}}\ +""".format(NUM_CODEPOINTS)) + +if __name__ == '__main__': + main() diff --git a/src/fmt/support/rst2md.py b/src/fmt/support/rst2md.py new file mode 100755 index 000000000..3f072007d --- /dev/null +++ b/src/fmt/support/rst2md.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# reStructuredText (RST) to GitHub-flavored Markdown converter + +import re, sys +from docutils import core, nodes, writers + + +def is_github_ref(node): + return re.match('https://github.com/.*/(issues|pull)/.*', node['refuri']) + + +class Translator(nodes.NodeVisitor): + def __init__(self, document): + nodes.NodeVisitor.__init__(self, document) + self.output = '' + self.indent = 0 + self.preserve_newlines = False + + def write(self, text): + self.output += text.replace('\n', '\n' + ' ' * self.indent) + + def visit_document(self, node): + pass + + def depart_document(self, node): + pass + + def visit_section(self, node): + pass + + def depart_section(self, node): + # Skip all sections except the first one. + raise nodes.StopTraversal + + def visit_title(self, node): + self.version = re.match(r'(\d+\.\d+\.\d+).*', node.children[0]).group(1) + raise nodes.SkipChildren + + def visit_title_reference(self, node): + raise Exception(node) + + def depart_title(self, node): + pass + + def visit_Text(self, node): + if not self.preserve_newlines: + node = node.replace('\n', ' ') + self.write(node) + + def depart_Text(self, node): + pass + + def visit_bullet_list(self, node): + pass + + def depart_bullet_list(self, node): + pass + + def visit_list_item(self, node): + self.write('* ') + self.indent += 2 + + def depart_list_item(self, node): + self.indent -= 2 + self.write('\n\n') + + def visit_paragraph(self, node): + self.write('\n\n') + + def depart_paragraph(self, node): + pass + + def visit_reference(self, node): + if not is_github_ref(node): + self.write('[') + + def depart_reference(self, node): + if not is_github_ref(node): + self.write('](' + node['refuri'] + ')') + + def visit_target(self, node): + pass + + def depart_target(self, node): + pass + + def visit_literal(self, node): + self.write('`') + + def depart_literal(self, node): + self.write('`') + + def visit_literal_block(self, node): + self.write('\n\n```') + if 'c++' in node['classes']: + self.write('c++') + self.write('\n') + self.preserve_newlines = True + + def depart_literal_block(self, node): + self.write('\n```\n') + self.preserve_newlines = False + + def visit_inline(self, node): + pass + + def depart_inline(self, node): + pass + + def visit_image(self, node): + self.write('![](' + node['uri'] + ')') + + def depart_image(self, node): + pass + + def write_row(self, row, widths): + for i, entry in enumerate(row): + text = entry[0][0] if len(entry) > 0 else '' + if i != 0: + self.write('|') + self.write('{:{}}'.format(text, widths[i])) + self.write('\n') + + def visit_table(self, node): + table = node.children[0] + colspecs = table[:-2] + thead = table[-2] + tbody = table[-1] + widths = [int(cs['colwidth']) for cs in colspecs] + sep = '|'.join(['-' * w for w in widths]) + '\n' + self.write('\n\n') + self.write_row(thead[0], widths) + self.write(sep) + for row in tbody: + self.write_row(row, widths) + raise nodes.SkipChildren + + def depart_table(self, node): + pass + +class MDWriter(writers.Writer): + """GitHub-flavored markdown writer""" + + supported = ('md',) + """Formats this writer supports.""" + + def translate(self): + translator = Translator(self.document) + self.document.walkabout(translator) + self.output = (translator.output, translator.version) + + +def convert(rst_path): + """Converts RST file to Markdown.""" + return core.publish_file(source_path=rst_path, writer=MDWriter()) + + +if __name__ == '__main__': + convert(sys.argv[1]) diff --git a/src/fmt/support/rtd/conf.py b/src/fmt/support/rtd/conf.py new file mode 100644 index 000000000..124fb9d4c --- /dev/null +++ b/src/fmt/support/rtd/conf.py @@ -0,0 +1,7 @@ +# Sphinx configuration for readthedocs. + +import os, sys + +master_doc = 'index' +html_theme = 'theme' +html_theme_path = ["."] diff --git a/src/fmt/support/rtd/index.rst b/src/fmt/support/rtd/index.rst new file mode 100644 index 000000000..7c88322f6 --- /dev/null +++ b/src/fmt/support/rtd/index.rst @@ -0,0 +1,2 @@ +If you are not redirected automatically, follow the +`link to the fmt documentation <https://fmt.dev/latest/>`_. diff --git a/src/fmt/support/rtd/theme/layout.html b/src/fmt/support/rtd/theme/layout.html new file mode 100644 index 000000000..29ebc55bb --- /dev/null +++ b/src/fmt/support/rtd/theme/layout.html @@ -0,0 +1,17 @@ +{% extends "basic/layout.html" %} + +{% block extrahead %} +<meta charset="UTF-8"> +<meta http-equiv="refresh" content="1;url=https://fmt.dev/latest/"> +<script type="text/javascript"> + window.location.href = "https://fmt.dev/latest/" +</script> +<title>Page Redirection</title> +{% endblock %} + +{% block document %} +If you are not redirected automatically, follow the <a href='https://fmt.dev/latest/'>link to the fmt documentation</a>. +{% endblock %} + +{% block footer %} +{% endblock %} diff --git a/src/fmt/support/rtd/theme/theme.conf b/src/fmt/support/rtd/theme/theme.conf new file mode 100644 index 000000000..89e03bbda --- /dev/null +++ b/src/fmt/support/rtd/theme/theme.conf @@ -0,0 +1,2 @@ +[theme] +inherit = basic diff --git a/src/fmt/test/CMakeLists.txt b/src/fmt/test/CMakeLists.txt new file mode 100644 index 000000000..971637859 --- /dev/null +++ b/src/fmt/test/CMakeLists.txt @@ -0,0 +1,245 @@ +add_subdirectory(gtest) + +set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc) +add_library(test-main STATIC ${TEST_MAIN_SRC}) +target_include_directories(test-main PUBLIC + $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>) +target_link_libraries(test-main gtest fmt) + +function(add_fmt_executable name) + add_executable(${name} ${ARGN}) + # (Wstringop-overflow) - [meta-bug] bogus/missing -Wstringop-overflow warnings + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88443 + # Bogus -Wstringop-overflow warning + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100395 + # [10 Regression] spurious -Wstringop-overflow writing to a trailing array plus offset + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95353 + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND + NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0) + target_compile_options(${name} PRIVATE -Wno-stringop-overflow) + # The linker flag is needed for LTO. + target_link_libraries(${name} -Wno-stringop-overflow) + endif () +endfunction() + +# Adds a test. +# Usage: add_fmt_test(name srcs...) +function(add_fmt_test name) + cmake_parse_arguments(ADD_FMT_TEST "HEADER_ONLY;MODULE" "" "" ${ARGN}) + + set(sources ${name}.cc ${ADD_FMT_TEST_UNPARSED_ARGUMENTS}) + if (ADD_FMT_TEST_HEADER_ONLY) + set(sources ${sources} ${TEST_MAIN_SRC} ../src/os.cc) + set(libs gtest fmt-header-only) + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wno-weak-vtables) + endif () + elseif (ADD_FMT_TEST_MODULE) + set(libs gtest test-module) + set_source_files_properties(${name}.cc PROPERTIES OBJECT_DEPENDS test-module) + else () + set(libs test-main fmt) + endif () + add_fmt_executable(${name} ${sources}) + target_link_libraries(${name} ${libs}) + + # Define if certain C++ features can be used. + if (FMT_PEDANTIC) + target_compile_options(${name} PRIVATE ${PEDANTIC_COMPILE_FLAGS}) + endif () + if (FMT_WERROR) + target_compile_options(${name} PRIVATE ${WERROR_FLAG}) + endif () + add_test(NAME ${name} COMMAND ${name}) +endfunction() + +add_fmt_test(args-test) +add_fmt_test(assert-test) +add_fmt_test(chrono-test) +add_fmt_test(color-test) +add_fmt_test(core-test) +add_fmt_test(gtest-extra-test) +add_fmt_test(format-test mock-allocator.h) +if (MSVC) + target_compile_options(format-test PRIVATE /bigobj) +endif () +if (NOT (MSVC AND BUILD_SHARED_LIBS)) + add_fmt_test(format-impl-test HEADER_ONLY header-only-test.cc) +endif () +add_fmt_test(ostream-test) +add_fmt_test(compile-test) +add_fmt_test(compile-fp-test HEADER_ONLY) +if (MSVC) + # Without this option, MSVC returns 199711L for the __cplusplus macro. + target_compile_options(compile-fp-test PRIVATE /Zc:__cplusplus) +endif() +add_fmt_test(printf-test) +add_fmt_test(ranges-test ranges-odr-test.cc) +add_fmt_test(scan-test) +add_fmt_test(std-test) +try_compile(compile_result_unused + ${CMAKE_CURRENT_BINARY_DIR} + SOURCES ${CMAKE_CURRENT_LIST_DIR}/detect-stdfs.cc + OUTPUT_VARIABLE RAWOUTPUT) +string(REGEX REPLACE ".*libfound \"([^\"]*)\".*" "\\1" STDLIBFS "${RAWOUTPUT}") +if (STDLIBFS) + target_link_libraries(std-test ${STDLIBFS}) +endif () +add_fmt_test(unicode-test HEADER_ONLY) +if (MSVC) + target_compile_options(unicode-test PRIVATE /utf-8) +endif () +add_fmt_test(xchar-test) +add_fmt_test(enforce-checks-test) +target_compile_definitions(enforce-checks-test PRIVATE + -DFMT_ENFORCE_COMPILE_STRING) + +if (FMT_CAN_MODULE) + # The tests need {fmt} to be compiled as traditional library + # because of visibility of implementation details. + # If module support is present the module tests require a + # test-only module to be built from {fmt} + add_library(test-module OBJECT ${CMAKE_SOURCE_DIR}/src/fmt.cc) + target_compile_features(test-module PUBLIC ${FMT_REQUIRED_FEATURES}) + target_include_directories(test-module PUBLIC + $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>) + enable_module(test-module) + + add_fmt_test(module-test MODULE test-main.cc) + if (MSVC) + target_compile_options(test-module PRIVATE /utf-8 /Zc:__cplusplus + /Zc:externConstexpr /Zc:inline) + target_compile_options(module-test PRIVATE /utf-8 /Zc:__cplusplus + /Zc:externConstexpr /Zc:inline) + endif () +endif () + +if (NOT DEFINED MSVC_STATIC_RUNTIME AND MSVC) + foreach (flag_var + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if (${flag_var} MATCHES "^(/|-)(MT|MTd)") + set(MSVC_STATIC_RUNTIME ON) + break() + endif() + endforeach() +endif() + +if (NOT MSVC_STATIC_RUNTIME) + add_fmt_executable(posix-mock-test + posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC}) + target_include_directories( + posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include) + target_link_libraries(posix-mock-test gtest) + if (FMT_PEDANTIC) + target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) + endif () + add_test(NAME posix-mock-test COMMAND posix-mock-test) + add_fmt_test(os-test) +endif () + +message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}") + +if (FMT_PEDANTIC) + # Test that the library can be compiled with exceptions disabled. + # -fno-exception is broken in icc: https://github.com/fmtlib/fmt/issues/822. + if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG) + endif () + if (HAVE_FNO_EXCEPTIONS_FLAG) + add_library(noexception-test ../src/format.cc noexception-test.cc) + target_include_directories( + noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include) + target_compile_options(noexception-test PRIVATE -fno-exceptions) + target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) + endif () + + # Test that the library compiles without locale. + add_library(nolocale-test ../src/format.cc) + target_include_directories( + nolocale-test PRIVATE ${PROJECT_SOURCE_DIR}/include) + target_compile_definitions( + nolocale-test PRIVATE FMT_STATIC_THOUSANDS_SEPARATOR=1) +endif () + +# These tests are disabled on Windows because they take too long. +if (FMT_PEDANTIC AND NOT WIN32) + # Test if incorrect API usages produce compilation error. + add_test(compile-error-test ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMAKE_CURRENT_SOURCE_DIR}/compile-error-test" + "${CMAKE_CURRENT_BINARY_DIR}/compile-error-test" + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-options + "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}" + "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" + "-DCXX_STANDARD_FLAG=${CXX_STANDARD_FLAG}" + "-DFMT_DIR=${CMAKE_SOURCE_DIR}") + + # Test if the targets are found from the build directory. + add_test(find-package-test ${CMAKE_CTEST_COMMAND} + -C ${CMAKE_BUILD_TYPE} + --build-and-test + "${CMAKE_CURRENT_SOURCE_DIR}/find-package-test" + "${CMAKE_CURRENT_BINARY_DIR}/find-package-test" + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-options + "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}" + "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" + "-DFMT_DIR=${PROJECT_BINARY_DIR}" + "-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}" + "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") + + # Test if the targets are found when add_subdirectory is used. + add_test(add-subdirectory-test ${CMAKE_CTEST_COMMAND} + -C ${CMAKE_BUILD_TYPE} + --build-and-test + "${CMAKE_CURRENT_SOURCE_DIR}/add-subdirectory-test" + "${CMAKE_CURRENT_BINARY_DIR}/add-subdirectory-test" + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-options + "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" + "-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}" + "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") +endif () + +# This test are disabled on Windows because it is only *NIX issue. +if (FMT_PEDANTIC AND NOT WIN32) + add_test(static-export-test ${CMAKE_CTEST_COMMAND} + -C ${CMAKE_BUILD_TYPE} + --build-and-test + "${CMAKE_CURRENT_SOURCE_DIR}/static-export-test" + "${CMAKE_CURRENT_BINARY_DIR}/static-export-test" + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-options + "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" + "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") +endif () + +# Activate optional CUDA tests if CUDA is found. For version selection see +# https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features +if (FMT_CUDA_TEST) + if (${CMAKE_VERSION} VERSION_LESS 3.15) + find_package(CUDA 9.0) + else () + include(CheckLanguage) + check_language(CUDA) + if (CMAKE_CUDA_COMPILER) + enable_language(CUDA OPTIONAL) + set(CUDA_FOUND TRUE) + endif () + endif () + + if (CUDA_FOUND) + add_subdirectory(cuda-test) + add_test(NAME cuda-test COMMAND fmt-in-cuda-test) + endif () +endif () diff --git a/src/fmt/test/add-subdirectory-test/CMakeLists.txt b/src/fmt/test/add-subdirectory-test/CMakeLists.txt new file mode 100644 index 000000000..e08d0cf70 --- /dev/null +++ b/src/fmt/test/add-subdirectory-test/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.1...3.18) + +project(fmt-test CXX) + +add_subdirectory(../.. fmt) + +add_executable(library-test main.cc) +target_include_directories(library-test PUBLIC SYSTEM .) +target_compile_options(library-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) +target_link_libraries(library-test fmt::fmt) + +if (TARGET fmt::fmt-header-only) + add_executable(header-only-test main.cc) + target_include_directories(header-only-test PUBLIC SYSTEM .) + target_compile_options(header-only-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) + target_link_libraries(header-only-test fmt::fmt-header-only) +endif () diff --git a/src/fmt/test/add-subdirectory-test/main.cc b/src/fmt/test/add-subdirectory-test/main.cc new file mode 100644 index 000000000..fcdf232e0 --- /dev/null +++ b/src/fmt/test/add-subdirectory-test/main.cc @@ -0,0 +1,5 @@ +#include "fmt/core.h" + +int main(int argc, char** argv) { + for (int i = 0; i < argc; ++i) fmt::print("{}: {}\n", i, argv[i]); +} diff --git a/src/fmt/test/args-test.cc b/src/fmt/test/args-test.cc new file mode 100644 index 000000000..aa01fa4e0 --- /dev/null +++ b/src/fmt/test/args-test.cc @@ -0,0 +1,186 @@ +// Formatting library for C++ - dynamic argument store tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/args.h" + +#include <memory> + +#include "gtest/gtest.h" + +TEST(args_test, basic) { + fmt::dynamic_format_arg_store<fmt::format_context> store; + store.push_back(42); + store.push_back("abc1"); + store.push_back(1.5f); + EXPECT_EQ("42 and abc1 and 1.5", fmt::vformat("{} and {} and {}", store)); +} + +TEST(args_test, strings_and_refs) { + // Unfortunately the tests are compiled with old ABI so strings use COW. + fmt::dynamic_format_arg_store<fmt::format_context> store; + char str[] = "1234567890"; + store.push_back(str); + store.push_back(std::cref(str)); + store.push_back(fmt::string_view{str}); + str[0] = 'X'; + + auto result = fmt::vformat("{} and {} and {}", store); + EXPECT_EQ("1234567890 and X234567890 and X234567890", result); +} + +struct custom_type { + int i = 0; +}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter<custom_type> { + auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template <typename FormatContext> + auto format(const custom_type& p, FormatContext& ctx) -> decltype(ctx.out()) { + return format_to(ctx.out(), "cust={}", p.i); + } +}; +FMT_END_NAMESPACE + +TEST(args_test, custom_format) { + fmt::dynamic_format_arg_store<fmt::format_context> store; + auto c = custom_type(); + store.push_back(c); + ++c.i; + store.push_back(c); + ++c.i; + store.push_back(std::cref(c)); + ++c.i; + auto result = fmt::vformat("{} and {} and {}", store); + EXPECT_EQ("cust=0 and cust=1 and cust=3", result); +} + +struct to_stringable { + friend fmt::string_view to_string_view(to_stringable) { return {}; } +}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter<to_stringable> { + auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) { + return ctx.begin(); + } + + auto format(to_stringable, format_context& ctx) -> decltype(ctx.out()) { + return ctx.out(); + } +}; +FMT_END_NAMESPACE + +TEST(args_test, to_string_and_formatter) { + fmt::dynamic_format_arg_store<fmt::format_context> store; + auto s = to_stringable(); + store.push_back(s); + store.push_back(std::cref(s)); + fmt::vformat("", store); +} + +TEST(args_test, named_int) { + fmt::dynamic_format_arg_store<fmt::format_context> store; + store.push_back(fmt::arg("a1", 42)); + EXPECT_EQ("42", fmt::vformat("{a1}", store)); +} + +TEST(args_test, named_strings) { + fmt::dynamic_format_arg_store<fmt::format_context> store; + char str[] = "1234567890"; + store.push_back(fmt::arg("a1", str)); + store.push_back(fmt::arg("a2", std::cref(str))); + str[0] = 'X'; + EXPECT_EQ("1234567890 and X234567890", fmt::vformat("{a1} and {a2}", store)); +} + +TEST(args_test, named_arg_by_ref) { + fmt::dynamic_format_arg_store<fmt::format_context> store; + char band[] = "Rolling Stones"; + store.push_back(fmt::arg("band", std::cref(band))); + band[9] = 'c'; // Changing band affects the output. + EXPECT_EQ(fmt::vformat("{band}", store), "Rolling Scones"); +} + +TEST(args_test, named_custom_format) { + fmt::dynamic_format_arg_store<fmt::format_context> store; + auto c = custom_type(); + store.push_back(fmt::arg("c1", c)); + ++c.i; + store.push_back(fmt::arg("c2", c)); + ++c.i; + store.push_back(fmt::arg("c_ref", std::cref(c))); + ++c.i; + auto result = fmt::vformat("{c1} and {c2} and {c_ref}", store); + EXPECT_EQ("cust=0 and cust=1 and cust=3", result); +} + +TEST(args_test, clear) { + fmt::dynamic_format_arg_store<fmt::format_context> store; + store.push_back(42); + + auto result = fmt::vformat("{}", store); + EXPECT_EQ("42", result); + + store.push_back(43); + result = fmt::vformat("{} and {}", store); + EXPECT_EQ("42 and 43", result); + + store.clear(); + store.push_back(44); + result = fmt::vformat("{}", store); + EXPECT_EQ("44", result); +} + +TEST(args_test, reserve) { + fmt::dynamic_format_arg_store<fmt::format_context> store; + store.reserve(2, 1); + store.push_back(1.5f); + store.push_back(fmt::arg("a1", 42)); + auto result = fmt::vformat("{a1} and {}", store); + EXPECT_EQ("42 and 1.5", result); +} + +struct copy_throwable { + copy_throwable() {} + copy_throwable(const copy_throwable&) { throw "deal with it"; } +}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter<copy_throwable> { + auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) { + return ctx.begin(); + } + auto format(copy_throwable, format_context& ctx) -> decltype(ctx.out()) { + return ctx.out(); + } +}; +FMT_END_NAMESPACE + +TEST(args_test, throw_on_copy) { + fmt::dynamic_format_arg_store<fmt::format_context> store; + store.push_back(std::string("foo")); + try { + store.push_back(copy_throwable()); + } catch (...) { + } + EXPECT_EQ(fmt::vformat("{}", store), "foo"); +} + +TEST(args_test, move_constructor) { + using store_type = fmt::dynamic_format_arg_store<fmt::format_context>; + auto store = std::unique_ptr<store_type>(new store_type()); + store->push_back(42); + store->push_back(std::string("foo")); + store->push_back(fmt::arg("a1", "foo")); + auto moved_store = std::move(*store); + store.reset(); + EXPECT_EQ(fmt::vformat("{} {} {a1}", moved_store), "42 foo foo"); +} diff --git a/src/fmt/test/assert-test.cc b/src/fmt/test/assert-test.cc new file mode 100644 index 000000000..c74e617e6 --- /dev/null +++ b/src/fmt/test/assert-test.cc @@ -0,0 +1,31 @@ +// Formatting library for C++ - FMT_ASSERT test +// +// It is a separate test to minimize the number of EXPECT_DEBUG_DEATH checks +// which are slow on some platforms. In other tests FMT_ASSERT is made to throw +// an exception which is much faster and easier to check. +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/core.h" +#include "gtest/gtest.h" + +TEST(assert_test, fail) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEBUG_DEATH(FMT_ASSERT(false, "don't panic!"), "don't panic!"); +#else + fmt::print("warning: death tests are not supported\n"); +#endif +} + +TEST(assert_test, dangling_else) { + bool test_condition = false; + bool executed_else = false; + if (test_condition) + FMT_ASSERT(true, ""); + else + executed_else = true; + EXPECT_TRUE(executed_else); +} diff --git a/src/fmt/test/chrono-test.cc b/src/fmt/test/chrono-test.cc new file mode 100644 index 000000000..0f2a2498e --- /dev/null +++ b/src/fmt/test/chrono-test.cc @@ -0,0 +1,643 @@ +// Formatting library for C++ - time formatting tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/chrono.h" + +#include <ctime> +#include <vector> + +#include "gtest-extra.h" // EXPECT_THROW_MSG +#include "util.h" // get_locale + +using fmt::runtime; + +using testing::Contains; + +auto make_tm() -> std::tm { + auto time = std::tm(); + time.tm_mday = 1; + return time; +} + +auto make_hour(int h) -> std::tm { + auto time = make_tm(); + time.tm_hour = h; + return time; +} + +auto make_minute(int m) -> std::tm { + auto time = make_tm(); + time.tm_min = m; + return time; +} + +auto make_second(int s) -> std::tm { + auto time = make_tm(); + time.tm_sec = s; + return time; +} + +std::string system_strftime(const std::string& format, const std::tm* timeptr, + std::locale* locptr = nullptr) { + auto loc = locptr ? *locptr : std::locale::classic(); + auto& facet = std::use_facet<std::time_put<char>>(loc); + std::ostringstream os; + os.imbue(loc); + facet.put(os, os, ' ', timeptr, format.c_str(), + format.c_str() + format.size()); +#ifdef _WIN32 + // Workaround a bug in older versions of Universal CRT. + auto str = os.str(); + if (str == "-0000") str = "+0000"; + return str; +#else + return os.str(); +#endif +} + +FMT_CONSTEXPR std::tm make_tm(int year, int mon, int mday, int hour, int min, + int sec) { + auto tm = std::tm(); + tm.tm_sec = sec; + tm.tm_min = min; + tm.tm_hour = hour; + tm.tm_mday = mday; + tm.tm_mon = mon - 1; + tm.tm_year = year - 1900; + return tm; +} + +TEST(chrono_test, format_tm) { + auto tm = std::tm(); + tm.tm_year = 116; + tm.tm_mon = 3; + tm.tm_mday = 25; + tm.tm_hour = 11; + tm.tm_min = 22; + tm.tm_sec = 33; + EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm), + "The date is 2016-04-25 11:22:33."); + EXPECT_EQ(fmt::format("{:%Y}", tm), "2016"); + EXPECT_EQ(fmt::format("{:%C}", tm), "20"); + EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm)); + EXPECT_EQ(fmt::format("{:%e}", tm), "25"); + EXPECT_EQ(fmt::format("{:%D}", tm), "04/25/16"); + EXPECT_EQ(fmt::format("{:%F}", tm), "2016-04-25"); + EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33"); + + // Short year + tm.tm_year = 999 - 1900; + tm.tm_mon = 0; // for %G + tm.tm_mday = 2; // for %G + tm.tm_wday = 3; // for %G + tm.tm_yday = 1; // for %G + EXPECT_EQ(fmt::format("{:%Y}", tm), "0999"); + EXPECT_EQ(fmt::format("{:%C%y}", tm), "0999"); + EXPECT_EQ(fmt::format("{:%G}", tm), "0999"); + + tm.tm_year = 27 - 1900; + EXPECT_EQ(fmt::format("{:%Y}", tm), "0027"); + EXPECT_EQ(fmt::format("{:%C%y}", tm), "0027"); + + // Overflow year + tm.tm_year = 2147483647; + EXPECT_EQ(fmt::format("{:%Y}", tm), "2147485547"); + + tm.tm_year = -2147483648; + EXPECT_EQ(fmt::format("{:%Y}", tm), "-2147481748"); + + // for week on the year + // https://www.cl.cam.ac.uk/~mgk25/iso-time.html + std::vector<std::tm> tm_list = { + make_tm(1975, 12, 29, 12, 14, 16), // W01 + make_tm(1977, 1, 2, 12, 14, 16), // W53 + make_tm(1999, 12, 27, 12, 14, 16), // W52 + make_tm(1999, 12, 31, 12, 14, 16), // W52 + make_tm(2000, 1, 1, 12, 14, 16), // W52 + make_tm(2000, 1, 2, 12, 14, 16), // W52 + make_tm(2000, 1, 3, 12, 14, 16) // W1 + }; + +#if defined(__MINGW32__) && !defined(_UCRT) + GTEST_SKIP() << "Skip the rest of this test because it relies on strftime() " + "conforming to C99, but on this platform, MINGW + MSVCRT, " + "the function conforms only to C89."; +#endif + + const std::string iso_week_spec = "%Y-%m-%d: %G %g %V"; + for (auto ctm : tm_list) { + // Calculate tm_yday, tm_wday, etc. + std::time_t t = std::mktime(&ctm); + tm = *std::localtime(&t); + + auto fmt_spec = fmt::format("{{:{}}}", iso_week_spec); + EXPECT_EQ(system_strftime(iso_week_spec, &tm), + fmt::format(fmt::runtime(fmt_spec), tm)); + } + + // Every day from 1970-01-01 + std::time_t time_now = std::time(nullptr); + for (std::time_t t = 6 * 3600; t < time_now; t += 86400) { + tm = *std::localtime(&t); + + auto fmt_spec = fmt::format("{{:{}}}", iso_week_spec); + EXPECT_EQ(system_strftime(iso_week_spec, &tm), + fmt::format(fmt::runtime(fmt_spec), tm)); + } +} + +// MSVC: +// minkernel\crts\ucrt\src\appcrt\time\wcsftime.cpp(971) : Assertion failed: +// timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099 +#ifndef _WIN32 +TEST(chrono_test, format_tm_future) { + auto tm = std::tm(); + tm.tm_year = 10445; // 10000+ years + tm.tm_mon = 3; + tm.tm_mday = 25; + tm.tm_hour = 11; + tm.tm_min = 22; + tm.tm_sec = 33; + EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm), + "The date is 12345-04-25 11:22:33."); + EXPECT_EQ(fmt::format("{:%Y}", tm), "12345"); + EXPECT_EQ(fmt::format("{:%C}", tm), "123"); + EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm)); + EXPECT_EQ(fmt::format("{:%D}", tm), "04/25/45"); + EXPECT_EQ(fmt::format("{:%F}", tm), "12345-04-25"); + EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33"); +} + +TEST(chrono_test, format_tm_past) { + auto tm = std::tm(); + tm.tm_year = -2001; + tm.tm_mon = 3; + tm.tm_mday = 25; + tm.tm_hour = 11; + tm.tm_min = 22; + tm.tm_sec = 33; + EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm), + "The date is -101-04-25 11:22:33."); + EXPECT_EQ(fmt::format("{:%Y}", tm), "-101"); + + // macOS %C - "-1" + // Linux %C - "-2" + // fmt %C - "-1" + EXPECT_EQ(fmt::format("{:%C}", tm), "-1"); + EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm)); + + // macOS %D - "04/25/01" (%y) + // Linux %D - "04/25/99" (%y) + // fmt %D - "04/25/01" (%y) + EXPECT_EQ(fmt::format("{:%D}", tm), "04/25/01"); + + EXPECT_EQ(fmt::format("{:%F}", tm), "-101-04-25"); + EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33"); + + tm.tm_year = -1901; // -1 + EXPECT_EQ(fmt::format("{:%Y}", tm), "-001"); + EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm)); + + tm.tm_year = -1911; // -11 + EXPECT_EQ(fmt::format("{:%Y}", tm), "-011"); + EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm)); +} +#endif + +TEST(chrono_test, grow_buffer) { + auto s = std::string("{:"); + for (int i = 0; i < 30; ++i) s += "%c"; + s += "}\n"; + auto t = std::time(nullptr); + (void)fmt::format(fmt::runtime(s), *std::localtime(&t)); +} + +TEST(chrono_test, format_to_empty_container) { + auto time = std::tm(); + time.tm_sec = 42; + auto s = std::string(); + fmt::format_to(std::back_inserter(s), "{:%S}", time); + EXPECT_EQ(s, "42"); +} + +TEST(chrono_test, empty_result) { EXPECT_EQ(fmt::format("{}", std::tm()), ""); } + +auto equal(const std::tm& lhs, const std::tm& rhs) -> bool { + return lhs.tm_sec == rhs.tm_sec && lhs.tm_min == rhs.tm_min && + lhs.tm_hour == rhs.tm_hour && lhs.tm_mday == rhs.tm_mday && + lhs.tm_mon == rhs.tm_mon && lhs.tm_year == rhs.tm_year && + lhs.tm_wday == rhs.tm_wday && lhs.tm_yday == rhs.tm_yday && + lhs.tm_isdst == rhs.tm_isdst; +} + +TEST(chrono_test, localtime) { + auto t = std::time(nullptr); + auto tm = *std::localtime(&t); + EXPECT_TRUE(equal(tm, fmt::localtime(t))); +} + +TEST(chrono_test, gmtime) { + auto t = std::time(nullptr); + auto tm = *std::gmtime(&t); + EXPECT_TRUE(equal(tm, fmt::gmtime(t))); +} + +template <typename TimePoint> auto strftime_full(TimePoint tp) -> std::string { + auto t = std::chrono::system_clock::to_time_t(tp); + auto tm = *std::localtime(&t); + return system_strftime("%Y-%m-%d %H:%M:%S", &tm); +} + +TEST(chrono_test, time_point) { + auto t1 = std::chrono::system_clock::now(); + EXPECT_EQ(strftime_full(t1), fmt::format("{:%Y-%m-%d %H:%M:%S}", t1)); + EXPECT_EQ(strftime_full(t1), fmt::format("{}", t1)); + using time_point = + std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>; + auto t2 = time_point(std::chrono::seconds(42)); + EXPECT_EQ(strftime_full(t2), fmt::format("{:%Y-%m-%d %H:%M:%S}", t2)); + + std::vector<std::string> spec_list = { + "%%", "%n", "%t", "%Y", "%EY", "%y", "%Oy", "%Ey", "%C", + "%EC", "%G", "%g", "%b", "%h", "%B", "%m", "%Om", "%U", + "%OU", "%W", "%OW", "%V", "%OV", "%j", "%d", "%Od", "%e", + "%Oe", "%a", "%A", "%w", "%Ow", "%u", "%Ou", "%H", "%OH", + "%I", "%OI", "%M", "%OM", "%S", "%OS", "%x", "%Ex", "%X", + "%EX", "%D", "%F", "%R", "%T", "%p", "%z", "%Z"}; +#ifndef _WIN32 + // Disabled on Windows because these formats are not consistent among + // platforms. + spec_list.insert(spec_list.end(), {"%c", "%Ec", "%r"}); +#elif defined(__MINGW32__) && !defined(_UCRT) + // Only C89 conversion specifiers when using MSVCRT instead of UCRT + spec_list = {"%%", "%Y", "%y", "%b", "%B", "%m", "%U", "%W", "%j", "%d", "%a", + "%A", "%w", "%H", "%I", "%M", "%S", "%x", "%X", "%p", "%Z"}; +#endif + spec_list.push_back("%Y-%m-%d %H:%M:%S"); + + for (const auto& spec : spec_list) { + auto t = std::chrono::system_clock::to_time_t(t1); + auto tm = *std::localtime(&t); + + auto sys_output = system_strftime(spec, &tm); + + auto fmt_spec = fmt::format("{{:{}}}", spec); + EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1)); + EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm)); + } +} + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR + +TEST(chrono_test, format_default) { + EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42))); + EXPECT_EQ("42as", + fmt::format("{}", std::chrono::duration<int, std::atto>(42))); + EXPECT_EQ("42fs", + fmt::format("{}", std::chrono::duration<int, std::femto>(42))); + EXPECT_EQ("42ps", + fmt::format("{}", std::chrono::duration<int, std::pico>(42))); + EXPECT_EQ("42ns", fmt::format("{}", std::chrono::nanoseconds(42))); + EXPECT_EQ("42µs", fmt::format("{}", std::chrono::microseconds(42))); + EXPECT_EQ("42ms", fmt::format("{}", std::chrono::milliseconds(42))); + EXPECT_EQ("42cs", + fmt::format("{}", std::chrono::duration<int, std::centi>(42))); + EXPECT_EQ("42ds", + fmt::format("{}", std::chrono::duration<int, std::deci>(42))); + EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42))); + EXPECT_EQ("42das", + fmt::format("{}", std::chrono::duration<int, std::deca>(42))); + EXPECT_EQ("42hs", + fmt::format("{}", std::chrono::duration<int, std::hecto>(42))); + EXPECT_EQ("42ks", + fmt::format("{}", std::chrono::duration<int, std::kilo>(42))); + EXPECT_EQ("42Ms", + fmt::format("{}", std::chrono::duration<int, std::mega>(42))); + EXPECT_EQ("42Gs", + fmt::format("{}", std::chrono::duration<int, std::giga>(42))); + EXPECT_EQ("42Ts", + fmt::format("{}", std::chrono::duration<int, std::tera>(42))); + EXPECT_EQ("42Ps", + fmt::format("{}", std::chrono::duration<int, std::peta>(42))); + EXPECT_EQ("42Es", + fmt::format("{}", std::chrono::duration<int, std::exa>(42))); + EXPECT_EQ("42m", fmt::format("{}", std::chrono::minutes(42))); + EXPECT_EQ("42h", fmt::format("{}", std::chrono::hours(42))); + EXPECT_EQ( + "42[15]s", + fmt::format("{}", std::chrono::duration<int, std::ratio<15, 1>>(42))); + EXPECT_EQ( + "42[15/4]s", + fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(42))); +} + +TEST(chrono_test, align) { + auto s = std::chrono::seconds(42); + EXPECT_EQ("42s ", fmt::format("{:5}", s)); + EXPECT_EQ("42s ", fmt::format("{:{}}", s, 5)); + EXPECT_EQ(" 42s", fmt::format("{:>5}", s)); + EXPECT_EQ("**42s**", fmt::format("{:*^7}", s)); + EXPECT_EQ("03:25:45 ", + fmt::format("{:12%H:%M:%S}", std::chrono::seconds(12345))); + EXPECT_EQ(" 03:25:45", + fmt::format("{:>12%H:%M:%S}", std::chrono::seconds(12345))); + EXPECT_EQ("~~03:25:45~~", + fmt::format("{:~^12%H:%M:%S}", std::chrono::seconds(12345))); + EXPECT_EQ("03:25:45 ", + fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12)); +} + +TEST(chrono_test, format_specs) { + EXPECT_EQ("%", fmt::format("{:%%}", std::chrono::seconds(0))); + EXPECT_EQ("\n", fmt::format("{:%n}", std::chrono::seconds(0))); + EXPECT_EQ("\t", fmt::format("{:%t}", std::chrono::seconds(0))); + EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(0))); + EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(60))); + EXPECT_EQ("42", fmt::format("{:%S}", std::chrono::seconds(42))); + EXPECT_EQ("01.234", fmt::format("{:%S}", std::chrono::milliseconds(1234))); + EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(0))); + EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(60))); + EXPECT_EQ("42", fmt::format("{:%M}", std::chrono::minutes(42))); + EXPECT_EQ("01", fmt::format("{:%M}", std::chrono::seconds(61))); + EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(0))); + EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(24))); + EXPECT_EQ("14", fmt::format("{:%H}", std::chrono::hours(14))); + EXPECT_EQ("01", fmt::format("{:%H}", std::chrono::minutes(61))); + EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(0))); + EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(12))); + EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(24))); + EXPECT_EQ("04", fmt::format("{:%I}", std::chrono::hours(4))); + EXPECT_EQ("02", fmt::format("{:%I}", std::chrono::hours(14))); + EXPECT_EQ("03:25:45", + fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345))); + EXPECT_EQ("03:25", fmt::format("{:%R}", std::chrono::seconds(12345))); + EXPECT_EQ("03:25:45", fmt::format("{:%T}", std::chrono::seconds(12345))); + EXPECT_EQ("12345", fmt::format("{:%Q}", std::chrono::seconds(12345))); + EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(12345))); +} + +TEST(chrono_test, invalid_specs) { + auto sec = std::chrono::seconds(0); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%a}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%A}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%c}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%x}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Ex}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%X}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%EX}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%D}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%F}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Ec}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%w}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%u}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%b}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%B}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%z}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Z}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Eq}"), sec), fmt::format_error, + "invalid format"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Oq}"), sec), fmt::format_error, + "invalid format"); +} + +auto format_tm(const std::tm& time, fmt::string_view spec, + const std::locale& loc) -> std::string { + auto& facet = std::use_facet<std::time_put<char>>(loc); + std::ostringstream os; + os.imbue(loc); + facet.put(os, os, ' ', &time, spec.begin(), spec.end()); + return os.str(); +} + +TEST(chrono_test, locale) { + auto loc = get_locale("ja_JP.utf8"); + if (loc == std::locale::classic()) return; +# define EXPECT_TIME(spec, time, duration) \ + { \ + auto jp_loc = std::locale("ja_JP.utf8"); \ + EXPECT_EQ(format_tm(time, spec, jp_loc), \ + fmt::format(jp_loc, "{:L" spec "}", duration)); \ + } + EXPECT_TIME("%OH", make_hour(14), std::chrono::hours(14)); + EXPECT_TIME("%OI", make_hour(14), std::chrono::hours(14)); + EXPECT_TIME("%OM", make_minute(42), std::chrono::minutes(42)); + EXPECT_TIME("%OS", make_second(42), std::chrono::seconds(42)); + auto time = make_tm(); + time.tm_hour = 3; + time.tm_min = 25; + time.tm_sec = 45; + auto sec = std::chrono::seconds(12345); + EXPECT_TIME("%r", time, sec); + EXPECT_TIME("%p", time, sec); +} + +using dms = std::chrono::duration<double, std::milli>; + +TEST(chrono_test, format_default_fp) { + typedef std::chrono::duration<float> fs; + EXPECT_EQ("1.234s", fmt::format("{}", fs(1.234))); + typedef std::chrono::duration<float, std::milli> fms; + EXPECT_EQ("1.234ms", fmt::format("{}", fms(1.234))); + typedef std::chrono::duration<double> ds; + EXPECT_EQ("1.234s", fmt::format("{}", ds(1.234))); + EXPECT_EQ("1.234ms", fmt::format("{}", dms(1.234))); +} + +TEST(chrono_test, format_precision) { + EXPECT_THROW_MSG( + (void)fmt::format(runtime("{:.2}"), std::chrono::seconds(42)), + fmt::format_error, "precision not allowed for this argument type"); + EXPECT_EQ("1ms", fmt::format("{:.0}", dms(1.234))); + EXPECT_EQ("1.2ms", fmt::format("{:.1}", dms(1.234))); + EXPECT_EQ("1.23ms", fmt::format("{:.{}}", dms(1.234), 2)); + + EXPECT_EQ("13ms", fmt::format("{:.0}", dms(12.56))); + EXPECT_EQ("12.6ms", fmt::format("{:.1}", dms(12.56))); + EXPECT_EQ("12.56ms", fmt::format("{:.2}", dms(12.56))); +} + +TEST(chrono_test, format_full_specs) { + EXPECT_EQ("1ms ", fmt::format("{:6.0}", dms(1.234))); + EXPECT_EQ("1.2ms ", fmt::format("{:6.1}", dms(1.234))); + EXPECT_EQ(" 1.23ms", fmt::format("{:>8.{}}", dms(1.234), 2)); + EXPECT_EQ(" 1.2ms ", fmt::format("{:^{}.{}}", dms(1.234), 7, 1)); + EXPECT_EQ(" 1.23ms ", fmt::format("{0:^{2}.{1}}", dms(1.234), 2, 8)); + EXPECT_EQ("=1.234ms=", fmt::format("{:=^{}.{}}", dms(1.234), 9, 3)); + EXPECT_EQ("*1.2340ms*", fmt::format("{:*^10.4}", dms(1.234))); + + EXPECT_EQ("13ms ", fmt::format("{:6.0}", dms(12.56))); + EXPECT_EQ(" 13ms", fmt::format("{:>8.{}}", dms(12.56), 0)); + EXPECT_EQ(" 13ms ", fmt::format("{:^{}.{}}", dms(12.56), 6, 0)); + EXPECT_EQ(" 13ms ", fmt::format("{0:^{2}.{1}}", dms(12.56), 0, 8)); + EXPECT_EQ("==13ms===", fmt::format("{:=^{}.{}}", dms(12.56), 9, 0)); + EXPECT_EQ("***13ms***", fmt::format("{:*^10.0}", dms(12.56))); +} + +TEST(chrono_test, format_simple_q) { + typedef std::chrono::duration<float> fs; + EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", fs(1.234))); + typedef std::chrono::duration<float, std::milli> fms; + EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", fms(1.234))); + typedef std::chrono::duration<double> ds; + EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", ds(1.234))); + EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", dms(1.234))); +} + +TEST(chrono_test, format_precision_q) { + EXPECT_THROW_MSG( + (void)fmt::format(runtime("{:.2%Q %q}"), std::chrono::seconds(42)), + fmt::format_error, "precision not allowed for this argument type"); + EXPECT_EQ("1.2 ms", fmt::format("{:.1%Q %q}", dms(1.234))); + EXPECT_EQ("1.23 ms", fmt::format("{:.{}%Q %q}", dms(1.234), 2)); +} + +TEST(chrono_test, format_full_specs_q) { + EXPECT_EQ("1 ms ", fmt::format("{:7.0%Q %q}", dms(1.234))); + EXPECT_EQ("1.2 ms ", fmt::format("{:7.1%Q %q}", dms(1.234))); + EXPECT_EQ(" 1.23 ms", fmt::format("{:>8.{}%Q %q}", dms(1.234), 2)); + EXPECT_EQ(" 1.2 ms ", fmt::format("{:^{}.{}%Q %q}", dms(1.234), 8, 1)); + EXPECT_EQ(" 1.23 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(1.234), 2, 9)); + EXPECT_EQ("=1.234 ms=", fmt::format("{:=^{}.{}%Q %q}", dms(1.234), 10, 3)); + EXPECT_EQ("*1.2340 ms*", fmt::format("{:*^11.4%Q %q}", dms(1.234))); + + EXPECT_EQ("13 ms ", fmt::format("{:7.0%Q %q}", dms(12.56))); + EXPECT_EQ(" 13 ms", fmt::format("{:>8.{}%Q %q}", dms(12.56), 0)); + EXPECT_EQ(" 13 ms ", fmt::format("{:^{}.{}%Q %q}", dms(12.56), 8, 0)); + EXPECT_EQ(" 13 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(12.56), 0, 9)); + EXPECT_EQ("==13 ms==", fmt::format("{:=^{}.{}%Q %q}", dms(12.56), 9, 0)); + EXPECT_EQ("***13 ms***", fmt::format("{:*^11.0%Q %q}", dms(12.56))); +} + +TEST(chrono_test, invalid_width_id) { + EXPECT_THROW((void)fmt::format(runtime("{:{o}"), std::chrono::seconds(0)), + fmt::format_error); +} + +TEST(chrono_test, invalid_colons) { + EXPECT_THROW((void)fmt::format(runtime("{0}=:{0::"), std::chrono::seconds(0)), + fmt::format_error); +} + +TEST(chrono_test, negative_durations) { + EXPECT_EQ("-12345", fmt::format("{:%Q}", std::chrono::seconds(-12345))); + EXPECT_EQ("-03:25:45", + fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345))); + EXPECT_EQ("-00:01", + fmt::format("{:%M:%S}", std::chrono::duration<double>(-1))); + EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(-12345))); + EXPECT_EQ("-00.127", + fmt::format("{:%S}", + std::chrono::duration<signed char, std::milli>{-127})); + auto min = std::numeric_limits<int>::min(); + EXPECT_EQ(fmt::format("{}", min), + fmt::format("{:%Q}", std::chrono::duration<int>(min))); +} + +TEST(chrono_test, special_durations) { + auto value = fmt::format("{:%S}", std::chrono::duration<double>(1e20)); + EXPECT_EQ(value, "40"); + auto nan = std::numeric_limits<double>::quiet_NaN(); + EXPECT_EQ( + "nan nan nan nan nan:nan nan", + fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration<double>(nan))); + EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::exa>(1)), + "1Es"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::atto>(1)), + "1as"); + EXPECT_EQ(fmt::format("{:%R}", std::chrono::duration<char, std::mega>{2}), + "03:33"); + EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration<char, std::mega>{2}), + "03:33:20"); + EXPECT_EQ("44.000000000000", + fmt::format("{:%S}", std::chrono::duration<float, std::pico>( + 1.54213895E+26))); +} + +TEST(chrono_test, unsigned_duration) { + EXPECT_EQ("42s", fmt::format("{}", std::chrono::duration<unsigned>(42))); +} + +TEST(chrono_test, weekday) { + auto loc = get_locale("ru_RU.UTF-8"); + std::locale::global(loc); + auto mon = fmt::weekday(1); + + auto tm = std::tm(); + tm.tm_wday = static_cast<int>(mon.c_encoding()); + + EXPECT_EQ(fmt::format("{}", mon), "Mon"); + EXPECT_EQ(fmt::format("{:%a}", tm), "Mon"); + + if (loc != std::locale::classic()) { + EXPECT_THAT((std::vector<std::string>{"пн", "Пн", "пнд", "Пнд"}), + Contains(fmt::format(loc, "{:L}", mon))); + EXPECT_THAT((std::vector<std::string>{"пн", "Пн", "пнд", "Пнд"}), + Contains(fmt::format(loc, "{:%a}", tm))); + } +} + +TEST(chrono_test, cpp20_duration_subsecond_support) { + using attoseconds = std::chrono::duration<long long, std::atto>; + // Check that 18 digits of subsecond precision are supported. + EXPECT_EQ(fmt::format("{:%S}", attoseconds{999999999999999999}), + "00.999999999999999999"); + EXPECT_EQ(fmt::format("{:%S}", attoseconds{673231113420148734}), + "00.673231113420148734"); + EXPECT_EQ(fmt::format("{:%S}", attoseconds{-673231113420148734}), + "-00.673231113420148734"); + EXPECT_EQ(fmt::format("{:%S}", std::chrono::nanoseconds{13420148734}), + "13.420148734"); + EXPECT_EQ(fmt::format("{:%S}", std::chrono::nanoseconds{-13420148734}), + "-13.420148734"); + EXPECT_EQ(fmt::format("{:%S}", std::chrono::milliseconds{1234}), "01.234"); + { + // Check that {:%H:%M:%S} is equivalent to {:%T}. + auto dur = std::chrono::milliseconds{3601234}; + auto formatted_dur = fmt::format("{:%T}", dur); + EXPECT_EQ(formatted_dur, "01:00:01.234"); + EXPECT_EQ(fmt::format("{:%H:%M:%S}", dur), formatted_dur); + } + using nanoseconds_dbl = std::chrono::duration<double, std::nano>; + EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{-123456789}), "-00.123456789"); + EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{9123456789}), "09.123456789"); + // Verify that only the seconds part is extracted and printed. + EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{99123456789}), "39.123456789"); + EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{99123000000}), "39.123000000"); + { + // Now the hour is printed, and we also test if negative doubles work. + auto dur = nanoseconds_dbl{-99123456789}; + auto formatted_dur = fmt::format("{:%T}", dur); + EXPECT_EQ(formatted_dur, "-00:01:39.123456789"); + EXPECT_EQ(fmt::format("{:%H:%M:%S}", dur), formatted_dur); + } + // Check that durations with precision greater than std::chrono::seconds have + // fixed precision, and print zeros even if there is no fractional part. + EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}), + "07.000000"); + EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 3>>(1)), + "00.333333"); + EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 7>>(1)), + "00.142857"); +} + +#endif // FMT_STATIC_THOUSANDS_SEPARATOR diff --git a/src/fmt/test/color-test.cc b/src/fmt/test/color-test.cc new file mode 100644 index 000000000..c2ba13a97 --- /dev/null +++ b/src/fmt/test/color-test.cc @@ -0,0 +1,72 @@ +// Formatting library for C++ - color tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/color.h" + +#include <iterator> // std::back_inserter + +#include "gtest-extra.h" // EXPECT_WRITE + +TEST(color_test, format) { + EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), + "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); + EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue"), + "\x1b[38;2;000;000;255mblue\x1b[0m"); + EXPECT_EQ( + fmt::format(fg(fmt::color::blue) | bg(fmt::color::red), "two color"), + "\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold"), "\x1b[1mbold\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::faint, "faint"), "\x1b[2mfaint\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::italic, "italic"), + "\x1b[3mitalic\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::underline, "underline"), + "\x1b[4munderline\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::blink, "blink"), "\x1b[5mblink\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::reverse, "reverse"), + "\x1b[7mreverse\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::conceal, "conceal"), + "\x1b[8mconceal\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::strikethrough, "strikethrough"), + "\x1b[9mstrikethrough\x1b[0m"); + EXPECT_EQ( + fmt::format(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"), + "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold error"), + "\x1b[1mbold error\x1b[0m"); + EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue log"), + "\x1b[38;2;000;000;255mblue log\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::text_style(), "hi"), "hi"); + EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "tred"), + "\x1b[31mtred\x1b[0m"); + EXPECT_EQ(fmt::format(bg(fmt::terminal_color::cyan), "tcyan"), + "\x1b[46mtcyan\x1b[0m"); + EXPECT_EQ(fmt::format(fg(fmt::terminal_color::bright_green), "tbgreen"), + "\x1b[92mtbgreen\x1b[0m"); + EXPECT_EQ(fmt::format(bg(fmt::terminal_color::bright_magenta), "tbmagenta"), + "\x1b[105mtbmagenta\x1b[0m"); + EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"), + "\x1b[31mfoo\x1b[0m"); + EXPECT_EQ(fmt::format("{}{}", fmt::styled("red", fg(fmt::color::red)), + fmt::styled("bold", fmt::emphasis::bold)), + "\x1b[38;2;255;000;000mred\x1b[0m\x1b[1mbold\x1b[0m"); + EXPECT_EQ(fmt::format("{}", fmt::styled("bar", fg(fmt::color::blue) | + fmt::emphasis::underline)), + "\x1b[4m\x1b[38;2;000;000;255mbar\x1b[0m"); +} + +TEST(color_test, format_to) { + auto out = std::string(); + fmt::format_to(std::back_inserter(out), fg(fmt::rgb(255, 20, 30)), + "rgb(255,20,30){}{}{}", 1, 2, 3); + EXPECT_EQ(fmt::to_string(out), + "\x1b[38;2;255;020;030mrgb(255,20,30)123\x1b[0m"); +} + +TEST(color_test, print) { + EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), + "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); +} diff --git a/src/fmt/test/compile-error-test/CMakeLists.txt b/src/fmt/test/compile-error-test/CMakeLists.txt new file mode 100644 index 000000000..9c65dfdce --- /dev/null +++ b/src/fmt/test/compile-error-test/CMakeLists.txt @@ -0,0 +1,241 @@ +# Test if compile errors are produced where necessary. + +cmake_minimum_required(VERSION 3.1...3.18) +project(compile-error-test CXX) + +set(fmt_headers " + #include <fmt/format.h> + #include <fmt/xchar.h> + #include <fmt/ostream.h> + #include <iostream> +") + +set(error_test_names "") +set(non_error_test_content "") + +# For error tests (we expect them to produce compilation error): +# * adds a name of test into `error_test_names` list +# * generates a single source file (with the same name) for each test +# For non-error tests (we expect them to compile successfully): +# * adds a code segment as separate function to `non_error_test_content` +function (expect_compile name code_fragment) + cmake_parse_arguments(EXPECT_COMPILE "ERROR" "" "" ${ARGN}) + string(MAKE_C_IDENTIFIER "${name}" test_name) + + if (EXPECT_COMPILE_ERROR) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/${test_name}.cc" " + ${fmt_headers} + void ${test_name}() { + ${code_fragment} + } + ") + set(error_test_names_copy "${error_test_names}") + list(APPEND error_test_names_copy "${test_name}") + set(error_test_names "${error_test_names_copy}" PARENT_SCOPE) + else() + set(non_error_test_content " + ${non_error_test_content} + void ${test_name}() { + ${code_fragment} + }" PARENT_SCOPE) + endif() +endfunction () + +# Generates a source file for non-error test with `non_error_test_content` and +# CMake project file with all error and single non-error test targets. +function (run_tests) + set(cmake_targets "") + foreach(test_name IN LISTS error_test_names) + set(cmake_targets " + ${cmake_targets} + add_library(test-${test_name} ${test_name}.cc) + target_link_libraries(test-${test_name} PRIVATE fmt::fmt) + ") + endforeach() + + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/non_error_test.cc" " + ${fmt_headers} + ${non_error_test_content} + ") + set(cmake_targets " + ${cmake_targets} + add_library(non-error-test non_error_test.cc) + target_link_libraries(non-error-test PRIVATE fmt::fmt) + ") + + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/CMakeLists.txt" " + cmake_minimum_required(VERSION 3.1...3.18) + project(tests CXX) + add_subdirectory(${FMT_DIR} fmt) + ${cmake_targets} + ") + + set(build_directory "${CMAKE_CURRENT_BINARY_DIR}/test/build") + file(MAKE_DIRECTORY "${build_directory}") + execute_process( + COMMAND + "${CMAKE_COMMAND}" + "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}" + "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" + "-DCMAKE_GENERATOR=${CMAKE_GENERATOR}" + "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}" + "-DFMT_DIR=${FMT_DIR}" + "${CMAKE_CURRENT_BINARY_DIR}/test" + WORKING_DIRECTORY "${build_directory}" + RESULT_VARIABLE result_var + OUTPUT_VARIABLE output_var + ERROR_VARIABLE output_var) + if (NOT result_var EQUAL 0) + message(FATAL_ERROR "Unable to configure:\n${output_var}") + endif() + + foreach(test_name IN LISTS error_test_names) + execute_process( + COMMAND + "${CMAKE_COMMAND}" --build "${build_directory}" --target "test-${test_name}" + WORKING_DIRECTORY "${build_directory}" + RESULT_VARIABLE result_var + OUTPUT_VARIABLE output_var + ERROR_QUIET) + if (result_var EQUAL 0) + message(SEND_ERROR "No compile error for \"${test_name}\":\n${output_var}") + endif () + endforeach() + + execute_process( + COMMAND + "${CMAKE_COMMAND}" --build "${build_directory}" --target "non-error-test" + WORKING_DIRECTORY "${build_directory}" + RESULT_VARIABLE result_var + OUTPUT_VARIABLE output_var + ERROR_VARIABLE output_var) + if (NOT result_var EQUAL 0) + message(SEND_ERROR "Compile error for combined non-error test:\n${output_var}") + endif () +endfunction () + + +# check if the source file skeleton compiles +expect_compile(check "") +expect_compile(check-error "compilation_error" ERROR) + +# Formatting a wide character with a narrow format string is forbidden. +expect_compile(wide-character-narrow-format-string "fmt::format(L\"{}\", L'a');") +expect_compile(wide-character-narrow-format-string-error "fmt::format(\"{}\", L'a');" ERROR) + +# Formatting a wide string with a narrow format string is forbidden. +expect_compile(wide-string-narrow-format-string "fmt::format(L\"{}\", L\"foo\");") +expect_compile(wide-string-narrow-format-string-error "fmt::format(\"{}\", L\"foo\");" ERROR) + +# Formatting a narrow string with a wide format string is forbidden because +# mixing UTF-8 with UTF-16/32 can result in an invalid output. +expect_compile(narrow-string-wide-format-string "fmt::format(L\"{}\", L\"foo\");") +expect_compile(narrow-string-wide-format-string-error "fmt::format(L\"{}\", \"foo\");" ERROR) + +expect_compile(cast-to-string " + struct S { + operator std::string() const { return std::string(); } + }; + fmt::format(\"{}\", std::string(S())); +") +expect_compile(cast-to-string-error " + struct S { + operator std::string() const { return std::string(); } + }; + fmt::format(\"{}\", S()); +" ERROR) + +# Formatting a function +expect_compile(format-function " + void (*f)(); + fmt::format(\"{}\", fmt::ptr(f)); +") +expect_compile(format-function-error " + void (*f)(); + fmt::format(\"{}\", f); +" ERROR) + +# Formatting an unformattable argument should always be a compile time error +expect_compile(format-lots-of-arguments-with-unformattable " + struct E {}; + fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, E()); +" ERROR) +expect_compile(format-lots-of-arguments-with-function " + void (*f)(); + fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, f); +" ERROR) + +# Check if user-defined literals are available +include(CheckCXXSourceCompiles) +set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG}) +check_cxx_source_compiles(" + void operator\"\" _udl(long double); + int main() {}" + SUPPORTS_USER_DEFINED_LITERALS) +set(CMAKE_REQUIRED_FLAGS ) +if (NOT SUPPORTS_USER_DEFINED_LITERALS) + set (SUPPORTS_USER_DEFINED_LITERALS OFF) +endif () + +# Make sure that compiler features detected in the header +# match the features detected in CMake. +if (SUPPORTS_USER_DEFINED_LITERALS) + set(supports_udl 1) +else () + set(supports_udl 0) +endif () +expect_compile(udl-check " + #if FMT_USE_USER_DEFINED_LITERALS != ${supports_udl} + # error + #endif +") + +if (CMAKE_CXX_STANDARD GREATER_EQUAL 20) + # Compile-time argument type check + expect_compile(format-string-number-spec " + #ifdef FMT_HAS_CONSTEVAL + fmt::format(\"{:d}\", 42); + #endif + ") + expect_compile(format-string-number-spec-error " + #ifdef FMT_HAS_CONSTEVAL + fmt::format(\"{:d}\", \"I am not a number\"); + #else + #error + #endif + " ERROR) + expect_compile(print-string-number-spec-error " + #ifdef FMT_HAS_CONSTEVAL + fmt::print(\"{:d}\", \"I am not a number\"); + #else + #error + #endif + " ERROR) + expect_compile(print-stream-string-number-spec-error " + #ifdef FMT_HAS_CONSTEVAL + fmt::print(std::cout, \"{:d}\", \"I am not a number\"); + #else + #error + #endif + " ERROR) + + # Compile-time argument name check + expect_compile(format-string-name " + #if defined(FMT_HAS_CONSTEVAL) && FMT_USE_NONTYPE_TEMPLATE_ARGS + using namespace fmt::literals; + fmt::print(\"{foo}\", \"foo\"_a=42); + #endif + ") + expect_compile(format-string-name-error " + #if defined(FMT_HAS_CONSTEVAL) && FMT_USE_NONTYPE_TEMPLATE_ARGS + using namespace fmt::literals; + fmt::print(\"{foo}\", \"bar\"_a=42); + #else + #error + #endif + " ERROR) +endif () + +# Run all tests +run_tests() diff --git a/src/fmt/test/compile-fp-test.cc b/src/fmt/test/compile-fp-test.cc new file mode 100644 index 000000000..db0cd906c --- /dev/null +++ b/src/fmt/test/compile-fp-test.cc @@ -0,0 +1,62 @@ +// Formatting library for C++ - formatting library tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/compile.h" +#include "gmock/gmock.h" + +#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 && \ + defined(__cpp_constexpr) && __cpp_constexpr >= 201907 && \ + defined(__cpp_constexpr_dynamic_alloc) && \ + __cpp_constexpr_dynamic_alloc >= 201907 && FMT_CPLUSPLUS >= 202002L +template <size_t max_string_length, typename Char = char> struct test_string { + template <typename T> constexpr bool operator==(const T& rhs) const noexcept { + return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0; + } + Char buffer[max_string_length]{}; +}; + +template <size_t max_string_length, typename Char = char, typename... Args> +consteval auto test_format(auto format, const Args&... args) { + test_string<max_string_length, Char> string{}; + fmt::format_to(string.buffer, format, args...); + return string; +} + +TEST(compile_time_formatting_test, floating_point) { + EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{}"), 0.0f)); + EXPECT_EQ("392.500000", test_format<11>(FMT_COMPILE("{0:f}"), 392.5f)); + + EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:}"), 0.0)); + EXPECT_EQ("0.000000", test_format<9>(FMT_COMPILE("{:f}"), 0.0)); + EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:g}"), 0.0)); + EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:}"), 392.65)); + EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:g}"), 392.65)); + EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:G}"), 392.65)); + EXPECT_EQ("4.9014e+06", test_format<11>(FMT_COMPILE("{:g}"), 4.9014e6)); + EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:f}"), -392.65)); + EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:F}"), -392.65)); + + EXPECT_EQ("3.926500e+02", test_format<13>(FMT_COMPILE("{0:e}"), 392.65)); + EXPECT_EQ("3.926500E+02", test_format<13>(FMT_COMPILE("{0:E}"), 392.65)); + EXPECT_EQ("+0000392.6", test_format<11>(FMT_COMPILE("{0:+010.4g}"), 392.65)); + EXPECT_EQ("9223372036854775808.000000", + test_format<27>(FMT_COMPILE("{:f}"), 9223372036854775807.0)); + + constexpr double nan = std::numeric_limits<double>::quiet_NaN(); + EXPECT_EQ("nan", test_format<4>(FMT_COMPILE("{}"), nan)); + EXPECT_EQ("+nan", test_format<5>(FMT_COMPILE("{:+}"), nan)); + if (std::signbit(-nan)) + EXPECT_EQ("-nan", test_format<5>(FMT_COMPILE("{}"), -nan)); + else + fmt::print("Warning: compiler doesn't handle negative NaN correctly"); + + constexpr double inf = std::numeric_limits<double>::infinity(); + EXPECT_EQ("inf", test_format<4>(FMT_COMPILE("{}"), inf)); + EXPECT_EQ("+inf", test_format<5>(FMT_COMPILE("{:+}"), inf)); + EXPECT_EQ("-inf", test_format<5>(FMT_COMPILE("{}"), -inf)); +} +#endif diff --git a/src/fmt/test/compile-test.cc b/src/fmt/test/compile-test.cc new file mode 100644 index 000000000..005c49930 --- /dev/null +++ b/src/fmt/test/compile-test.cc @@ -0,0 +1,392 @@ +// Formatting library for C++ - formatting library tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/compile.h" + +#include <type_traits> + +#include "fmt/chrono.h" +#include "gmock/gmock.h" +#include "gtest-extra.h" + +TEST(iterator_test, counting_iterator) { + auto it = fmt::detail::counting_iterator(); + auto prev = it++; + EXPECT_EQ(prev.count(), 0); + EXPECT_EQ(it.count(), 1); + EXPECT_EQ((it + 41).count(), 42); +} + +TEST(iterator_test, truncating_iterator) { + char* p = nullptr; + auto it = fmt::detail::truncating_iterator<char*>(p, 3); + auto prev = it++; + EXPECT_EQ(prev.base(), p); + EXPECT_EQ(it.base(), p + 1); +} + +TEST(iterator_test, truncating_iterator_default_construct) { + auto it = fmt::detail::truncating_iterator<char*>(); + EXPECT_EQ(nullptr, it.base()); + EXPECT_EQ(std::size_t{0}, it.count()); +} + +#ifdef __cpp_lib_ranges +TEST(iterator_test, truncating_iterator_is_output_iterator) { + static_assert( + std::output_iterator<fmt::detail::truncating_iterator<char*>, char>); +} +#endif + +TEST(iterator_test, truncating_back_inserter) { + auto buffer = std::string(); + auto bi = std::back_inserter(buffer); + auto it = fmt::detail::truncating_iterator<decltype(bi)>(bi, 2); + *it++ = '4'; + *it++ = '2'; + *it++ = '1'; + EXPECT_EQ(buffer.size(), 2); + EXPECT_EQ(buffer, "42"); +} + +TEST(compile_test, compile_fallback) { + // FMT_COMPILE should fallback on runtime formatting when `if constexpr` is + // not available. + EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42)); +} + +struct type_with_get { + template <int> friend void get(type_with_get); +}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter<type_with_get> : formatter<int> { + template <typename FormatContext> + auto format(type_with_get, FormatContext& ctx) -> decltype(ctx.out()) { + return formatter<int>::format(42, ctx); + } +}; +FMT_END_NAMESPACE + +TEST(compile_test, compile_type_with_get) { + EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), type_with_get())); +} + +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +struct test_formattable {}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter<test_formattable> : formatter<const char*> { + char word_spec = 'f'; + constexpr auto parse(format_parse_context& ctx) { + auto it = ctx.begin(), end = ctx.end(); + if (it == end || *it == '}') return it; + if (it != end && (*it == 'f' || *it == 'b')) word_spec = *it++; + if (it != end && *it != '}') throw format_error("invalid format"); + return it; + } + template <typename FormatContext> + constexpr auto format(test_formattable, FormatContext& ctx) const + -> decltype(ctx.out()) { + return formatter<const char*>::format(word_spec == 'f' ? "foo" : "bar", + ctx); + } +}; +FMT_END_NAMESPACE + +TEST(compile_test, format_default) { + EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42)); + EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42u)); + EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42ll)); + EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42ull)); + EXPECT_EQ("true", fmt::format(FMT_COMPILE("{}"), true)); + EXPECT_EQ("x", fmt::format(FMT_COMPILE("{}"), 'x')); + EXPECT_EQ("4.2", fmt::format(FMT_COMPILE("{}"), 4.2)); + EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), "foo")); + EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), std::string("foo"))); + EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), test_formattable())); + auto t = std::chrono::system_clock::now(); + EXPECT_EQ(fmt::format("{}", t), fmt::format(FMT_COMPILE("{}"), t)); +# ifdef __cpp_lib_byte + EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), std::byte{42})); +# endif +} + +TEST(compile_test, format_wide_string) { + EXPECT_EQ(L"42", fmt::format(FMT_COMPILE(L"{}"), 42)); +} + +TEST(compile_test, format_specs) { + EXPECT_EQ("42", fmt::format(FMT_COMPILE("{:x}"), 0x42)); + EXPECT_EQ("1.2 ms ", + fmt::format(FMT_COMPILE("{:7.1%Q %q}"), + std::chrono::duration<double, std::milli>(1.234))); +} + +TEST(compile_test, dynamic_format_specs) { + EXPECT_EQ("foo ", fmt::format(FMT_COMPILE("{:{}}"), "foo", 5)); + EXPECT_EQ(" 3.14", fmt::format(FMT_COMPILE("{:{}.{}f}"), 3.141592, 6, 2)); + EXPECT_EQ( + "=1.234ms=", + fmt::format(FMT_COMPILE("{:=^{}.{}}"), + std::chrono::duration<double, std::milli>(1.234), 9, 3)); +} + +TEST(compile_test, manual_ordering) { + EXPECT_EQ("42", fmt::format(FMT_COMPILE("{0}"), 42)); + EXPECT_EQ(" -42", fmt::format(FMT_COMPILE("{0:4}"), -42)); + EXPECT_EQ("41 43", fmt::format(FMT_COMPILE("{0} {1}"), 41, 43)); + EXPECT_EQ("41 43", fmt::format(FMT_COMPILE("{1} {0}"), 43, 41)); + EXPECT_EQ("41 43", fmt::format(FMT_COMPILE("{0} {2}"), 41, 42, 43)); + EXPECT_EQ(" 41 43", fmt::format(FMT_COMPILE("{1:{2}} {0:4}"), 43, 41, 4)); + EXPECT_EQ("42 1.2 ms ", + fmt::format(FMT_COMPILE("{0} {1:7.1%Q %q}"), 42, + std::chrono::duration<double, std::milli>(1.234))); + EXPECT_EQ( + "true 42 42 foo 0x1234 foo", + fmt::format(FMT_COMPILE("{0} {1} {2} {3} {4} {5}"), true, 42, 42.0f, + "foo", reinterpret_cast<void*>(0x1234), test_formattable())); + EXPECT_EQ(L"42", fmt::format(FMT_COMPILE(L"{0}"), 42)); +} + +TEST(compile_test, named) { + auto runtime_named_field_compiled = + fmt::detail::compile<decltype(fmt::arg("arg", 42))>(FMT_COMPILE("{arg}")); + static_assert(std::is_same_v<decltype(runtime_named_field_compiled), + fmt::detail::runtime_named_field<char>>); + + EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), fmt::arg("arg", 42))); + EXPECT_EQ("41 43", fmt::format(FMT_COMPILE("{} {}"), fmt::arg("arg", 41), + fmt::arg("arg", 43))); + + EXPECT_EQ("foobar", + fmt::format(FMT_COMPILE("{a0}{a1}"), fmt::arg("a0", "foo"), + fmt::arg("a1", "bar"))); + EXPECT_EQ("foobar", fmt::format(FMT_COMPILE("{}{a1}"), fmt::arg("a0", "foo"), + fmt::arg("a1", "bar"))); + EXPECT_EQ("foofoo", fmt::format(FMT_COMPILE("{a0}{}"), fmt::arg("a0", "foo"), + fmt::arg("a1", "bar"))); + EXPECT_EQ("foobar", fmt::format(FMT_COMPILE("{0}{a1}"), fmt::arg("a0", "foo"), + fmt::arg("a1", "bar"))); + EXPECT_EQ("foobar", fmt::format(FMT_COMPILE("{a0}{1}"), fmt::arg("a0", "foo"), + fmt::arg("a1", "bar"))); + + EXPECT_EQ("foobar", + fmt::format(FMT_COMPILE("{}{a1}"), "foo", fmt::arg("a1", "bar"))); + EXPECT_EQ("foobar", + fmt::format(FMT_COMPILE("{a0}{a1}"), fmt::arg("a1", "bar"), + fmt::arg("a2", "baz"), fmt::arg("a0", "foo"))); + EXPECT_EQ(" bar foo ", + fmt::format(FMT_COMPILE(" {foo} {bar} "), fmt::arg("foo", "bar"), + fmt::arg("bar", "foo"))); + + EXPECT_THROW(fmt::format(FMT_COMPILE("{invalid}"), fmt::arg("valid", 42)), + fmt::format_error); + +# if FMT_USE_NONTYPE_TEMPLATE_ARGS + using namespace fmt::literals; + auto statically_named_field_compiled = + fmt::detail::compile<decltype("arg"_a = 42)>(FMT_COMPILE("{arg}")); + static_assert(std::is_same_v<decltype(statically_named_field_compiled), + fmt::detail::field<char, int, 0>>); + + EXPECT_EQ("41 43", + fmt::format(FMT_COMPILE("{a0} {a1}"), "a0"_a = 41, "a1"_a = 43)); + EXPECT_EQ("41 43", + fmt::format(FMT_COMPILE("{a1} {a0}"), "a0"_a = 43, "a1"_a = 41)); +# endif +} + +TEST(compile_test, join) { + unsigned char data[] = {0x1, 0x2, 0xaf}; + EXPECT_EQ("0102af", fmt::format(FMT_COMPILE("{:02x}"), fmt::join(data, ""))); +} + +TEST(compile_test, format_to) { + char buf[8]; + auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42); + *end = '\0'; + EXPECT_STREQ("42", buf); + end = fmt::format_to(buf, FMT_COMPILE("{:x}"), 42); + *end = '\0'; + EXPECT_STREQ("2a", buf); +} + +TEST(compile_test, format_to_n) { + constexpr auto buffer_size = 8; + char buffer[buffer_size]; + auto res = fmt::format_to_n(buffer, buffer_size, FMT_COMPILE("{}"), 42); + *res.out = '\0'; + EXPECT_STREQ("42", buffer); + res = fmt::format_to_n(buffer, buffer_size, FMT_COMPILE("{:x}"), 42); + *res.out = '\0'; + EXPECT_STREQ("2a", buffer); +} + +#ifdef __cpp_lib_bit_cast +TEST(compile_test, constexpr_formatted_size) { + FMT_CONSTEXPR20 size_t s1 = fmt::formatted_size(FMT_COMPILE("{0}"), 42); + EXPECT_EQ(2, s1); + FMT_CONSTEXPR20 size_t s2 = fmt::formatted_size(FMT_COMPILE("{0:<4.2f}"), 42.0); + EXPECT_EQ(5, s2); +} +#endif + +TEST(compile_test, text_and_arg) { + EXPECT_EQ(">>>42<<<", fmt::format(FMT_COMPILE(">>>{}<<<"), 42)); + EXPECT_EQ("42!", fmt::format(FMT_COMPILE("{}!"), 42)); +} + +TEST(compile_test, unknown_format_fallback) { + EXPECT_EQ(" 42 ", + fmt::format(FMT_COMPILE("{name:^4}"), fmt::arg("name", 42))); + + std::vector<char> v; + fmt::format_to(std::back_inserter(v), FMT_COMPILE("{name:^4}"), + fmt::arg("name", 42)); + EXPECT_EQ(" 42 ", fmt::string_view(v.data(), v.size())); + + char buffer[4]; + auto result = fmt::format_to_n(buffer, 4, FMT_COMPILE("{name:^5}"), + fmt::arg("name", 42)); + EXPECT_EQ(5u, result.size); + EXPECT_EQ(buffer + 4, result.out); + EXPECT_EQ(" 42 ", fmt::string_view(buffer, 4)); +} + +TEST(compile_test, empty) { EXPECT_EQ("", fmt::format(FMT_COMPILE(""))); } + +struct to_stringable { + friend fmt::string_view to_string_view(to_stringable) { return {}; } +}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter<to_stringable> { + auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template <typename FormatContext> + auto format(const to_stringable&, FormatContext& ctx) -> decltype(ctx.out()) { + return ctx.out(); + } +}; +FMT_END_NAMESPACE + +TEST(compile_test, to_string_and_formatter) { + fmt::format(FMT_COMPILE("{}"), to_stringable()); +} + +TEST(compile_test, print) { + EXPECT_WRITE(stdout, fmt::print(FMT_COMPILE("Don't {}!"), "panic"), + "Don't panic!"); + EXPECT_WRITE(stderr, fmt::print(stderr, FMT_COMPILE("Don't {}!"), "panic"), + "Don't panic!"); +} +#endif + +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +TEST(compile_test, compile_format_string_literal) { + using namespace fmt::literals; + EXPECT_EQ("", fmt::format(""_cf)); + EXPECT_EQ("42", fmt::format("{}"_cf, 42)); + EXPECT_EQ(L"42", fmt::format(L"{}"_cf, 42)); +} +#endif + +// MSVS 2019 19.29.30145.0 - Support C++20 and OK. +// MSVS 2022 19.32.31332.0 - compile-test.cc(362,3): fatal error C1001: Internal +// compiler error. +// (compiler file +// 'D:\a\_work\1\s\src\vctools\Compiler\CxxFE\sl\p1\c\constexpr\constexpr.cpp', +// line 8635) +#if ((FMT_CPLUSPLUS >= 202002L) && \ + (!FMT_MSC_VERSION || FMT_MSC_VERSION < 1930)) || \ + (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002) +template <size_t max_string_length, typename Char = char> struct test_string { + template <typename T> constexpr bool operator==(const T& rhs) const noexcept { + return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0; + } + Char buffer[max_string_length]{}; +}; + +template <size_t max_string_length, typename Char = char, typename... Args> +consteval auto test_format(auto format, const Args&... args) { + test_string<max_string_length, Char> string{}; + fmt::format_to(string.buffer, format, args...); + return string; +} + +TEST(compile_time_formatting_test, bool) { + EXPECT_EQ("true", test_format<5>(FMT_COMPILE("{}"), true)); + EXPECT_EQ("false", test_format<6>(FMT_COMPILE("{}"), false)); + EXPECT_EQ("true ", test_format<6>(FMT_COMPILE("{:5}"), true)); + EXPECT_EQ("1", test_format<2>(FMT_COMPILE("{:d}"), true)); +} + +TEST(compile_time_formatting_test, integer) { + EXPECT_EQ("42", test_format<3>(FMT_COMPILE("{}"), 42)); + EXPECT_EQ("420", test_format<4>(FMT_COMPILE("{}"), 420)); + EXPECT_EQ("42 42", test_format<6>(FMT_COMPILE("{} {}"), 42, 42)); + EXPECT_EQ("42 42", + test_format<6>(FMT_COMPILE("{} {}"), uint32_t{42}, uint64_t{42})); + + EXPECT_EQ("+42", test_format<4>(FMT_COMPILE("{:+}"), 42)); + EXPECT_EQ("42", test_format<3>(FMT_COMPILE("{:-}"), 42)); + EXPECT_EQ(" 42", test_format<4>(FMT_COMPILE("{: }"), 42)); + + EXPECT_EQ("-0042", test_format<6>(FMT_COMPILE("{:05}"), -42)); + + EXPECT_EQ("101010", test_format<7>(FMT_COMPILE("{:b}"), 42)); + EXPECT_EQ("0b101010", test_format<9>(FMT_COMPILE("{:#b}"), 42)); + EXPECT_EQ("0B101010", test_format<9>(FMT_COMPILE("{:#B}"), 42)); + EXPECT_EQ("042", test_format<4>(FMT_COMPILE("{:#o}"), 042)); + EXPECT_EQ("0x4a", test_format<5>(FMT_COMPILE("{:#x}"), 0x4a)); + EXPECT_EQ("0X4A", test_format<5>(FMT_COMPILE("{:#X}"), 0x4a)); + + EXPECT_EQ(" 42", test_format<6>(FMT_COMPILE("{:5}"), 42)); + EXPECT_EQ(" 42", test_format<6>(FMT_COMPILE("{:5}"), 42ll)); + EXPECT_EQ(" 42", test_format<6>(FMT_COMPILE("{:5}"), 42ull)); + + EXPECT_EQ("42 ", test_format<5>(FMT_COMPILE("{:<4}"), 42)); + EXPECT_EQ(" 42", test_format<5>(FMT_COMPILE("{:>4}"), 42)); + EXPECT_EQ(" 42 ", test_format<5>(FMT_COMPILE("{:^4}"), 42)); + EXPECT_EQ("**-42", test_format<6>(FMT_COMPILE("{:*>5}"), -42)); +} + +TEST(compile_time_formatting_test, char) { + EXPECT_EQ("c", test_format<2>(FMT_COMPILE("{}"), 'c')); + + EXPECT_EQ("c ", test_format<4>(FMT_COMPILE("{:3}"), 'c')); + EXPECT_EQ("99", test_format<3>(FMT_COMPILE("{:d}"), 'c')); +} + +TEST(compile_time_formatting_test, string) { + EXPECT_EQ("42", test_format<3>(FMT_COMPILE("{}"), "42")); + EXPECT_EQ("The answer is 42", + test_format<17>(FMT_COMPILE("{} is {}"), "The answer", "42")); + + EXPECT_EQ("abc**", test_format<6>(FMT_COMPILE("{:*<5}"), "abc")); + EXPECT_EQ("**🤡**", test_format<9>(FMT_COMPILE("{:*^6}"), "🤡")); +} + +TEST(compile_time_formatting_test, combination) { + EXPECT_EQ("420, true, answer", + test_format<18>(FMT_COMPILE("{}, {}, {}"), 420, true, "answer")); + + EXPECT_EQ(" -42", test_format<5>(FMT_COMPILE("{:{}}"), -42, 4)); +} + +TEST(compile_time_formatting_test, custom_type) { + EXPECT_EQ("foo", test_format<4>(FMT_COMPILE("{}"), test_formattable())); + EXPECT_EQ("bar", test_format<4>(FMT_COMPILE("{:b}"), test_formattable())); +} + +TEST(compile_time_formatting_test, multibyte_fill) { + EXPECT_EQ("жж42", test_format<8>(FMT_COMPILE("{:ж>4}"), 42)); +} +#endif diff --git a/src/fmt/test/core-test.cc b/src/fmt/test/core-test.cc new file mode 100644 index 000000000..c76dc1617 --- /dev/null +++ b/src/fmt/test/core-test.cc @@ -0,0 +1,978 @@ +// Formatting library for C++ - core tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +// clang-format off +#include "test-assert.h" +// clang-format on + +#define I 42 // simulate https://en.cppreference.com/w/c/numeric/complex/I +#include "fmt/core.h" +#undef I + +#include <algorithm> // std::copy_n +#include <climits> // INT_MAX +#include <cstring> // std::strlen +#include <functional> // std::equal_to +#include <iterator> // std::back_insert_iterator +#include <limits> // std::numeric_limits +#include <string> // std::string +#include <type_traits> // std::is_same + +#include "gmock/gmock.h" + +using fmt::string_view; +using fmt::detail::buffer; + +using testing::_; +using testing::Invoke; +using testing::Return; + +#ifdef FMT_FORMAT_H_ +# error core-test includes format.h +#endif + +TEST(string_view_test, value_type) { + static_assert(std::is_same<string_view::value_type, char>::value, ""); +} + +TEST(string_view_test, ctor) { + EXPECT_STREQ("abc", fmt::string_view("abc").data()); + EXPECT_EQ(3u, fmt::string_view("abc").size()); + + EXPECT_STREQ("defg", fmt::string_view(std::string("defg")).data()); + EXPECT_EQ(4u, fmt::string_view(std::string("defg")).size()); +} + +TEST(string_view_test, length) { + // Test that string_view::size() returns string length, not buffer size. + char str[100] = "some string"; + EXPECT_EQ(std::strlen(str), string_view(str).size()); + EXPECT_LT(std::strlen(str), sizeof(str)); +} + +// Check string_view's comparison operator. +template <template <typename> class Op> void check_op() { + const char* inputs[] = {"foo", "fop", "fo"}; + size_t num_inputs = sizeof(inputs) / sizeof(*inputs); + for (size_t i = 0; i < num_inputs; ++i) { + for (size_t j = 0; j < num_inputs; ++j) { + string_view lhs(inputs[i]), rhs(inputs[j]); + EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0), Op<string_view>()(lhs, rhs)); + } + } +} + +TEST(string_view_test, compare) { + EXPECT_EQ(string_view("foo").compare(string_view("foo")), 0); + EXPECT_GT(string_view("fop").compare(string_view("foo")), 0); + EXPECT_LT(string_view("foo").compare(string_view("fop")), 0); + EXPECT_GT(string_view("foo").compare(string_view("fo")), 0); + EXPECT_LT(string_view("fo").compare(string_view("foo")), 0); + check_op<std::equal_to>(); + check_op<std::not_equal_to>(); + check_op<std::less>(); + check_op<std::less_equal>(); + check_op<std::greater>(); + check_op<std::greater_equal>(); +} + +namespace test_ns { +template <typename Char> class test_string { + private: + std::basic_string<Char> s_; + + public: + test_string(const Char* s) : s_(s) {} + const Char* data() const { return s_.data(); } + size_t length() const { return s_.size(); } + operator const Char*() const { return s_.c_str(); } +}; + +template <typename Char> +fmt::basic_string_view<Char> to_string_view(const test_string<Char>& s) { + return {s.data(), s.length()}; +} +} // namespace test_ns + +TEST(core_test, is_output_iterator) { + EXPECT_TRUE((fmt::detail::is_output_iterator<char*, char>::value)); + EXPECT_FALSE((fmt::detail::is_output_iterator<const char*, char>::value)); + EXPECT_FALSE((fmt::detail::is_output_iterator<std::string, char>::value)); + EXPECT_TRUE( + (fmt::detail::is_output_iterator<std::back_insert_iterator<std::string>, + char>::value)); + EXPECT_TRUE( + (fmt::detail::is_output_iterator<std::string::iterator, char>::value)); + EXPECT_FALSE((fmt::detail::is_output_iterator<std::string::const_iterator, + char>::value)); +} + +TEST(core_test, buffer_appender) { + // back_insert_iterator is not default-constructible before C++20, so + // buffer_appender can only be default-constructible when back_insert_iterator + // is. + static_assert( + std::is_default_constructible< + std::back_insert_iterator<fmt::detail::buffer<char>>>::value == + std::is_default_constructible< + fmt::detail::buffer_appender<char>>::value, + ""); + +#ifdef __cpp_lib_ranges + static_assert(std::output_iterator<fmt::detail::buffer_appender<char>, char>); +#endif +} + +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470 +TEST(buffer_test, noncopyable) { + EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value); +# if !FMT_MSC_VERSION + // std::is_copy_assignable is broken in MSVC2013. + EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value); +# endif +} + +TEST(buffer_test, nonmoveable) { + EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value); +# if !FMT_MSC_VERSION + // std::is_move_assignable is broken in MSVC2013. + EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value); +# endif +} +#endif + +TEST(buffer_test, indestructible) { + static_assert(!std::is_destructible<fmt::detail::buffer<int>>(), + "buffer's destructor is protected"); +} + +template <typename T> struct mock_buffer final : buffer<T> { + MOCK_METHOD1(do_grow, size_t(size_t capacity)); + + void grow(size_t capacity) override { + this->set(this->data(), do_grow(capacity)); + } + + mock_buffer(T* data = nullptr, size_t buf_capacity = 0) { + this->set(data, buf_capacity); + ON_CALL(*this, do_grow(_)).WillByDefault(Invoke([](size_t capacity) { + return capacity; + })); + } +}; + +TEST(buffer_test, ctor) { + { + mock_buffer<int> buffer; + EXPECT_EQ(nullptr, buffer.data()); + EXPECT_EQ(static_cast<size_t>(0), buffer.size()); + EXPECT_EQ(static_cast<size_t>(0), buffer.capacity()); + } + { + int dummy; + mock_buffer<int> buffer(&dummy); + EXPECT_EQ(&dummy, &buffer[0]); + EXPECT_EQ(static_cast<size_t>(0), buffer.size()); + EXPECT_EQ(static_cast<size_t>(0), buffer.capacity()); + } + { + int dummy; + size_t capacity = std::numeric_limits<size_t>::max(); + mock_buffer<int> buffer(&dummy, capacity); + EXPECT_EQ(&dummy, &buffer[0]); + EXPECT_EQ(static_cast<size_t>(0), buffer.size()); + EXPECT_EQ(capacity, buffer.capacity()); + } +} + +TEST(buffer_test, access) { + char data[10]; + mock_buffer<char> buffer(data, sizeof(data)); + buffer[0] = 11; + EXPECT_EQ(11, buffer[0]); + buffer[3] = 42; + EXPECT_EQ(42, *(&buffer[0] + 3)); + const fmt::detail::buffer<char>& const_buffer = buffer; + EXPECT_EQ(42, const_buffer[3]); +} + +TEST(buffer_test, try_resize) { + char data[123]; + mock_buffer<char> buffer(data, sizeof(data)); + buffer[10] = 42; + EXPECT_EQ(42, buffer[10]); + buffer.try_resize(20); + EXPECT_EQ(20u, buffer.size()); + EXPECT_EQ(123u, buffer.capacity()); + EXPECT_EQ(42, buffer[10]); + buffer.try_resize(5); + EXPECT_EQ(5u, buffer.size()); + EXPECT_EQ(123u, buffer.capacity()); + EXPECT_EQ(42, buffer[10]); + // Check if try_resize calls grow. + EXPECT_CALL(buffer, do_grow(124)); + buffer.try_resize(124); + EXPECT_CALL(buffer, do_grow(200)); + buffer.try_resize(200); +} + +TEST(buffer_test, try_resize_partial) { + char data[10]; + mock_buffer<char> buffer(data, sizeof(data)); + EXPECT_CALL(buffer, do_grow(20)).WillOnce(Return(15)); + buffer.try_resize(20); + EXPECT_EQ(buffer.capacity(), 15); + EXPECT_EQ(buffer.size(), 15); +} + +TEST(buffer_test, clear) { + mock_buffer<char> buffer; + EXPECT_CALL(buffer, do_grow(20)); + buffer.try_resize(20); + buffer.try_resize(0); + EXPECT_EQ(static_cast<size_t>(0), buffer.size()); + EXPECT_EQ(20u, buffer.capacity()); +} + +TEST(buffer_test, append) { + char data[15]; + mock_buffer<char> buffer(data, 10); + auto test = "test"; + buffer.append(test, test + 5); + EXPECT_STREQ(test, &buffer[0]); + EXPECT_EQ(5u, buffer.size()); + buffer.try_resize(10); + EXPECT_CALL(buffer, do_grow(12)); + buffer.append(test, test + 2); + EXPECT_EQ('t', buffer[10]); + EXPECT_EQ('e', buffer[11]); + EXPECT_EQ(12u, buffer.size()); +} + +TEST(buffer_test, append_partial) { + char data[10]; + mock_buffer<char> buffer(data, sizeof(data)); + testing::InSequence seq; + EXPECT_CALL(buffer, do_grow(15)).WillOnce(Return(10)); + EXPECT_CALL(buffer, do_grow(15)).WillOnce(Invoke([&buffer](size_t) { + EXPECT_EQ(fmt::string_view(buffer.data(), buffer.size()), "0123456789"); + buffer.clear(); + return 10; + })); + auto test = "0123456789abcde"; + buffer.append(test, test + 15); +} + +TEST(buffer_test, append_allocates_enough_storage) { + char data[19]; + mock_buffer<char> buffer(data, 10); + auto test = "abcdefgh"; + buffer.try_resize(10); + EXPECT_CALL(buffer, do_grow(19)); + buffer.append(test, test + 9); +} + +struct custom_context { + using char_type = char; + using parse_context_type = fmt::format_parse_context; + + bool called = false; + + template <typename T> struct formatter_type { + auto parse(fmt::format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + const char* format(const T&, custom_context& ctx) { + ctx.called = true; + return nullptr; + } + }; + + void advance_to(const char*) {} +}; + +struct test_struct {}; + +FMT_BEGIN_NAMESPACE +template <typename Char> struct formatter<test_struct, Char> { + auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + auto format(test_struct, format_context& ctx) -> decltype(ctx.out()) { + auto test = string_view("test"); + return std::copy_n(test.data(), test.size(), ctx.out()); + } +}; +FMT_END_NAMESPACE + +TEST(arg_test, format_args) { + auto args = fmt::format_args(); + EXPECT_FALSE(args.get(1)); +} + +TEST(arg_test, make_value_with_custom_context) { + auto t = test_struct(); + fmt::detail::value<custom_context> arg( + fmt::detail::arg_mapper<custom_context>().map(t)); + auto ctx = custom_context(); + auto parse_ctx = fmt::format_parse_context(""); + arg.custom.format(&t, parse_ctx, ctx); + EXPECT_TRUE(ctx.called); +} + +// Use a unique result type to make sure that there are no undesirable +// conversions. +struct test_result {}; + +template <typename T> struct mock_visitor { + template <typename U> struct result { using type = test_result; }; + + mock_visitor() { + ON_CALL(*this, visit(_)).WillByDefault(Return(test_result())); + } + + MOCK_METHOD1_T(visit, test_result(T value)); + MOCK_METHOD0_T(unexpected, void()); + + test_result operator()(T value) { return visit(value); } + + template <typename U> test_result operator()(U) { + unexpected(); + return test_result(); + } +}; + +template <typename T> struct visit_type { using type = T; }; + +#define VISIT_TYPE(type_, visit_type_) \ + template <> struct visit_type<type_> { using type = visit_type_; } + +VISIT_TYPE(signed char, int); +VISIT_TYPE(unsigned char, unsigned); +VISIT_TYPE(short, int); +VISIT_TYPE(unsigned short, unsigned); + +#if LONG_MAX == INT_MAX +VISIT_TYPE(long, int); +VISIT_TYPE(unsigned long, unsigned); +#else +VISIT_TYPE(long, long long); +VISIT_TYPE(unsigned long, unsigned long long); +#endif + +#define CHECK_ARG(Char, expected, value) \ + { \ + testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \ + EXPECT_CALL(visitor, visit(expected)); \ + using iterator = std::back_insert_iterator<buffer<Char>>; \ + fmt::visit_format_arg( \ + visitor, \ + fmt::detail::make_arg<fmt::basic_format_context<iterator, Char>>( \ + value)); \ + } + +#define CHECK_ARG_SIMPLE(value) \ + { \ + using value_type = decltype(value); \ + typename visit_type<value_type>::type expected = value; \ + CHECK_ARG(char, expected, value) \ + CHECK_ARG(wchar_t, expected, value) \ + } + +template <typename T> class numeric_arg_test : public testing::Test {}; + +using test_types = + testing::Types<bool, signed char, unsigned char, short, unsigned short, int, + unsigned, long, unsigned long, long long, unsigned long long, + float, double, long double>; +TYPED_TEST_SUITE(numeric_arg_test, test_types); + +template <typename T, fmt::enable_if_t<std::is_integral<T>::value, int> = 0> +T test_value() { + return static_cast<T>(42); +} + +template <typename T, + fmt::enable_if_t<std::is_floating_point<T>::value, int> = 0> +T test_value() { + return static_cast<T>(4.2); +} + +TYPED_TEST(numeric_arg_test, make_and_visit) { + CHECK_ARG_SIMPLE(test_value<TypeParam>()); + CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::min()); + CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::max()); +} + +TEST(arg_test, char_arg) { CHECK_ARG(char, 'a', 'a'); } + +TEST(arg_test, string_arg) { + char str_data[] = "test"; + char* str = str_data; + const char* cstr = str; + CHECK_ARG(char, cstr, str); + + auto sv = fmt::string_view(str); + CHECK_ARG(char, sv, std::string(str)); +} + +TEST(arg_test, wstring_arg) { + wchar_t str_data[] = L"test"; + wchar_t* str = str_data; + const wchar_t* cstr = str; + + auto sv = fmt::basic_string_view<wchar_t>(str); + CHECK_ARG(wchar_t, cstr, str); + CHECK_ARG(wchar_t, cstr, cstr); + CHECK_ARG(wchar_t, sv, std::wstring(str)); + CHECK_ARG(wchar_t, sv, fmt::basic_string_view<wchar_t>(str)); +} + +TEST(arg_test, pointer_arg) { + void* p = nullptr; + const void* cp = nullptr; + CHECK_ARG(char, cp, p); + CHECK_ARG(wchar_t, cp, p); + CHECK_ARG_SIMPLE(cp); +} + +struct check_custom { + test_result operator()( + fmt::basic_format_arg<fmt::format_context>::handle h) const { + struct test_buffer final : fmt::detail::buffer<char> { + char data[10]; + test_buffer() : fmt::detail::buffer<char>(data, 0, 10) {} + void grow(size_t) override {} + } buffer; + auto parse_ctx = fmt::format_parse_context(""); + auto ctx = fmt::format_context(fmt::detail::buffer_appender<char>(buffer), + fmt::format_args()); + h.format(parse_ctx, ctx); + EXPECT_EQ("test", std::string(buffer.data, buffer.size())); + return test_result(); + } +}; + +TEST(arg_test, custom_arg) { + auto test = test_struct(); + using visitor = + mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>; + testing::StrictMock<visitor> v; + EXPECT_CALL(v, visit(_)).WillOnce(Invoke(check_custom())); + fmt::visit_format_arg(v, fmt::detail::make_arg<fmt::format_context>(test)); +} + +TEST(arg_test, visit_invalid_arg) { + testing::StrictMock<mock_visitor<fmt::monostate>> visitor; + EXPECT_CALL(visitor, visit(_)); + auto arg = fmt::basic_format_arg<fmt::format_context>(); + fmt::visit_format_arg(visitor, arg); +} + +#if FMT_USE_CONSTEXPR + +enum class arg_id_result { none, empty, index, name, error }; +struct test_arg_id_handler { + arg_id_result res = arg_id_result::none; + int index = 0; + string_view name; + + constexpr void operator()() { res = arg_id_result::empty; } + + constexpr void operator()(int i) { + res = arg_id_result::index; + index = i; + } + + constexpr void operator()(string_view n) { + res = arg_id_result::name; + name = n; + } + + constexpr void on_error(const char*) { res = arg_id_result::error; } +}; + +template <size_t N> +constexpr test_arg_id_handler parse_arg_id(const char (&s)[N]) { + test_arg_id_handler h; + fmt::detail::parse_arg_id(s, s + N, h); + return h; +} + +TEST(format_test, constexpr_parse_arg_id) { + static_assert(parse_arg_id(":").res == arg_id_result::empty, ""); + static_assert(parse_arg_id("}").res == arg_id_result::empty, ""); + static_assert(parse_arg_id("42:").res == arg_id_result::index, ""); + static_assert(parse_arg_id("42:").index == 42, ""); + static_assert(parse_arg_id("foo:").res == arg_id_result::name, ""); + static_assert(parse_arg_id("foo:").name.size() == 3, ""); + static_assert(parse_arg_id("!").res == arg_id_result::error, ""); +} + +struct test_format_specs_handler { + enum result { none, hash, zero, loc, error }; + result res = none; + + fmt::align_t alignment = fmt::align::none; + fmt::sign_t sign = fmt::sign::none; + char fill = 0; + int width = 0; + fmt::detail::arg_ref<char> width_ref; + int precision = 0; + fmt::detail::arg_ref<char> precision_ref; + fmt::presentation_type type = fmt::presentation_type::none; + + // Workaround for MSVC2017 bug that results in "expression did not evaluate + // to a constant" with compiler-generated copy ctor. + constexpr test_format_specs_handler() {} + constexpr test_format_specs_handler(const test_format_specs_handler& other) = + default; + + constexpr void on_align(fmt::align_t a) { alignment = a; } + constexpr void on_fill(fmt::string_view f) { fill = f[0]; } + constexpr void on_sign(fmt::sign_t s) { sign = s; } + constexpr void on_hash() { res = hash; } + constexpr void on_zero() { res = zero; } + constexpr void on_localized() { res = loc; } + + constexpr void on_width(int w) { width = w; } + constexpr void on_dynamic_width(fmt::detail::auto_id) {} + constexpr void on_dynamic_width(int index) { width_ref = index; } + constexpr void on_dynamic_width(string_view) {} + + constexpr void on_precision(int p) { precision = p; } + constexpr void on_dynamic_precision(fmt::detail::auto_id) {} + constexpr void on_dynamic_precision(int index) { precision_ref = index; } + constexpr void on_dynamic_precision(string_view) {} + + constexpr void end_precision() {} + constexpr void on_type(fmt::presentation_type t) { type = t; } + constexpr void on_error(const char*) { res = error; } +}; + +template <size_t N> +constexpr test_format_specs_handler parse_test_specs(const char (&s)[N]) { + auto h = test_format_specs_handler(); + fmt::detail::parse_format_specs(s, s + N - 1, h); + return h; +} + +TEST(core_test, constexpr_parse_format_specs) { + using handler = test_format_specs_handler; + static_assert(parse_test_specs("<").alignment == fmt::align::left, ""); + static_assert(parse_test_specs("*^").fill == '*', ""); + static_assert(parse_test_specs("+").sign == fmt::sign::plus, ""); + static_assert(parse_test_specs("-").sign == fmt::sign::minus, ""); + static_assert(parse_test_specs(" ").sign == fmt::sign::space, ""); + static_assert(parse_test_specs("#").res == handler::hash, ""); + static_assert(parse_test_specs("0").res == handler::zero, ""); + static_assert(parse_test_specs("L").res == handler::loc, ""); + static_assert(parse_test_specs("42").width == 42, ""); + static_assert(parse_test_specs("{42}").width_ref.val.index == 42, ""); + static_assert(parse_test_specs(".42").precision == 42, ""); + static_assert(parse_test_specs(".{42}").precision_ref.val.index == 42, ""); + static_assert(parse_test_specs("d").type == fmt::presentation_type::dec, ""); + static_assert(parse_test_specs("{<").res == handler::error, ""); +} + +struct test_parse_context { + using char_type = char; + + constexpr int next_arg_id() { return 11; } + template <typename Id> FMT_CONSTEXPR void check_arg_id(Id) {} + FMT_CONSTEXPR void check_dynamic_spec(int) {} + + constexpr const char* begin() { return nullptr; } + constexpr const char* end() { return nullptr; } + + void on_error(const char*) {} +}; + +template <size_t N> +constexpr fmt::detail::dynamic_format_specs<char> parse_dynamic_specs( + const char (&s)[N]) { + auto specs = fmt::detail::dynamic_format_specs<char>(); + auto ctx = test_parse_context(); + auto h = fmt::detail::dynamic_specs_handler<test_parse_context>(specs, ctx); + parse_format_specs(s, s + N - 1, h); + return specs; +} + +TEST(format_test, constexpr_dynamic_specs_handler) { + static_assert(parse_dynamic_specs("<").align == fmt::align::left, ""); + static_assert(parse_dynamic_specs("*^").fill[0] == '*', ""); + static_assert(parse_dynamic_specs("+").sign == fmt::sign::plus, ""); + static_assert(parse_dynamic_specs("-").sign == fmt::sign::minus, ""); + static_assert(parse_dynamic_specs(" ").sign == fmt::sign::space, ""); + static_assert(parse_dynamic_specs("#").alt, ""); + static_assert(parse_dynamic_specs("0").align == fmt::align::numeric, ""); + static_assert(parse_dynamic_specs("42").width == 42, ""); + static_assert(parse_dynamic_specs("{}").width_ref.val.index == 11, ""); + static_assert(parse_dynamic_specs("{42}").width_ref.val.index == 42, ""); + static_assert(parse_dynamic_specs(".42").precision == 42, ""); + static_assert(parse_dynamic_specs(".{}").precision_ref.val.index == 11, ""); + static_assert(parse_dynamic_specs(".{42}").precision_ref.val.index == 42, ""); + static_assert(parse_dynamic_specs("d").type == fmt::presentation_type::dec, + ""); +} + +template <size_t N> +constexpr test_format_specs_handler check_specs(const char (&s)[N]) { + fmt::detail::specs_checker<test_format_specs_handler> checker( + test_format_specs_handler(), fmt::detail::type::double_type); + parse_format_specs(s, s + N - 1, checker); + return checker; +} + +TEST(format_test, constexpr_specs_checker) { + using handler = test_format_specs_handler; + static_assert(check_specs("<").alignment == fmt::align::left, ""); + static_assert(check_specs("*^").fill == '*', ""); + static_assert(check_specs("+").sign == fmt::sign::plus, ""); + static_assert(check_specs("-").sign == fmt::sign::minus, ""); + static_assert(check_specs(" ").sign == fmt::sign::space, ""); + static_assert(check_specs("#").res == handler::hash, ""); + static_assert(check_specs("0").res == handler::zero, ""); + static_assert(check_specs("42").width == 42, ""); + static_assert(check_specs("{42}").width_ref.val.index == 42, ""); + static_assert(check_specs(".42").precision == 42, ""); + static_assert(check_specs(".{42}").precision_ref.val.index == 42, ""); + static_assert(check_specs("d").type == fmt::presentation_type::dec, ""); + static_assert(check_specs("{<").res == handler::error, ""); +} + +struct test_format_string_handler { + constexpr void on_text(const char*, const char*) {} + + constexpr int on_arg_id() { return 0; } + + template <typename T> constexpr int on_arg_id(T) { return 0; } + + constexpr void on_replacement_field(int, const char*) {} + + constexpr const char* on_format_specs(int, const char* begin, const char*) { + return begin; + } + + constexpr void on_error(const char*) { error = true; } + + bool error = false; +}; + +template <size_t N> constexpr bool parse_string(const char (&s)[N]) { + auto h = test_format_string_handler(); + fmt::detail::parse_format_string<true>(fmt::string_view(s, N - 1), h); + return !h.error; +} + +TEST(format_test, constexpr_parse_format_string) { + static_assert(parse_string("foo"), ""); + static_assert(!parse_string("}"), ""); + static_assert(parse_string("{}"), ""); + static_assert(parse_string("{42}"), ""); + static_assert(parse_string("{foo}"), ""); + static_assert(parse_string("{:}"), ""); +} +#endif // FMT_USE_CONSTEXPR + +struct enabled_formatter {}; +struct enabled_ptr_formatter {}; +struct disabled_formatter {}; +struct disabled_formatter_convertible { + operator int() const { return 42; } +}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter<enabled_formatter> { + auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + auto format(enabled_formatter, format_context& ctx) -> decltype(ctx.out()) { + return ctx.out(); + } +}; + +template <> struct formatter<enabled_ptr_formatter*> { + auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + auto format(enabled_ptr_formatter*, format_context& ctx) + -> decltype(ctx.out()) { + return ctx.out(); + } +}; +FMT_END_NAMESPACE + +TEST(core_test, has_formatter) { + using fmt::has_formatter; + using context = fmt::format_context; + static_assert(has_formatter<enabled_formatter, context>::value, ""); + static_assert(!has_formatter<disabled_formatter, context>::value, ""); + static_assert(!has_formatter<disabled_formatter_convertible, context>::value, + ""); +} + +struct const_formattable {}; +struct nonconst_formattable {}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter<const_formattable> { + auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + auto format(const const_formattable&, format_context& ctx) + -> decltype(ctx.out()) { + auto test = string_view("test"); + return std::copy_n(test.data(), test.size(), ctx.out()); + } +}; + +template <> struct formatter<nonconst_formattable> { + auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + auto format(nonconst_formattable&, format_context& ctx) + -> decltype(ctx.out()) { + auto test = string_view("test"); + return std::copy_n(test.data(), test.size(), ctx.out()); + } +}; +FMT_END_NAMESPACE + +struct convertible_to_pointer { + operator const int*() const { return nullptr; } +}; + +struct convertible_to_pointer_formattable { + operator const int*() const { return nullptr; } +}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter<convertible_to_pointer_formattable> { + auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + auto format(convertible_to_pointer_formattable, format_context& ctx) const + -> decltype(ctx.out()) { + auto test = string_view("test"); + return std::copy_n(test.data(), test.size(), ctx.out()); + } +}; +FMT_END_NAMESPACE + +enum class unformattable_scoped_enum {}; + +namespace test { +enum class formattable_scoped_enum {}; +auto format_as(formattable_scoped_enum) -> int { return 42; } + +struct convertible_to_enum { + operator formattable_scoped_enum() const { return {}; } +}; +} // namespace test + +TEST(core_test, is_formattable) { +#if 0 + // This should be enabled once corresponding map overloads are gone. + static_assert(fmt::is_formattable<signed char*>::value, ""); + static_assert(fmt::is_formattable<unsigned char*>::value, ""); + static_assert(fmt::is_formattable<const signed char*>::value, ""); + static_assert(fmt::is_formattable<const unsigned char*>::value, ""); +#endif + static_assert(!fmt::is_formattable<wchar_t>::value, ""); +#ifdef __cpp_char8_t + static_assert(!fmt::is_formattable<char8_t>::value, ""); +#endif + static_assert(!fmt::is_formattable<char16_t>::value, ""); + static_assert(!fmt::is_formattable<char32_t>::value, ""); + static_assert(!fmt::is_formattable<const wchar_t*>::value, ""); + static_assert(!fmt::is_formattable<const wchar_t[3]>::value, ""); + static_assert(!fmt::is_formattable<fmt::basic_string_view<wchar_t>>::value, + ""); + static_assert(fmt::is_formattable<enabled_formatter>::value, ""); + static_assert(!fmt::is_formattable<enabled_ptr_formatter*>::value, ""); + static_assert(!fmt::is_formattable<disabled_formatter>::value, ""); + static_assert(fmt::is_formattable<disabled_formatter_convertible>::value, ""); + + static_assert(fmt::is_formattable<const_formattable&>::value, ""); + static_assert(fmt::is_formattable<const const_formattable&>::value, ""); + + static_assert(fmt::is_formattable<nonconst_formattable&>::value, ""); +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 + static_assert(!fmt::is_formattable<const nonconst_formattable&>::value, ""); +#endif + + static_assert(!fmt::is_formattable<convertible_to_pointer>::value, ""); + const auto f = convertible_to_pointer_formattable(); + EXPECT_EQ(fmt::format("{}", f), "test"); + + static_assert(!fmt::is_formattable<void (*)()>::value, ""); + + struct s; + static_assert(!fmt::is_formattable<int(s::*)>::value, ""); + static_assert(!fmt::is_formattable<int (s::*)()>::value, ""); + static_assert(!fmt::is_formattable<unformattable_scoped_enum>::value, ""); + static_assert(fmt::is_formattable<test::formattable_scoped_enum>::value, ""); + static_assert(!fmt::is_formattable<test::convertible_to_enum>::value, ""); +} + +TEST(core_test, format) { EXPECT_EQ(fmt::format("{}", 42), "42"); } + +TEST(core_test, format_to) { + std::string s; + fmt::format_to(std::back_inserter(s), "{}", 42); + EXPECT_EQ(s, "42"); +} + +TEST(core_test, format_as) { + EXPECT_EQ(fmt::format("{}", test::formattable_scoped_enum()), "42"); +} + +struct convertible_to_int { + operator int() const { return 42; } +}; + +struct convertible_to_c_string { + operator const char*() const { return "foo"; } +}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter<convertible_to_int> { + auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + auto format(convertible_to_int, format_context& ctx) -> decltype(ctx.out()) { + return std::copy_n("foo", 3, ctx.out()); + } +}; + +template <> struct formatter<convertible_to_c_string> { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + auto format(convertible_to_c_string, format_context& ctx) + -> decltype(ctx.out()) { + return std::copy_n("bar", 3, ctx.out()); + } +}; +FMT_END_NAMESPACE + +TEST(core_test, formatter_overrides_implicit_conversion) { + EXPECT_EQ(fmt::format("{}", convertible_to_int()), "foo"); + EXPECT_EQ(fmt::format("{}", convertible_to_c_string()), "bar"); +} + +// Test that check is not found by ADL. +template <typename T> void check(T); +TEST(core_test, adl_check) { + EXPECT_EQ(fmt::format("{}", test_struct()), "test"); +} + +TEST(core_test, to_string_view_foreign_strings) { + using namespace test_ns; + EXPECT_EQ(to_string_view(test_string<char>("42")), "42"); + fmt::detail::type type = + fmt::detail::mapped_type_constant<test_string<char>, + fmt::format_context>::value; + EXPECT_EQ(type, fmt::detail::type::string_type); +} + +struct implicitly_convertible_to_string { + operator std::string() const { return "foo"; } +}; + +struct implicitly_convertible_to_string_view { + operator fmt::string_view() const { return "foo"; } +}; + +TEST(core_test, format_implicitly_convertible_to_string_view) { + EXPECT_EQ("foo", fmt::format("{}", implicitly_convertible_to_string_view())); +} + +// std::is_constructible is broken in MSVC until version 2015. +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1900 +struct explicitly_convertible_to_string_view { + explicit operator fmt::string_view() const { return "foo"; } +}; + +TEST(core_test, format_explicitly_convertible_to_string_view) { + // Types explicitly convertible to string_view are not formattable by + // default because it may introduce ODR violations. + static_assert( + !fmt::is_formattable<explicitly_convertible_to_string_view>::value, ""); +} + +# ifdef FMT_USE_STRING_VIEW +struct explicitly_convertible_to_std_string_view { + explicit operator std::string_view() const { return "foo"; } +}; + +TEST(core_test, format_explicitly_convertible_to_std_string_view) { + // Types explicitly convertible to string_view are not formattable by + // default because it may introduce ODR violations. + static_assert( + !fmt::is_formattable<explicitly_convertible_to_std_string_view>::value, + ""); +} +# endif +#endif + +struct convertible_to_long_long { + operator long long() const { return 1LL << 32; } +}; + +TEST(format_test, format_convertible_to_long_long) { + EXPECT_EQ("100000000", fmt::format("{:x}", convertible_to_long_long())); +} + +struct disabled_rvalue_conversion { + operator const char*() const& { return "foo"; } + operator const char*() & { return "foo"; } + operator const char*() const&& = delete; + operator const char*() && = delete; +}; + +TEST(core_test, disabled_rvalue_conversion) { + EXPECT_EQ("foo", fmt::format("{}", disabled_rvalue_conversion())); +} + +namespace adl_test { +template <typename... T> void make_format_args(const T&...) = delete; + +struct string : std::string {}; +} // namespace adl_test + +// Test that formatting functions compile when make_format_args is found by ADL. +TEST(core_test, adl) { + // Only check compilation and don't run the code to avoid polluting the output + // and since the output is tested elsewhere. + if (fmt::detail::const_check(true)) return; + auto s = adl_test::string(); + char buf[10]; + (void)fmt::format("{}", s); + fmt::format_to(buf, "{}", s); + fmt::format_to_n(buf, 10, "{}", s); + (void)fmt::formatted_size("{}", s); + fmt::print("{}", s); + fmt::print(stdout, "{}", s); +} + +TEST(core_test, has_const_formatter) { + EXPECT_TRUE((fmt::detail::has_const_formatter<const_formattable, + fmt::format_context>())); + EXPECT_FALSE((fmt::detail::has_const_formatter<nonconst_formattable, + fmt::format_context>())); +} + +TEST(core_test, format_nonconst) { + EXPECT_EQ(fmt::format("{}", nonconst_formattable()), "test"); +} diff --git a/src/fmt/test/cuda-test/CMakeLists.txt b/src/fmt/test/cuda-test/CMakeLists.txt new file mode 100644 index 000000000..be8685b89 --- /dev/null +++ b/src/fmt/test/cuda-test/CMakeLists.txt @@ -0,0 +1,73 @@ +# We can find some usecases which follow the guide of CMake which uses +# `enable_language(CUDA)` instead of `find_package(CUDA)` and let the CMake +# built-in functions use NVCC. + +# See: https://cmake.org/cmake/help/latest/module/FindCUDA.html#replacement +# +# However, this requires CMake version 3.10 or higher and we can't be sure most +# of the CUDA projects are using those. +# +# This test relies on `find_package(CUDA)` in the parent CMake config. + +# These can be updated when NVCC becomes ready for C++ 17 features +# https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features +set(CMAKE_CUDA_STANDARD 14) +set(CMAKE_CUDA_STANDARD_REQUIRED 14) + +# In this test, we assume that the user is going to compile CUDA source code +# with some libraries (fmt in this case). +# +# In addition to that, this test invokes both the C++ host compiler and NVCC +# by providing another (non-CUDA) C++ source code. +if (${CMAKE_VERSION} VERSION_LESS 3.15) + # https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html + list(APPEND CUDA_NVCC_FLAGS "-std=c++14") + if (MSVC) + # This is the solution of pytorch: + # https://github.com/pytorch/pytorch/pull/7118 + list(APPEND CUDA_NVCC_FLAGS "-Xcompiler" "/std:c++14") + list(APPEND CUDA_NVCC_FLAGS "-Xcompiler" "/Zc:__cplusplus") + # for the reason of this -Xcompiler options, see below. + endif () + cuda_add_executable(fmt-in-cuda-test cuda-cpp14.cu cpp14.cc) + target_compile_features(fmt-in-cuda-test PRIVATE cxx_std_14) + if (MSVC) + # This part is for (non-CUDA) C++ code. MSVC can define incorrect + # `__cplusplus` macro. Fix for the issue is to use additional compiler flag. + # + # See Also: + # https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ + # https://github.com/Microsoft/vscode-cpptools/issues/2595 + target_compile_options(fmt-in-cuda-test PRIVATE /Zc:__cplusplus /permissive-) + endif () +else() + # now using a "new" way of handling CUDA + add_executable(fmt-in-cuda-test cuda-cpp14.cu cpp14.cc) + set_target_properties(fmt-in-cuda-test PROPERTIES CUDA_SEPARABLE_COMPILATION ON) + target_compile_features(fmt-in-cuda-test PRIVATE cxx_std_14) + if (MSVC) + # with MSVC, 'cxx_std_14' will only propagate to the host code (MSVC), but will + # not set __cplusplus correctly anyway, while nvcc will ignore it. + # If specified for nvcc on the command line as '-std=c++14' nvcc will emit this + # message instead: + # nvcc warning : The -std=c++14 flag is not supported with the configured host + # compiler. Flag will be ignored. + set_property(SOURCE cuda-cpp14.cu APPEND PROPERTY + COMPILE_OPTIONS -Xcompiler /std:c++14 -Xcompiler /Zc:__cplusplus) + set_property(SOURCE cpp14.cc APPEND PROPERTY + COMPILE_OPTIONS /std:c++14 /Zc:__cplusplus) + endif() +endif() + +get_target_property(IN_USE_CUDA_STANDARD fmt-in-cuda-test CUDA_STANDARD) +message(STATUS "cuda_standard: ${IN_USE_CUDA_STANDARD}") + +get_target_property(IN_USE_CUDA_STANDARD_REQUIRED + fmt-in-cuda-test CUDA_STANDARD_REQUIRED) +message(STATUS "cuda_standard_required: ${IN_USE_CUDA_STANDARD_REQUIRED}") + +# We don't use PUBLIC or other keyword for reasons explained in the +# CUDA_LINK_LIBRARIES_KEYWORD section in +# https://cmake.org/cmake/help/latest/module/FindCUDA.html +target_link_libraries(fmt-in-cuda-test fmt::fmt) + diff --git a/src/fmt/test/cuda-test/cpp14.cc b/src/fmt/test/cuda-test/cpp14.cc new file mode 100644 index 000000000..ad8463962 --- /dev/null +++ b/src/fmt/test/cuda-test/cpp14.cc @@ -0,0 +1,11 @@ +#include <fmt/core.h> + +// The purpose of this part is to ensure NVCC's host compiler also supports +// the standard version. See 'cuda-cpp14.cu'. +// +// https://en.cppreference.com/w/cpp/preprocessor/replace#Predefined_macros +static_assert(__cplusplus >= 201402L, "expect C++ 2014 for host compiler"); + +auto make_message_cpp() -> std::string { + return fmt::format("host compiler \t: __cplusplus == {}", __cplusplus); +} diff --git a/src/fmt/test/cuda-test/cuda-cpp14.cu b/src/fmt/test/cuda-test/cuda-cpp14.cu new file mode 100644 index 000000000..76a9d0883 --- /dev/null +++ b/src/fmt/test/cuda-test/cuda-cpp14.cu @@ -0,0 +1,28 @@ +// Direct NVCC command line example: +// +// nvcc ./cuda-cpp14.cu -x cu -I"../include" -l"fmtd" -L"../build/Debug" \ +// -std=c++14 -Xcompiler /std:c++14 -Xcompiler /Zc:__cplusplus + +// Ensure that we are using the latest C++ standard for NVCC +// The version is C++14 +// +// https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#c-cplusplus-language-support +// https://en.cppreference.com/w/cpp/preprocessor/replace#Predefined_macros +static_assert(__cplusplus >= 201402L, "expect C++ 2014 for nvcc"); + +#include <fmt/core.h> + +#include <cuda.h> +#include <iostream> + +extern auto make_message_cpp() -> std::string; +extern auto make_message_cuda() -> std::string; + +int main() { + std::cout << make_message_cuda() << std::endl; + std::cout << make_message_cpp() << std::endl; +} + +auto make_message_cuda() -> std::string { + return fmt::format("nvcc compiler \t: __cplusplus == {}", __cplusplus); +} diff --git a/src/fmt/test/detect-stdfs.cc b/src/fmt/test/detect-stdfs.cc new file mode 100644 index 000000000..2dc665383 --- /dev/null +++ b/src/fmt/test/detect-stdfs.cc @@ -0,0 +1,18 @@ +// Formatting library for C++ - tests of formatters for standard library types +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include <exception> // _GLIBCXX_RELEASE & _LIBCPP_VERSION + +#if defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8 +# error libfound "stdc++fs" +#elif !defined(__apple_build_version__) && defined(_LIBCPP_VERSION) && \ + _LIBCPP_VERSION >= 7000 && _LIBCPP_VERSION < 9000 +# error libfound "c++fs" +#else +// none if std::filesystem does not require additional libraries +# error libfound "" +#endif diff --git a/src/fmt/test/enforce-checks-test.cc b/src/fmt/test/enforce-checks-test.cc new file mode 100644 index 000000000..c77cb142f --- /dev/null +++ b/src/fmt/test/enforce-checks-test.cc @@ -0,0 +1,63 @@ +// Formatting library for C++ - formatting library tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include <iterator> +#include <vector> + +#include "fmt/chrono.h" +#include "fmt/color.h" +#include "fmt/format.h" +#include "fmt/ostream.h" +#include "fmt/ranges.h" +#include "fmt/xchar.h" + +// Exercise the API to verify that everything we expect to can compile. +void test_format_api() { + (void)fmt::format(FMT_STRING("{}"), 42); + (void)fmt::format(FMT_STRING(L"{}"), 42); + (void)fmt::format(FMT_STRING("noop")); + + (void)fmt::to_string(42); + (void)fmt::to_wstring(42); + + std::vector<char> out; + fmt::format_to(std::back_inserter(out), FMT_STRING("{}"), 42); + + char buffer[4]; + fmt::format_to_n(buffer, 3, FMT_STRING("{}"), 12345); + + wchar_t wbuffer[4]; + fmt::format_to_n(wbuffer, 3, FMT_STRING(L"{}"), 12345); +} + +void test_chrono() { + (void)fmt::format(FMT_STRING("{}"), std::chrono::seconds(42)); + (void)fmt::format(FMT_STRING(L"{}"), std::chrono::seconds(42)); +} + +void test_text_style() { + fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)"); + (void)fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), + "rgb(255,20,30)"); + + fmt::text_style ts = fg(fmt::rgb(255, 20, 30)); + std::string out; + fmt::format_to(std::back_inserter(out), ts, + FMT_STRING("rgb(255,20,30){}{}{}"), 1, 2, 3); +} + +void test_range() { + std::vector<char> hello = {'h', 'e', 'l', 'l', 'o'}; + (void)fmt::format(FMT_STRING("{}"), hello); +} + +int main() { + test_format_api(); + test_chrono(); + test_text_style(); + test_range(); +} diff --git a/src/fmt/test/find-package-test/CMakeLists.txt b/src/fmt/test/find-package-test/CMakeLists.txt new file mode 100644 index 000000000..93d686e64 --- /dev/null +++ b/src/fmt/test/find-package-test/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.1...3.18) + +project(fmt-test) + +find_package(FMT REQUIRED) + +add_executable(library-test main.cc) +target_link_libraries(library-test fmt::fmt) +target_compile_options(library-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) +target_include_directories(library-test PUBLIC SYSTEM .) + +if (TARGET fmt::fmt-header-only) + add_executable(header-only-test main.cc) + target_link_libraries(header-only-test fmt::fmt-header-only) + target_compile_options(header-only-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) + target_include_directories(header-only-test PUBLIC SYSTEM .) +endif () diff --git a/src/fmt/test/find-package-test/main.cc b/src/fmt/test/find-package-test/main.cc new file mode 100644 index 000000000..911e0e8d1 --- /dev/null +++ b/src/fmt/test/find-package-test/main.cc @@ -0,0 +1,5 @@ +#include "fmt/format.h" + +int main(int argc, char** argv) { + for (int i = 0; i < argc; ++i) fmt::print("{}: {}\n", i, argv[i]); +} diff --git a/src/fmt/test/format-impl-test.cc b/src/fmt/test/format-impl-test.cc new file mode 100644 index 000000000..46486a6f4 --- /dev/null +++ b/src/fmt/test/format-impl-test.cc @@ -0,0 +1,547 @@ +// Formatting library for C++ - formatting library implementation tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include <algorithm> +#include <cstring> + +// clang-format off +#include "test-assert.h" +// clang-format on + +#include "fmt/format.h" +#include "gmock/gmock.h" +#include "util.h" + +using fmt::detail::bigint; +using fmt::detail::fp; +using fmt::detail::max_value; + +static_assert(!std::is_copy_constructible<bigint>::value, ""); +static_assert(!std::is_copy_assignable<bigint>::value, ""); + +TEST(bigint_test, construct) { + EXPECT_EQ(fmt::to_string(bigint()), ""); + EXPECT_EQ(fmt::to_string(bigint(0x42)), "42"); + EXPECT_EQ(fmt::to_string(bigint(0x123456789abcedf0)), "123456789abcedf0"); +} + +TEST(bigint_test, compare) { + bigint n1(42); + bigint n2(42); + EXPECT_EQ(compare(n1, n2), 0); + n2 <<= 32; + EXPECT_LT(compare(n1, n2), 0); + bigint n3(43); + EXPECT_LT(compare(n1, n3), 0); + EXPECT_GT(compare(n3, n1), 0); + bigint n4(42 * 0x100000001); + EXPECT_LT(compare(n2, n4), 0); + EXPECT_GT(compare(n4, n2), 0); +} + +TEST(bigint_test, add_compare) { + EXPECT_LT( + add_compare(bigint(0xffffffff), bigint(0xffffffff), bigint(1) <<= 64), 0); + EXPECT_LT(add_compare(bigint(1) <<= 32, bigint(1), bigint(1) <<= 96), 0); + EXPECT_GT(add_compare(bigint(1) <<= 32, bigint(0), bigint(0xffffffff)), 0); + EXPECT_GT(add_compare(bigint(0), bigint(1) <<= 32, bigint(0xffffffff)), 0); + EXPECT_GT(add_compare(bigint(42), bigint(1), bigint(42)), 0); + EXPECT_GT(add_compare(bigint(0xffffffff), bigint(1), bigint(0xffffffff)), 0); + EXPECT_LT(add_compare(bigint(10), bigint(10), bigint(22)), 0); + EXPECT_LT(add_compare(bigint(0x100000010), bigint(0x100000010), + bigint(0x300000010)), + 0); + EXPECT_GT(add_compare(bigint(0x1ffffffff), bigint(0x100000002), + bigint(0x300000000)), + 0); + EXPECT_EQ(add_compare(bigint(0x1ffffffff), bigint(0x100000002), + bigint(0x300000001)), + 0); + EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002), + bigint(0x300000002)), + 0); + EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002), + bigint(0x300000003)), + 0); +} + +TEST(bigint_test, shift_left) { + bigint n(0x42); + n <<= 0; + EXPECT_EQ(fmt::to_string(n), "42"); + n <<= 1; + EXPECT_EQ(fmt::to_string(n), "84"); + n <<= 25; + EXPECT_EQ(fmt::to_string(n), "108000000"); +} + +TEST(bigint_test, multiply) { + bigint n(0x42); + EXPECT_THROW(n *= 0, assertion_failure); + n *= 1; + EXPECT_EQ(fmt::to_string(n), "42"); + + n *= 2; + EXPECT_EQ(fmt::to_string(n), "84"); + n *= 0x12345678; + EXPECT_EQ(fmt::to_string(n), "962fc95e0"); + + bigint bigmax(max_value<uint32_t>()); + bigmax *= max_value<uint32_t>(); + EXPECT_EQ(fmt::to_string(bigmax), "fffffffe00000001"); + + const auto max64 = max_value<uint64_t>(); + bigmax = max64; + bigmax *= max64; + EXPECT_EQ(fmt::to_string(bigmax), "fffffffffffffffe0000000000000001"); + + const auto max128 = (fmt::detail::uint128_t(max64) << 64) | max64; + bigmax = max128; + bigmax *= max128; + EXPECT_EQ(fmt::to_string(bigmax), + "fffffffffffffffffffffffffffffffe00000000000000000000000000000001"); +} + +TEST(bigint_test, square) { + bigint n0(0); + n0.square(); + EXPECT_EQ(fmt::to_string(n0), "0"); + bigint n1(0x100); + n1.square(); + EXPECT_EQ(fmt::to_string(n1), "10000"); + bigint n2(0xfffffffff); + n2.square(); + EXPECT_EQ(fmt::to_string(n2), "ffffffffe000000001"); + bigint n3(max_value<uint64_t>()); + n3.square(); + EXPECT_EQ(fmt::to_string(n3), "fffffffffffffffe0000000000000001"); + bigint n4; + n4.assign_pow10(10); + EXPECT_EQ(fmt::to_string(n4), "2540be400"); +} + +TEST(bigint_test, divmod_assign_zero_divisor) { + bigint zero(0); + EXPECT_THROW(bigint(0).divmod_assign(zero), assertion_failure); + EXPECT_THROW(bigint(42).divmod_assign(zero), assertion_failure); +} + +TEST(bigint_test, divmod_assign_self) { + bigint n(100); + EXPECT_THROW(n.divmod_assign(n), assertion_failure); +} + +TEST(bigint_test, divmod_assign_unaligned) { + // (42 << 340) / pow(10, 100): + bigint n1(42); + n1 <<= 340; + bigint n2; + n2.assign_pow10(100); + int result = n1.divmod_assign(n2); + EXPECT_EQ(result, 9406); + EXPECT_EQ(fmt::to_string(n1), + "10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96"); +} + +TEST(bigint_test, divmod_assign) { + // 100 / 10: + bigint n1(100); + int result = n1.divmod_assign(bigint(10)); + EXPECT_EQ(result, 10); + EXPECT_EQ(fmt::to_string(n1), "0"); + // pow(10, 100) / (42 << 320): + n1.assign_pow10(100); + result = n1.divmod_assign(bigint(42) <<= 320); + EXPECT_EQ(result, 111); + EXPECT_EQ(fmt::to_string(n1), + "13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96"); + // 42 / 100: + bigint n2(42); + n1.assign_pow10(2); + result = n2.divmod_assign(n1); + EXPECT_EQ(result, 0); + EXPECT_EQ(fmt::to_string(n2), "2a"); +} + +template <bool is_iec559> void run_double_tests() { + fmt::print("warning: double is not IEC559, skipping FP tests\n"); +} + +template <> void run_double_tests<true>() { + // Construct from double. + EXPECT_EQ(fp(1.23), fp(0x13ae147ae147aeu, -52)); +} + +TEST(fp_test, double_tests) { + run_double_tests<std::numeric_limits<double>::is_iec559>(); +} + +TEST(fp_test, normalize) { + const auto v = fp(0xbeef, 42); + auto normalized = normalize(v); + EXPECT_EQ(normalized.f, 0xbeef000000000000); + EXPECT_EQ(normalized.e, -6); +} + +TEST(fp_test, multiply) { + auto v = fp(123ULL << 32, 4) * fp(56ULL << 32, 7); + EXPECT_EQ(v.f, 123u * 56u); + EXPECT_EQ(v.e, 4 + 7 + 64); + v = fp(123ULL << 32, 4) * fp(567ULL << 31, 8); + EXPECT_EQ(v.f, (123 * 567 + 1u) / 2); + EXPECT_EQ(v.e, 4 + 8 + 64); +} + +TEST(fp_test, get_cached_power) { + using limits = std::numeric_limits<double>; + for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) { + int dec_exp = 0; + auto power = fmt::detail::get_cached_power(exp, dec_exp); + bigint exact, cache(power.f); + if (dec_exp >= 0) { + exact.assign_pow10(dec_exp); + if (power.e <= 0) + exact <<= -power.e; + else + cache <<= power.e; + exact.align(cache); + cache.align(exact); + auto exact_str = fmt::to_string(exact); + auto cache_str = fmt::to_string(cache); + EXPECT_EQ(exact_str.size(), cache_str.size()); + EXPECT_EQ(exact_str.substr(0, 15), cache_str.substr(0, 15)); + int diff = cache_str[15] - exact_str[15]; + if (diff == 1) + EXPECT_GT(exact_str[16], '8'); + else + EXPECT_EQ(diff, 0); + } else { + cache.assign_pow10(-dec_exp); + cache *= power.f + 1; // Inexact check. + exact = 1; + exact <<= -power.e; + exact.align(cache); + auto exact_str = fmt::to_string(exact); + auto cache_str = fmt::to_string(cache); + EXPECT_EQ(exact_str.size(), cache_str.size()); + EXPECT_EQ(exact_str.substr(0, 16), cache_str.substr(0, 16)); + } + } +} + +TEST(fp_test, dragonbox_max_k) { + using fmt::detail::dragonbox::floor_log10_pow2; + using float_info = fmt::detail::dragonbox::float_info<float>; + EXPECT_EQ( + fmt::detail::const_check(float_info::max_k), + float_info::kappa - + floor_log10_pow2(std::numeric_limits<float>::min_exponent - + fmt::detail::num_significand_bits<float>() - 1)); + using double_info = fmt::detail::dragonbox::float_info<double>; + EXPECT_EQ( + fmt::detail::const_check(double_info::max_k), + double_info::kappa - + floor_log10_pow2(std::numeric_limits<double>::min_exponent - + fmt::detail::num_significand_bits<double>() - 1)); +} + +TEST(fp_test, get_round_direction) { + using fmt::detail::get_round_direction; + using fmt::detail::round_direction; + EXPECT_EQ(get_round_direction(100, 50, 0), round_direction::down); + EXPECT_EQ(get_round_direction(100, 51, 0), round_direction::up); + EXPECT_EQ(get_round_direction(100, 40, 10), round_direction::down); + EXPECT_EQ(get_round_direction(100, 60, 10), round_direction::up); + for (size_t i = 41; i < 60; ++i) + EXPECT_EQ(get_round_direction(100, i, 10), round_direction::unknown); + uint64_t max = max_value<uint64_t>(); + EXPECT_THROW(get_round_direction(100, 100, 0), assertion_failure); + EXPECT_THROW(get_round_direction(100, 0, 100), assertion_failure); + EXPECT_THROW(get_round_direction(100, 0, 50), assertion_failure); + // Check that remainder + error doesn't overflow. + EXPECT_EQ(get_round_direction(max, max - 1, 2), round_direction::up); + // Check that 2 * (remainder + error) doesn't overflow. + EXPECT_EQ(get_round_direction(max, max / 2 + 1, max / 2), + round_direction::unknown); + // Check that remainder - error doesn't overflow. + EXPECT_EQ(get_round_direction(100, 40, 41), round_direction::unknown); + // Check that 2 * (remainder - error) doesn't overflow. + EXPECT_EQ(get_round_direction(max, max - 1, 1), round_direction::up); +} + +TEST(fp_test, fixed_handler) { + struct handler : fmt::detail::gen_digits_handler { + char buffer[10]; + handler(int prec = 0) : fmt::detail::gen_digits_handler() { + buf = buffer; + precision = prec; + } + }; + handler().on_digit('0', 100, 99, 0, false); + EXPECT_THROW(handler().on_digit('0', 100, 100, 0, false), assertion_failure); + namespace digits = fmt::detail::digits; + EXPECT_EQ(handler(1).on_digit('0', 100, 10, 10, false), digits::error); + // Check that divisor - error doesn't overflow. + EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, false), digits::error); + // Check that 2 * error doesn't overflow. + uint64_t max = max_value<uint64_t>(); + EXPECT_EQ(handler(1).on_digit('0', max, 10, max - 1, false), digits::error); +} + +TEST(fp_test, grisu_format_compiles_with_on_ieee_double) { + auto buf = fmt::memory_buffer(); + format_float(0.42, -1, fmt::detail::float_specs(), buf); +} + +TEST(format_impl_test, format_error_code) { + std::string msg = "error 42", sep = ": "; + { + auto buffer = fmt::memory_buffer(); + format_to(fmt::appender(buffer), "garbage"); + fmt::detail::format_error_code(buffer, 42, "test"); + EXPECT_EQ(to_string(buffer), "test: " + msg); + } + { + auto buffer = fmt::memory_buffer(); + auto prefix = + std::string(fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x'); + fmt::detail::format_error_code(buffer, 42, prefix); + EXPECT_EQ(msg, to_string(buffer)); + } + int codes[] = {42, -1}; + for (size_t i = 0, n = sizeof(codes) / sizeof(*codes); i < n; ++i) { + // Test maximum buffer size. + msg = fmt::format("error {}", codes[i]); + fmt::memory_buffer buffer; + auto prefix = + std::string(fmt::inline_buffer_size - msg.size() - sep.size(), 'x'); + fmt::detail::format_error_code(buffer, codes[i], prefix); + EXPECT_EQ(prefix + sep + msg, to_string(buffer)); + size_t size = fmt::inline_buffer_size; + EXPECT_EQ(size, buffer.size()); + buffer.resize(0); + // Test with a message that doesn't fit into the buffer. + prefix += 'x'; + fmt::detail::format_error_code(buffer, codes[i], prefix); + EXPECT_EQ(to_string(buffer), msg); + } +} + +TEST(format_impl_test, compute_width) { + EXPECT_EQ(4, + fmt::detail::compute_width( + fmt::basic_string_view<fmt::detail::char8_type>( + reinterpret_cast<const fmt::detail::char8_type*>("ёжик")))); +} + +// Tests fmt::detail::count_digits for integer type Int. +template <typename Int> void test_count_digits() { + for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::detail::count_digits(i)); + for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) { + n *= 10; + EXPECT_EQ(fmt::detail::count_digits(n - 1), i); + EXPECT_EQ(fmt::detail::count_digits(n), i + 1); + } +} + +TEST(format_impl_test, count_digits) { + test_count_digits<uint32_t>(); + test_count_digits<uint64_t>(); +} + +#if FMT_USE_FLOAT128 +TEST(format_impl_test, write_float128) { + auto s = std::string(); + fmt::detail::write<char>(std::back_inserter(s), __float128(42)); + EXPECT_EQ(s, "42"); +} +#endif + +struct double_double { + double a; + double b; + + explicit constexpr double_double(double a_val = 0, double b_val = 0) + : a(a_val), b(b_val) {} + + operator double() const { return a + b; } + auto operator-() const -> double_double { return double_double(-a, -b); } +}; + +bool operator>=(const double_double& lhs, const double_double& rhs) { + return lhs.a + lhs.b >= rhs.a + rhs.b; +} + +struct slow_float { + float value; + + explicit constexpr slow_float(float val = 0) : value(val) {} + operator float() const { return value; } + auto operator-() const -> slow_float { return slow_float(-value); } +}; + +namespace std { +template <> struct is_floating_point<double_double> : std::true_type {}; +template <> struct numeric_limits<double_double> { + // is_iec559 is true for double-double in libstdc++. + static constexpr bool is_iec559 = true; + static constexpr int digits = 106; +}; + +template <> struct is_floating_point<slow_float> : std::true_type {}; +template <> struct numeric_limits<slow_float> : numeric_limits<float> {}; +} // namespace std + +FMT_BEGIN_NAMESPACE +namespace detail { +template <> struct is_fast_float<slow_float> : std::false_type {}; +namespace dragonbox { +template <> struct float_info<slow_float> { + using carrier_uint = uint32_t; + static const int exponent_bits = 8; +}; +} // namespace dragonbox +} // namespace detail +FMT_END_NAMESPACE + +TEST(format_impl_test, write_double_double) { + auto s = std::string(); + fmt::detail::write<char>(std::back_inserter(s), double_double(42), {}); + // Specializing is_floating_point is broken in MSVC. + if (!FMT_MSC_VERSION) EXPECT_EQ(s, "42"); +} + +TEST(format_impl_test, write_dragon_even) { + auto s = std::string(); + fmt::detail::write<char>(std::back_inserter(s), slow_float(33554450.0f), {}); + // Specializing is_floating_point is broken in MSVC. + if (!FMT_MSC_VERSION) EXPECT_EQ(s, "33554450"); +} + +#ifdef _WIN32 +# include <windows.h> + +TEST(format_impl_test, write_console_signature) { + decltype(::WriteConsoleW)* p = fmt::detail::WriteConsoleW; + (void)p; +} +#endif + +// A public domain branchless UTF-8 decoder by Christopher Wellons: +// https://github.com/skeeto/branchless-utf8 +constexpr bool unicode_is_surrogate(uint32_t c) { + return c >= 0xD800U && c <= 0xDFFFU; +} + +FMT_CONSTEXPR char* utf8_encode(char* s, uint32_t c) { + if (c >= (1UL << 16)) { + s[0] = static_cast<char>(0xf0 | (c >> 18)); + s[1] = static_cast<char>(0x80 | ((c >> 12) & 0x3f)); + s[2] = static_cast<char>(0x80 | ((c >> 6) & 0x3f)); + s[3] = static_cast<char>(0x80 | ((c >> 0) & 0x3f)); + return s + 4; + } else if (c >= (1UL << 11)) { + s[0] = static_cast<char>(0xe0 | (c >> 12)); + s[1] = static_cast<char>(0x80 | ((c >> 6) & 0x3f)); + s[2] = static_cast<char>(0x80 | ((c >> 0) & 0x3f)); + return s + 3; + } else if (c >= (1UL << 7)) { + s[0] = static_cast<char>(0xc0 | (c >> 6)); + s[1] = static_cast<char>(0x80 | ((c >> 0) & 0x3f)); + return s + 2; + } else { + s[0] = static_cast<char>(c); + return s + 1; + } +} + +// Make sure it can decode every character +TEST(format_impl_test, utf8_decode_decode_all) { + for (uint32_t i = 0; i < 0x10ffff; i++) { + if (!unicode_is_surrogate(i)) { + int e; + uint32_t c; + char buf[8] = {0}; + char* end = utf8_encode(buf, i); + const char* res = fmt::detail::utf8_decode(buf, &c, &e); + EXPECT_EQ(end, res); + EXPECT_EQ(c, i); + EXPECT_EQ(e, 0); + } + } +} + +// Reject everything outside of U+0000..U+10FFFF +TEST(format_impl_test, utf8_decode_out_of_range) { + for (uint32_t i = 0x110000; i < 0x1fffff; i++) { + int e; + uint32_t c; + char buf[8] = {0}; + utf8_encode(buf, i); + const char* end = fmt::detail::utf8_decode(buf, &c, &e); + EXPECT_NE(e, 0); + EXPECT_EQ(end - buf, 4); + } +} + +// Does it reject all surrogate halves? +TEST(format_impl_test, utf8_decode_surrogate_halves) { + for (uint32_t i = 0xd800; i <= 0xdfff; i++) { + int e; + uint32_t c; + char buf[8] = {0}; + utf8_encode(buf, i); + fmt::detail::utf8_decode(buf, &c, &e); + EXPECT_NE(e, 0); + } +} + +// How about non-canonical encodings? +TEST(format_impl_test, utf8_decode_non_canonical_encodings) { + int e; + uint32_t c; + const char* end; + + char buf2[8] = {char(0xc0), char(0xA4)}; + end = fmt::detail::utf8_decode(buf2, &c, &e); + EXPECT_NE(e, 0); // non-canonical len 2 + EXPECT_EQ(end, buf2 + 2); // non-canonical recover 2 + + char buf3[8] = {char(0xe0), char(0x80), char(0xA4)}; + end = fmt::detail::utf8_decode(buf3, &c, &e); + EXPECT_NE(e, 0); // non-canonical len 3 + EXPECT_EQ(end, buf3 + 3); // non-canonical recover 3 + + char buf4[8] = {char(0xf0), char(0x80), char(0x80), char(0xA4)}; + end = fmt::detail::utf8_decode(buf4, &c, &e); + EXPECT_NE(e, 0); // non-canonical encoding len 4 + EXPECT_EQ(end, buf4 + 4); // non-canonical recover 4 +} + +// Let's try some bogus byte sequences +TEST(format_impl_test, utf8_decode_bogus_byte_sequences) { + int e; + uint32_t c; + + // Invalid first byte + char buf0[4] = {char(0xff)}; + auto len = fmt::detail::utf8_decode(buf0, &c, &e) - buf0; + EXPECT_NE(e, 0); // "bogus [ff] 0x%02x U+%04lx", e, (unsigned long)c); + EXPECT_EQ(len, 1); // "bogus [ff] recovery %d", len); + + // Invalid first byte + char buf1[4] = {char(0x80)}; + len = fmt::detail::utf8_decode(buf1, &c, &e) - buf1; + EXPECT_NE(e, 0); // "bogus [80] 0x%02x U+%04lx", e, (unsigned long)c); + EXPECT_EQ(len, 1); // "bogus [80] recovery %d", len); + + // Looks like a two-byte sequence but second byte is wrong + char buf2[4] = {char(0xc0), char(0x0a)}; + len = fmt::detail::utf8_decode(buf2, &c, &e) - buf2; + EXPECT_NE(e, 0); // "bogus [c0 0a] 0x%02x U+%04lx", e, (unsigned long)c + EXPECT_EQ(len, 2); // "bogus [c0 0a] recovery %d", len); +} diff --git a/src/fmt/test/format-test.cc b/src/fmt/test/format-test.cc new file mode 100644 index 000000000..f3bdb4649 --- /dev/null +++ b/src/fmt/test/format-test.cc @@ -0,0 +1,2311 @@ +// Formatting library for C++ - formatting library tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +// Check if fmt/format.h compiles with windows.h included before it. +#ifdef _WIN32 +# include <windows.h> +#endif +// clang-format off +#include "fmt/format.h" +// clang-format on + +#include <stdint.h> // uint32_t + +#include <climits> // INT_MAX +#include <cmath> // std::signbit +#include <cstring> // std::strlen +#include <iterator> // std::back_inserter +#include <list> // std::list +#include <memory> // std::unique_ptr +#include <type_traits> // std::is_default_constructible + +#include "gtest-extra.h" +#include "mock-allocator.h" +#include "util.h" + +using fmt::basic_memory_buffer; +using fmt::format_error; +using fmt::memory_buffer; +using fmt::runtime; +using fmt::string_view; +using fmt::detail::max_value; +using fmt::detail::uint128_fallback; + +using testing::Return; +using testing::StrictMock; + +enum { buffer_size = 256 }; + +TEST(uint128_test, ctor) { + auto n = uint128_fallback(); + EXPECT_EQ(n, 0); + n = uint128_fallback(42); + EXPECT_EQ(n, 42); + EXPECT_EQ(static_cast<uint64_t>(n), 42); +} + +TEST(uint128_test, shift) { + auto n = uint128_fallback(42); + n = n << 64; + EXPECT_EQ(static_cast<uint64_t>(n), 0); + n = n >> 64; + EXPECT_EQ(static_cast<uint64_t>(n), 42); + n = n << 62; + EXPECT_EQ(static_cast<uint64_t>(n >> 64), 0xa); + EXPECT_EQ(static_cast<uint64_t>(n), 0x8000000000000000); + n = n >> 62; + EXPECT_EQ(static_cast<uint64_t>(n), 42); + EXPECT_EQ(uint128_fallback(1) << 112, uint128_fallback(0x1000000000000, 0)); + EXPECT_EQ(uint128_fallback(0x1000000000000, 0) >> 112, uint128_fallback(1)); +} + +TEST(uint128_test, minus) { + auto n = uint128_fallback(42); + EXPECT_EQ(n - 2, 40); +} + +TEST(uint128_test, plus_assign) { + auto n = uint128_fallback(32); + n += uint128_fallback(10); + EXPECT_EQ(n, 42); + n = uint128_fallback(max_value<uint64_t>()); + n += uint128_fallback(1); + EXPECT_EQ(n, uint128_fallback(1) << 64); +} + +TEST(uint128_test, multiply) { + auto n = uint128_fallback(2251799813685247); + n = n * 3611864890; + EXPECT_EQ(static_cast<uint64_t>(n >> 64), 440901); +} + +template <typename Float> void check_isfinite() { + using fmt::detail::isfinite; + EXPECT_TRUE(isfinite(Float(0.0))); + EXPECT_TRUE(isfinite(Float(42.0))); + EXPECT_TRUE(isfinite(Float(-42.0))); + EXPECT_TRUE(isfinite(Float(fmt::detail::max_value<double>()))); + // Use double because std::numeric_limits is broken for __float128. + using limits = std::numeric_limits<double>; + FMT_CONSTEXPR20 auto result = isfinite(Float(limits::infinity())); + EXPECT_FALSE(result); + EXPECT_FALSE(isfinite(Float(limits::infinity()))); + EXPECT_FALSE(isfinite(Float(-limits::infinity()))); + EXPECT_FALSE(isfinite(Float(limits::quiet_NaN()))); + EXPECT_FALSE(isfinite(Float(-limits::quiet_NaN()))); +} + +TEST(float_test, isfinite) { + check_isfinite<double>(); +#if FMT_USE_FLOAT128 + check_isfinite<fmt::detail::float128>(); +#endif +} + +template <typename Float> void check_isnan() { + using fmt::detail::isnan; + EXPECT_FALSE(isnan(Float(0.0))); + EXPECT_FALSE(isnan(Float(42.0))); + EXPECT_FALSE(isnan(Float(-42.0))); + EXPECT_FALSE(isnan(Float(fmt::detail::max_value<double>()))); + // Use double because std::numeric_limits is broken for __float128. + using limits = std::numeric_limits<double>; + EXPECT_FALSE(isnan(Float(limits::infinity()))); + EXPECT_FALSE(isnan(Float(-limits::infinity()))); + EXPECT_TRUE(isnan(Float(limits::quiet_NaN()))); + EXPECT_TRUE(isnan(Float(-limits::quiet_NaN()))); +} + +TEST(float_test, isnan) { + check_isnan<double>(); +#if FMT_USE_FLOAT128 + check_isnan<fmt::detail::float128>(); +#endif +} + +struct uint32_pair { + uint32_t u[2]; +}; + +TEST(util_test, bit_cast) { + auto s = fmt::detail::bit_cast<uint32_pair>(uint64_t{42}); + EXPECT_EQ(fmt::detail::bit_cast<uint64_t>(s), 42ull); + s = fmt::detail::bit_cast<uint32_pair>(~uint64_t{0}); + EXPECT_EQ(fmt::detail::bit_cast<uint64_t>(s), ~0ull); +} + +// Increment a number in a string. +void increment(char* s) { + for (int i = static_cast<int>(std::strlen(s)) - 1; i >= 0; --i) { + if (s[i] != '9') { + ++s[i]; + break; + } + s[i] = '0'; + } +} + +TEST(util_test, increment) { + char s[10] = "123"; + increment(s); + EXPECT_STREQ("124", s); + s[2] = '8'; + increment(s); + EXPECT_STREQ("129", s); + increment(s); + EXPECT_STREQ("130", s); + s[1] = s[2] = '9'; + increment(s); + EXPECT_STREQ("200", s); +} + +TEST(util_test, parse_nonnegative_int) { + auto s = fmt::string_view("10000000000"); + auto begin = s.begin(), end = s.end(); + EXPECT_EQ(fmt::detail::parse_nonnegative_int(begin, end, -1), -1); + s = "2147483649"; + begin = s.begin(); + end = s.end(); + EXPECT_EQ(fmt::detail::parse_nonnegative_int(begin, end, -1), -1); +} + +TEST(util_test, utf8_to_utf16) { + auto u = fmt::detail::utf8_to_utf16("лошадка"); + EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", u.str()); + EXPECT_EQ(7, u.size()); + // U+10437 { DESERET SMALL LETTER YEE } + EXPECT_EQ(L"\xD801\xDC37", fmt::detail::utf8_to_utf16("𐐷").str()); + EXPECT_THROW_MSG(fmt::detail::utf8_to_utf16("\xc3\x28"), std::runtime_error, + "invalid utf8"); + EXPECT_THROW_MSG(fmt::detail::utf8_to_utf16(fmt::string_view("л", 1)), + std::runtime_error, "invalid utf8"); + EXPECT_EQ(L"123456", fmt::detail::utf8_to_utf16("123456").str()); +} + +TEST(util_test, utf8_to_utf16_empty_string) { + auto s = std::string(); + auto u = fmt::detail::utf8_to_utf16(s.c_str()); + EXPECT_EQ(L"", u.str()); + EXPECT_EQ(s.size(), u.size()); +} + +TEST(util_test, allocator_ref) { + using test_allocator_ref = allocator_ref<mock_allocator<int>>; + auto check_forwarding = [](mock_allocator<int>& alloc, + test_allocator_ref& ref) { + int mem; + // Check if value_type is properly defined. + allocator_ref<mock_allocator<int>>::value_type* ptr = &mem; + // Check forwarding. + EXPECT_CALL(alloc, allocate(42)).WillOnce(Return(ptr)); + ref.allocate(42); + EXPECT_CALL(alloc, deallocate(ptr, 42)); + ref.deallocate(ptr, 42); + }; + + StrictMock<mock_allocator<int>> alloc; + auto ref = test_allocator_ref(&alloc); + // Check if allocator_ref forwards to the underlying allocator. + check_forwarding(alloc, ref); + test_allocator_ref ref2(ref); + check_forwarding(alloc, ref2); + test_allocator_ref ref3; + EXPECT_EQ(nullptr, ref3.get()); + ref3 = ref; + check_forwarding(alloc, ref3); +} + +TEST(util_test, format_system_error) { + fmt::memory_buffer message; + fmt::format_system_error(message, EDOM, "test"); + auto ec = std::error_code(EDOM, std::generic_category()); + EXPECT_EQ(to_string(message), std::system_error(ec, "test").what()); + message = fmt::memory_buffer(); + + // Check if std::allocator throws on allocating max size_t / 2 chars. + size_t max_size = max_value<size_t>() / 2; + bool throws_on_alloc = false; + try { + auto alloc = std::allocator<char>(); + alloc.deallocate(alloc.allocate(max_size), max_size); + } catch (const std::bad_alloc&) { + throws_on_alloc = true; + } + if (!throws_on_alloc) { + fmt::print("warning: std::allocator allocates {} chars\n", max_size); + return; + } +} + +TEST(util_test, system_error) { + auto test_error = fmt::system_error(EDOM, "test"); + auto ec = std::error_code(EDOM, std::generic_category()); + EXPECT_STREQ(test_error.what(), std::system_error(ec, "test").what()); + EXPECT_EQ(test_error.code(), ec); + + auto error = std::system_error(std::error_code()); + try { + throw fmt::system_error(EDOM, "test {}", "error"); + } catch (const std::system_error& e) { + error = e; + } + fmt::memory_buffer message; + fmt::format_system_error(message, EDOM, "test error"); + EXPECT_EQ(error.what(), to_string(message)); + EXPECT_EQ(error.code(), std::error_code(EDOM, std::generic_category())); +} + +TEST(util_test, report_system_error) { + fmt::memory_buffer out; + fmt::format_system_error(out, EDOM, "test error"); + out.push_back('\n'); + EXPECT_WRITE(stderr, fmt::report_system_error(EDOM, "test error"), + to_string(out)); +} + +TEST(memory_buffer_test, ctor) { + basic_memory_buffer<char, 123> buffer; + EXPECT_EQ(static_cast<size_t>(0), buffer.size()); + EXPECT_EQ(123u, buffer.capacity()); +} + +using std_allocator = allocator_ref<std::allocator<char>>; + +TEST(memory_buffer_test, move_ctor_inline_buffer) { + auto check_move_buffer = + [](const char* str, basic_memory_buffer<char, 5, std_allocator>& buffer) { + std::allocator<char>* alloc = buffer.get_allocator().get(); + basic_memory_buffer<char, 5, std_allocator> buffer2(std::move(buffer)); + // Move shouldn't destroy the inline content of the first buffer. + EXPECT_EQ(str, std::string(&buffer[0], buffer.size())); + EXPECT_EQ(str, std::string(&buffer2[0], buffer2.size())); + EXPECT_EQ(5u, buffer2.capacity()); + // Move should transfer allocator. + EXPECT_EQ(nullptr, buffer.get_allocator().get()); + EXPECT_EQ(alloc, buffer2.get_allocator().get()); + }; + + auto alloc = std::allocator<char>(); + basic_memory_buffer<char, 5, std_allocator> buffer((std_allocator(&alloc))); + const char test[] = "test"; + buffer.append(string_view(test, 4)); + check_move_buffer("test", buffer); + // Adding one more character fills the inline buffer, but doesn't cause + // dynamic allocation. + buffer.push_back('a'); + check_move_buffer("testa", buffer); +} + +TEST(memory_buffer_test, move_ctor_dynamic_buffer) { + auto alloc = std::allocator<char>(); + basic_memory_buffer<char, 4, std_allocator> buffer((std_allocator(&alloc))); + const char test[] = "test"; + buffer.append(test, test + 4); + const char* inline_buffer_ptr = &buffer[0]; + // Adding one more character causes the content to move from the inline to + // a dynamically allocated buffer. + buffer.push_back('a'); + basic_memory_buffer<char, 4, std_allocator> buffer2(std::move(buffer)); + // Move should rip the guts of the first buffer. + EXPECT_EQ(&buffer[0], inline_buffer_ptr); + EXPECT_EQ(buffer.size(), 0); + EXPECT_EQ(std::string(&buffer2[0], buffer2.size()), "testa"); + EXPECT_GT(buffer2.capacity(), 4u); +} + +void check_move_assign_buffer(const char* str, + basic_memory_buffer<char, 5>& buffer) { + basic_memory_buffer<char, 5> buffer2; + buffer2 = std::move(buffer); + // Move shouldn't destroy the inline content of the first buffer. + EXPECT_EQ(str, std::string(&buffer[0], buffer.size())); + EXPECT_EQ(str, std::string(&buffer2[0], buffer2.size())); + EXPECT_EQ(5u, buffer2.capacity()); +} + +TEST(memory_buffer_test, move_assignment) { + basic_memory_buffer<char, 5> buffer; + const char test[] = "test"; + buffer.append(test, test + 4); + check_move_assign_buffer("test", buffer); + // Adding one more character fills the inline buffer, but doesn't cause + // dynamic allocation. + buffer.push_back('a'); + check_move_assign_buffer("testa", buffer); + const char* inline_buffer_ptr = &buffer[0]; + // Adding one more character causes the content to move from the inline to + // a dynamically allocated buffer. + buffer.push_back('b'); + basic_memory_buffer<char, 5> buffer2; + buffer2 = std::move(buffer); + // Move should rip the guts of the first buffer. + EXPECT_EQ(inline_buffer_ptr, &buffer[0]); + EXPECT_EQ("testab", std::string(&buffer2[0], buffer2.size())); + EXPECT_GT(buffer2.capacity(), 5u); +} + +TEST(memory_buffer_test, grow) { + typedef allocator_ref<mock_allocator<int>> Allocator; + mock_allocator<int> alloc; + basic_memory_buffer<int, 10, Allocator> buffer((Allocator(&alloc))); + buffer.resize(7); + using fmt::detail::to_unsigned; + for (int i = 0; i < 7; ++i) buffer[to_unsigned(i)] = i * i; + EXPECT_EQ(10u, buffer.capacity()); + int mem[20]; + mem[7] = 0xdead; + EXPECT_CALL(alloc, allocate(20)).WillOnce(Return(mem)); + buffer.try_reserve(20); + EXPECT_EQ(20u, buffer.capacity()); + // Check if size elements have been copied + for (int i = 0; i < 7; ++i) EXPECT_EQ(i * i, buffer[to_unsigned(i)]); + // and no more than that. + EXPECT_EQ(0xdead, buffer[7]); + EXPECT_CALL(alloc, deallocate(mem, 20)); +} + +TEST(memory_buffer_test, allocator) { + using test_allocator = allocator_ref<mock_allocator<char>>; + basic_memory_buffer<char, 10, test_allocator> buffer; + EXPECT_EQ(nullptr, buffer.get_allocator().get()); + StrictMock<mock_allocator<char>> alloc; + char mem; + { + basic_memory_buffer<char, 10, test_allocator> buffer2( + (test_allocator(&alloc))); + EXPECT_EQ(&alloc, buffer2.get_allocator().get()); + size_t size = 2 * fmt::inline_buffer_size; + EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem)); + buffer2.reserve(size); + EXPECT_CALL(alloc, deallocate(&mem, size)); + } +} + +TEST(memory_buffer_test, exception_in_deallocate) { + using test_allocator = allocator_ref<mock_allocator<char>>; + StrictMock<mock_allocator<char>> alloc; + basic_memory_buffer<char, 10, test_allocator> buffer( + (test_allocator(&alloc))); + size_t size = 2 * fmt::inline_buffer_size; + auto mem = std::vector<char>(size); + { + EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem[0])); + buffer.resize(size); + std::fill(&buffer[0], &buffer[0] + size, 'x'); + } + auto mem2 = std::vector<char>(2 * size); + { + EXPECT_CALL(alloc, allocate(2 * size)).WillOnce(Return(&mem2[0])); + auto e = std::exception(); + EXPECT_CALL(alloc, deallocate(&mem[0], size)).WillOnce(testing::Throw(e)); + EXPECT_THROW(buffer.reserve(2 * size), std::exception); + EXPECT_EQ(&mem2[0], &buffer[0]); + // Check that the data has been copied. + for (size_t i = 0; i < size; ++i) EXPECT_EQ('x', buffer[i]); + } + EXPECT_CALL(alloc, deallocate(&mem2[0], 2 * size)); +} + +template <typename Allocator, size_t MaxSize> +class max_size_allocator : public Allocator { + public: + using typename Allocator::value_type; + size_t max_size() const noexcept { return MaxSize; } + value_type* allocate(size_t n) { + if (n > max_size()) { + throw std::length_error("size > max_size"); + } + return std::allocator_traits<Allocator>::allocate( + *static_cast<Allocator*>(this), n); + } + void deallocate(value_type* p, size_t n) { + std::allocator_traits<Allocator>::deallocate(*static_cast<Allocator*>(this), + p, n); + } +}; + +TEST(memory_buffer_test, max_size_allocator) { + // 160 = 128 + 32 + using test_allocator = max_size_allocator<std::allocator<char>, 160>; + basic_memory_buffer<char, 10, test_allocator> buffer; + buffer.resize(128); + // new_capacity = 128 + 128/2 = 192 > 160 + buffer.resize(160); // Shouldn't throw. +} + +TEST(memory_buffer_test, max_size_allocator_overflow) { + using test_allocator = max_size_allocator<std::allocator<char>, 160>; + basic_memory_buffer<char, 10, test_allocator> buffer; + EXPECT_THROW(buffer.resize(161), std::exception); +} + +TEST(format_test, escape) { + EXPECT_EQ("{", fmt::format("{{")); + EXPECT_EQ("before {", fmt::format("before {{")); + EXPECT_EQ("{ after", fmt::format("{{ after")); + EXPECT_EQ("before { after", fmt::format("before {{ after")); + + EXPECT_EQ("}", fmt::format("}}")); + EXPECT_EQ("before }", fmt::format("before }}")); + EXPECT_EQ("} after", fmt::format("}} after")); + EXPECT_EQ("before } after", fmt::format("before }} after")); + + EXPECT_EQ("{}", fmt::format("{{}}")); + EXPECT_EQ("{42}", fmt::format("{{{0}}}", 42)); +} + +TEST(format_test, unmatched_braces) { + EXPECT_THROW_MSG((void)fmt::format(runtime("{")), format_error, + "invalid format string"); + EXPECT_THROW_MSG((void)fmt::format(runtime("}")), format_error, + "unmatched '}' in format string"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0{}")), format_error, + "invalid format string"); +} + +TEST(format_test, no_args) { EXPECT_EQ("test", fmt::format("test")); } + +TEST(format_test, args_in_different_positions) { + EXPECT_EQ("42", fmt::format("{0}", 42)); + EXPECT_EQ("before 42", fmt::format("before {0}", 42)); + EXPECT_EQ("42 after", fmt::format("{0} after", 42)); + EXPECT_EQ("before 42 after", fmt::format("before {0} after", 42)); + EXPECT_EQ("answer = 42", fmt::format("{0} = {1}", "answer", 42)); + EXPECT_EQ("42 is the answer", fmt::format("{1} is the {0}", "answer", 42)); + EXPECT_EQ("abracadabra", fmt::format("{0}{1}{0}", "abra", "cad")); +} + +TEST(format_test, arg_errors) { + EXPECT_THROW_MSG((void)fmt::format(runtime("{")), format_error, + "invalid format string"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{?}")), format_error, + "invalid format string"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0")), format_error, + "invalid format string"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0}")), format_error, + "argument not found"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{00}"), 42), format_error, + "invalid format string"); + + char format_str[buffer_size]; + safe_sprintf(format_str, "{%u", INT_MAX); + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error, + "invalid format string"); + safe_sprintf(format_str, "{%u}", INT_MAX); + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error, + "argument not found"); + + safe_sprintf(format_str, "{%u", INT_MAX + 1u); + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error, + "invalid format string"); + safe_sprintf(format_str, "{%u}", INT_MAX + 1u); + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error, + "argument not found"); +} + +template <int N> struct test_format { + template <typename... T> + static std::string format(fmt::string_view fmt, const T&... args) { + return test_format<N - 1>::format(fmt, N - 1, args...); + } +}; + +template <> struct test_format<0> { + template <typename... T> + static std::string format(fmt::string_view fmt, const T&... args) { + return fmt::format(runtime(fmt), args...); + } +}; + +TEST(format_test, many_args) { + EXPECT_EQ("19", test_format<20>::format("{19}")); + EXPECT_THROW_MSG(test_format<20>::format("{20}"), format_error, + "argument not found"); + EXPECT_THROW_MSG(test_format<21>::format("{21}"), format_error, + "argument not found"); + using fmt::detail::max_packed_args; + std::string format_str = fmt::format("{{{}}}", max_packed_args + 1); + EXPECT_THROW_MSG(test_format<max_packed_args>::format(format_str), + format_error, "argument not found"); +} + +TEST(format_test, named_arg) { + EXPECT_EQ("1/a/A", fmt::format("{_1}/{a_}/{A_}", fmt::arg("a_", 'a'), + fmt::arg("A_", "A"), fmt::arg("_1", 1))); + EXPECT_EQ(" -42", fmt::format("{0:{width}}", -42, fmt::arg("width", 4))); + EXPECT_EQ("st", + fmt::format("{0:.{precision}}", "str", fmt::arg("precision", 2))); + EXPECT_EQ("1 2", fmt::format("{} {two}", 1, fmt::arg("two", 2))); + EXPECT_EQ("42", + fmt::format("{c}", fmt::arg("a", 0), fmt::arg("b", 0), + fmt::arg("c", 42), fmt::arg("d", 0), fmt::arg("e", 0), + fmt::arg("f", 0), fmt::arg("g", 0), fmt::arg("h", 0), + fmt::arg("i", 0), fmt::arg("j", 0), fmt::arg("k", 0), + fmt::arg("l", 0), fmt::arg("m", 0), fmt::arg("n", 0), + fmt::arg("o", 0), fmt::arg("p", 0))); + EXPECT_THROW_MSG((void)fmt::format(runtime("{a}")), format_error, + "argument not found"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{a}"), 42), format_error, + "argument not found"); +} + +TEST(format_test, auto_arg_index) { + EXPECT_EQ("abc", fmt::format("{}{}{}", 'a', 'b', 'c')); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0}{}"), 'a', 'b'), format_error, + "cannot switch from manual to automatic argument indexing"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{}{0}"), 'a', 'b'), format_error, + "cannot switch from automatic to manual argument indexing"); + EXPECT_EQ("1.2", fmt::format("{:.{}}", 1.2345, 2)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0}:.{}"), 1.2345, 2), + format_error, + "cannot switch from manual to automatic argument indexing"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:.{0}}"), 1.2345, 2), + format_error, + "cannot switch from automatic to manual argument indexing"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{}")), format_error, + "argument not found"); +} + +TEST(format_test, empty_specs) { EXPECT_EQ("42", fmt::format("{0:}", 42)); } + +TEST(format_test, left_align) { + EXPECT_EQ("42 ", fmt::format("{0:<4}", 42)); + EXPECT_EQ("42 ", fmt::format("{0:<4o}", 042)); + EXPECT_EQ("42 ", fmt::format("{0:<4x}", 0x42)); + EXPECT_EQ("-42 ", fmt::format("{0:<5}", -42)); + EXPECT_EQ("42 ", fmt::format("{0:<5}", 42u)); + EXPECT_EQ("-42 ", fmt::format("{0:<5}", -42l)); + EXPECT_EQ("42 ", fmt::format("{0:<5}", 42ul)); + EXPECT_EQ("-42 ", fmt::format("{0:<5}", -42ll)); + EXPECT_EQ("42 ", fmt::format("{0:<5}", 42ull)); + EXPECT_EQ("-42 ", fmt::format("{0:<5}", -42.0)); + EXPECT_EQ("-42 ", fmt::format("{0:<5}", -42.0l)); + EXPECT_EQ("c ", fmt::format("{0:<5}", 'c')); + EXPECT_EQ("abc ", fmt::format("{0:<5}", "abc")); + EXPECT_EQ("0xface ", fmt::format("{0:<8}", reinterpret_cast<void*>(0xface))); +} + +TEST(format_test, right_align) { + EXPECT_EQ(" 42", fmt::format("{0:>4}", 42)); + EXPECT_EQ(" 42", fmt::format("{0:>4o}", 042)); + EXPECT_EQ(" 42", fmt::format("{0:>4x}", 0x42)); + EXPECT_EQ(" -42", fmt::format("{0:>5}", -42)); + EXPECT_EQ(" 42", fmt::format("{0:>5}", 42u)); + EXPECT_EQ(" -42", fmt::format("{0:>5}", -42l)); + EXPECT_EQ(" 42", fmt::format("{0:>5}", 42ul)); + EXPECT_EQ(" -42", fmt::format("{0:>5}", -42ll)); + EXPECT_EQ(" 42", fmt::format("{0:>5}", 42ull)); + EXPECT_EQ(" -42", fmt::format("{0:>5}", -42.0)); + EXPECT_EQ(" -42", fmt::format("{0:>5}", -42.0l)); + EXPECT_EQ(" c", fmt::format("{0:>5}", 'c')); + EXPECT_EQ(" abc", fmt::format("{0:>5}", "abc")); + EXPECT_EQ(" 0xface", fmt::format("{0:>8}", reinterpret_cast<void*>(0xface))); +} + +TEST(format_test, center_align) { + EXPECT_EQ(" 42 ", fmt::format("{0:^5}", 42)); + EXPECT_EQ(" 42 ", fmt::format("{0:^5o}", 042)); + EXPECT_EQ(" 42 ", fmt::format("{0:^5x}", 0x42)); + EXPECT_EQ(" -42 ", fmt::format("{0:^5}", -42)); + EXPECT_EQ(" 42 ", fmt::format("{0:^5}", 42u)); + EXPECT_EQ(" -42 ", fmt::format("{0:^5}", -42l)); + EXPECT_EQ(" 42 ", fmt::format("{0:^5}", 42ul)); + EXPECT_EQ(" -42 ", fmt::format("{0:^5}", -42ll)); + EXPECT_EQ(" 42 ", fmt::format("{0:^5}", 42ull)); + EXPECT_EQ(" -42 ", fmt::format("{0:^5}", -42.0)); + EXPECT_EQ(" -42 ", fmt::format("{0:^5}", -42.0l)); + EXPECT_EQ(" c ", fmt::format("{0:^5}", 'c')); + EXPECT_EQ(" abc ", fmt::format("{0:^6}", "abc")); + EXPECT_EQ(" 0xface ", fmt::format("{0:^8}", reinterpret_cast<void*>(0xface))); +} + +TEST(format_test, fill) { + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{<5}"), 'c'), format_error, + "invalid fill character '{'"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{<5}}"), 'c'), format_error, + "invalid fill character '{'"); + EXPECT_EQ("**42", fmt::format("{0:*>4}", 42)); + EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42)); + EXPECT_EQ("***42", fmt::format("{0:*>5}", 42u)); + EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42l)); + EXPECT_EQ("***42", fmt::format("{0:*>5}", 42ul)); + EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42ll)); + EXPECT_EQ("***42", fmt::format("{0:*>5}", 42ull)); + EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42.0)); + EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42.0l)); + EXPECT_EQ("c****", fmt::format("{0:*<5}", 'c')); + EXPECT_EQ("abc**", fmt::format("{0:*<5}", "abc")); + EXPECT_EQ("**0xface", + fmt::format("{0:*>8}", reinterpret_cast<void*>(0xface))); + EXPECT_EQ("foo=", fmt::format("{:}=", "foo")); + EXPECT_EQ(std::string("\0\0\0*", 4), + fmt::format(string_view("{:\0>4}", 6), '*')); + EXPECT_EQ("жж42", fmt::format("{0:ж>4}", 42)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:\x80\x80\x80\x80\x80>}"), 0), + format_error, "invalid type specifier"); +} + +TEST(format_test, plus_sign) { + EXPECT_EQ("+42", fmt::format("{0:+}", 42)); + EXPECT_EQ("-42", fmt::format("{0:+}", -42)); + EXPECT_EQ("+42", fmt::format("{0:+}", 42)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42u), format_error, + "format specifier requires signed argument"); + EXPECT_EQ("+42", fmt::format("{0:+}", 42l)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ul), format_error, + "format specifier requires signed argument"); + EXPECT_EQ("+42", fmt::format("{0:+}", 42ll)); +#if FMT_USE_INT128 + EXPECT_EQ("+42", fmt::format("{0:+}", __int128_t(42))); +#endif + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ull), format_error, + "format specifier requires signed argument"); + EXPECT_EQ("+42", fmt::format("{0:+}", 42.0)); + EXPECT_EQ("+42", fmt::format("{0:+}", 42.0l)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+"), 'c'), format_error, + "missing '}' in format string"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 'c'), format_error, + "invalid format specifier for char"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), "abc"), format_error, + "format specifier requires numeric argument"); + EXPECT_THROW_MSG( + (void)fmt::format(runtime("{0:+}"), reinterpret_cast<void*>(0x42)), + format_error, "format specifier requires numeric argument"); +} + +TEST(format_test, minus_sign) { + EXPECT_EQ("42", fmt::format("{0:-}", 42)); + EXPECT_EQ("-42", fmt::format("{0:-}", -42)); + EXPECT_EQ("42", fmt::format("{0:-}", 42)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42u), format_error, + "format specifier requires signed argument"); + EXPECT_EQ("42", fmt::format("{0:-}", 42l)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42ul), format_error, + "format specifier requires signed argument"); + EXPECT_EQ("42", fmt::format("{0:-}", 42ll)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42ull), format_error, + "format specifier requires signed argument"); + EXPECT_EQ("42", fmt::format("{0:-}", 42.0)); + EXPECT_EQ("42", fmt::format("{0:-}", 42.0l)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-"), 'c'), format_error, + "missing '}' in format string"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 'c'), format_error, + "invalid format specifier for char"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), "abc"), format_error, + "format specifier requires numeric argument"); + EXPECT_THROW_MSG( + (void)fmt::format(runtime("{0:-}"), reinterpret_cast<void*>(0x42)), + format_error, "format specifier requires numeric argument"); +} + +TEST(format_test, space_sign) { + EXPECT_EQ(" 42", fmt::format("{0: }", 42)); + EXPECT_EQ("-42", fmt::format("{0: }", -42)); + EXPECT_EQ(" 42", fmt::format("{0: }", 42)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42u), format_error, + "format specifier requires signed argument"); + EXPECT_EQ(" 42", fmt::format("{0: }", 42l)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42ul), format_error, + "format specifier requires signed argument"); + EXPECT_EQ(" 42", fmt::format("{0: }", 42ll)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42ull), format_error, + "format specifier requires signed argument"); + EXPECT_EQ(" 42", fmt::format("{0: }", 42.0)); + EXPECT_EQ(" 42", fmt::format("{0: }", 42.0l)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0: "), 'c'), format_error, + "missing '}' in format string"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 'c'), format_error, + "invalid format specifier for char"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), "abc"), format_error, + "format specifier requires numeric argument"); + EXPECT_THROW_MSG( + (void)fmt::format(runtime("{0: }"), reinterpret_cast<void*>(0x42)), + format_error, "format specifier requires numeric argument"); +} + +TEST(format_test, hash_flag) { + EXPECT_EQ("42", fmt::format("{0:#}", 42)); + EXPECT_EQ("-42", fmt::format("{0:#}", -42)); + EXPECT_EQ("0b101010", fmt::format("{0:#b}", 42)); + EXPECT_EQ("0B101010", fmt::format("{0:#B}", 42)); + EXPECT_EQ("-0b101010", fmt::format("{0:#b}", -42)); + EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42)); + EXPECT_EQ("0X42", fmt::format("{0:#X}", 0x42)); + EXPECT_EQ("-0x42", fmt::format("{0:#x}", -0x42)); + EXPECT_EQ("0", fmt::format("{0:#o}", 0)); + EXPECT_EQ("042", fmt::format("{0:#o}", 042)); + EXPECT_EQ("-042", fmt::format("{0:#o}", -042)); + EXPECT_EQ("42", fmt::format("{0:#}", 42u)); + EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42u)); + EXPECT_EQ("042", fmt::format("{0:#o}", 042u)); + + EXPECT_EQ("-42", fmt::format("{0:#}", -42l)); + EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42l)); + EXPECT_EQ("-0x42", fmt::format("{0:#x}", -0x42l)); + EXPECT_EQ("042", fmt::format("{0:#o}", 042l)); + EXPECT_EQ("-042", fmt::format("{0:#o}", -042l)); + EXPECT_EQ("42", fmt::format("{0:#}", 42ul)); + EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42ul)); + EXPECT_EQ("042", fmt::format("{0:#o}", 042ul)); + + EXPECT_EQ("-42", fmt::format("{0:#}", -42ll)); + EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42ll)); + EXPECT_EQ("-0x42", fmt::format("{0:#x}", -0x42ll)); + EXPECT_EQ("042", fmt::format("{0:#o}", 042ll)); + EXPECT_EQ("-042", fmt::format("{0:#o}", -042ll)); + EXPECT_EQ("42", fmt::format("{0:#}", 42ull)); + EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42ull)); + EXPECT_EQ("042", fmt::format("{0:#o}", 042ull)); + + EXPECT_EQ("-42.0", fmt::format("{0:#}", -42.0)); + EXPECT_EQ("-42.0", fmt::format("{0:#}", -42.0l)); + EXPECT_EQ("4.e+01", fmt::format("{:#.0e}", 42.0)); + EXPECT_EQ("0.", fmt::format("{:#.0f}", 0.01)); + EXPECT_EQ("0.50", fmt::format("{:#.2g}", 0.5)); + EXPECT_EQ("0.", fmt::format("{:#.0f}", 0.5)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#"), 'c'), format_error, + "missing '}' in format string"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), 'c'), format_error, + "invalid format specifier for char"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), "abc"), format_error, + "format specifier requires numeric argument"); + EXPECT_THROW_MSG( + (void)fmt::format(runtime("{0:#}"), reinterpret_cast<void*>(0x42)), + format_error, "format specifier requires numeric argument"); +} + +TEST(format_test, zero_flag) { + EXPECT_EQ("42", fmt::format("{0:0}", 42)); + EXPECT_EQ("-0042", fmt::format("{0:05}", -42)); + EXPECT_EQ("00042", fmt::format("{0:05}", 42u)); + EXPECT_EQ("-0042", fmt::format("{0:05}", -42l)); + EXPECT_EQ("00042", fmt::format("{0:05}", 42ul)); + EXPECT_EQ("-0042", fmt::format("{0:05}", -42ll)); + EXPECT_EQ("00042", fmt::format("{0:05}", 42ull)); + EXPECT_EQ("-000042", fmt::format("{0:07}", -42.0)); + EXPECT_EQ("-000042", fmt::format("{0:07}", -42.0l)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:0"), 'c'), format_error, + "missing '}' in format string"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), 'c'), format_error, + "invalid format specifier for char"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), "abc"), format_error, + "format specifier requires numeric argument"); + EXPECT_THROW_MSG( + (void)fmt::format(runtime("{0:05}"), reinterpret_cast<void*>(0x42)), + format_error, "format specifier requires numeric argument"); +} + +TEST(format_test, width) { + char format_str[buffer_size]; + safe_sprintf(format_str, "{0:%u", UINT_MAX); + increment(format_str + 3); + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, + "number is too big"); + size_t size = std::strlen(format_str); + format_str[size] = '}'; + format_str[size + 1] = 0; + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, + "number is too big"); + + safe_sprintf(format_str, "{0:%u", INT_MAX + 1u); + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, + "number is too big"); + safe_sprintf(format_str, "{0:%u}", INT_MAX + 1u); + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, + "number is too big"); + EXPECT_EQ(" -42", fmt::format("{0:4}", -42)); + EXPECT_EQ(" 42", fmt::format("{0:5}", 42u)); + EXPECT_EQ(" -42", fmt::format("{0:6}", -42l)); + EXPECT_EQ(" 42", fmt::format("{0:7}", 42ul)); + EXPECT_EQ(" -42", fmt::format("{0:6}", -42ll)); + EXPECT_EQ(" 42", fmt::format("{0:7}", 42ull)); + EXPECT_EQ(" -1.23", fmt::format("{0:8}", -1.23)); + EXPECT_EQ(" -1.23", fmt::format("{0:9}", -1.23l)); + EXPECT_EQ(" 0xcafe", + fmt::format("{0:10}", reinterpret_cast<void*>(0xcafe))); + EXPECT_EQ("x ", fmt::format("{0:11}", 'x')); + EXPECT_EQ("str ", fmt::format("{0:12}", "str")); + EXPECT_EQ(fmt::format("{:*^6}", "🤡"), "**🤡**"); + EXPECT_EQ(fmt::format("{:*^8}", "你好"), "**你好**"); + EXPECT_EQ(fmt::format("{:#6}", 42.0), " 42.0"); + EXPECT_EQ(fmt::format("{:6c}", static_cast<int>('x')), "x "); + EXPECT_EQ(fmt::format("{:>06.0f}", 0.00884311), "000000"); +} + +TEST(format_test, runtime_width) { + char format_str[buffer_size]; + safe_sprintf(format_str, "{0:{%u", UINT_MAX); + increment(format_str + 4); + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, + "invalid format string"); + size_t size = std::strlen(format_str); + format_str[size] = '}'; + format_str[size + 1] = 0; + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, + "argument not found"); + format_str[size + 1] = '}'; + format_str[size + 2] = 0; + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, + "argument not found"); + + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{"), 0), format_error, + "invalid format string"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{}"), 0), format_error, + "cannot switch from manual to automatic argument indexing"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{?}}"), 0), format_error, + "invalid format string"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0), format_error, + "argument not found"); + + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{0:}}"), 0), format_error, + "invalid format string"); + + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, -1), format_error, + "negative width"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1u)), + format_error, "number is too big"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, -1l), format_error, + "negative width"); + if (fmt::detail::const_check(sizeof(long) > sizeof(int))) { + long value = INT_MAX; + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (value + 1)), + format_error, "number is too big"); + } + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1ul)), + format_error, "number is too big"); + + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, '0'), format_error, + "width is not integer"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, 0.0), format_error, + "width is not integer"); + + EXPECT_EQ(" -42", fmt::format("{0:{1}}", -42, 4)); + EXPECT_EQ(" 42", fmt::format("{0:{1}}", 42u, 5)); + EXPECT_EQ(" -42", fmt::format("{0:{1}}", -42l, 6)); + EXPECT_EQ(" 42", fmt::format("{0:{1}}", 42ul, 7)); + EXPECT_EQ(" -42", fmt::format("{0:{1}}", -42ll, 6)); + EXPECT_EQ(" 42", fmt::format("{0:{1}}", 42ull, 7)); + EXPECT_EQ(" -1.23", fmt::format("{0:{1}}", -1.23, 8)); + EXPECT_EQ(" -1.23", fmt::format("{0:{1}}", -1.23l, 9)); + EXPECT_EQ(" 0xcafe", + fmt::format("{0:{1}}", reinterpret_cast<void*>(0xcafe), 10)); + EXPECT_EQ("x ", fmt::format("{0:{1}}", 'x', 11)); + EXPECT_EQ("str ", fmt::format("{0:{1}}", "str", 12)); + EXPECT_EQ(fmt::format("{:{}}", 42, short(4)), " 42"); +} + +TEST(format_test, precision) { + char format_str[buffer_size]; + safe_sprintf(format_str, "{0:.%u", UINT_MAX); + increment(format_str + 4); + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, + "number is too big"); + size_t size = std::strlen(format_str); + format_str[size] = '}'; + format_str[size + 1] = 0; + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, + "number is too big"); + + safe_sprintf(format_str, "{0:.%u", INT_MAX + 1u); + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, + "number is too big"); + safe_sprintf(format_str, "{0:.%u}", INT_MAX + 1u); + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, + "number is too big"); + + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:."), 0), format_error, + "missing precision specifier"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.}"), 0), format_error, + "missing precision specifier"); + + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2"), 0), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42u), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42u), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42l), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42l), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42ul), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42ul), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42ll), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42ll), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42ull), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42ull), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:3.0}"), 'x'), format_error, + "precision not allowed for this argument type"); + EXPECT_EQ("1.2", fmt::format("{0:.2}", 1.2345)); + EXPECT_EQ("1.2", fmt::format("{0:.2}", 1.2345l)); + EXPECT_EQ("1.2e+56", fmt::format("{:.2}", 1.234e56)); + EXPECT_EQ("1.1", fmt::format("{0:.3}", 1.1)); + EXPECT_EQ("1e+00", fmt::format("{:.0e}", 1.0L)); + EXPECT_EQ(" 0.0e+00", fmt::format("{:9.1e}", 0.0)); + EXPECT_EQ( + fmt::format("{:.494}", 4.9406564584124654E-324), + "4.9406564584124654417656879286822137236505980261432476442558568250067550" + "727020875186529983636163599237979656469544571773092665671035593979639877" + "479601078187812630071319031140452784581716784898210368871863605699873072" + "305000638740915356498438731247339727316961514003171538539807412623856559" + "117102665855668676818703956031062493194527159149245532930545654440112748" + "012970999954193198940908041656332452475714786901472678015935523861155013" + "480352649347201937902681071074917033322268447533357208324319361e-324"); + EXPECT_EQ( + fmt::format("{:.1074f}", 1.1125369292536e-308), + "0.0000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000111253692925360019747947051741965785554081512200979" + "355021686109411883779182127659725163430929750364498219730822952552570601" + "152163505899912777129583674906301179059298598412303893909188340988729019" + "014361467448914817838555156840459458527907308695109202499990850735085304" + "478476991912072201449236975063640913461919914396877093174125167509869762" + "482369631100360266123742648159508919592746619553246586039571522788247697" + "156360766271842991667238355464496455107749716934387136380536472531224398" + "559833794807213172371254492216255558078524900147957309382830827524104234" + "530961756787819847850302379672357738807808384667004752163416921762619527" + "462847642037420991432005657440259928195996762610375541867198059294212446" + "81962777939941034720757232455434770912461317493580281734466552734375"); + + std::string outputs[] = { + "-0X1.41FE3FFE71C9E000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000P+127", + "-0XA.0FF1FFF38E4F0000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000P+124"}; + EXPECT_THAT(outputs, + testing::Contains(fmt::format("{:.838A}", -2.14001164E+38))); + + if (std::numeric_limits<long double>::digits == 64) { + auto ld = (std::numeric_limits<long double>::min)(); + EXPECT_EQ(fmt::format("{:.0}", ld), "3e-4932"); + EXPECT_EQ( + fmt::format("{:0g}", std::numeric_limits<long double>::denorm_min()), + "3.6452e-4951"); + } + + EXPECT_EQ("123.", fmt::format("{:#.0f}", 123.0)); + EXPECT_EQ("1.23", fmt::format("{:.02f}", 1.234)); + EXPECT_EQ("0.001", fmt::format("{:.1g}", 0.001)); + EXPECT_EQ("1019666400", fmt::format("{}", 1019666432.0f)); + EXPECT_EQ("1e+01", fmt::format("{:.0e}", 9.5)); + EXPECT_EQ("1.0e-34", fmt::format("{:.1e}", 1e-34)); + + EXPECT_THROW_MSG( + (void)fmt::format(runtime("{0:.2}"), reinterpret_cast<void*>(0xcafe)), + format_error, "precision not allowed for this argument type"); + EXPECT_THROW_MSG( + (void)fmt::format(runtime("{0:.2f}"), reinterpret_cast<void*>(0xcafe)), + format_error, "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:.{}e}"), 42.0, + fmt::detail::max_value<int>()), + format_error, "number is too big"); + EXPECT_THROW_MSG( + (void)fmt::format("{:.2147483646f}", -2.2121295195081227E+304), + format_error, "number is too big"); + + EXPECT_EQ("st", fmt::format("{0:.2}", "str")); + EXPECT_EQ("вожык", fmt::format("{0:.5}", "вожыкі")); +} + +TEST(format_test, runtime_precision) { + char format_str[buffer_size]; + safe_sprintf(format_str, "{0:.{%u", UINT_MAX); + increment(format_str + 5); + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, + "invalid format string"); + size_t size = std::strlen(format_str); + format_str[size] = '}'; + format_str[size + 1] = 0; + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, + "argument not found"); + format_str[size + 1] = '}'; + format_str[size + 2] = 0; + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, + "argument not found"); + + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{"), 0), format_error, + "invalid format string"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{}"), 0), format_error, + "cannot switch from manual to automatic argument indexing"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{?}}"), 0), format_error, + "invalid format string"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}"), 0, 0), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0), format_error, + "argument not found"); + + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{0:}}"), 0), format_error, + "invalid format string"); + + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, -1), format_error, + "negative precision"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, (INT_MAX + 1u)), + format_error, "number is too big"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, -1l), format_error, + "negative precision"); + if (fmt::detail::const_check(sizeof(long) > sizeof(int))) { + long value = INT_MAX; + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, (value + 1)), + format_error, "number is too big"); + } + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, (INT_MAX + 1ul)), + format_error, "number is too big"); + + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, '0'), format_error, + "precision is not integer"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, 0.0), format_error, + "precision is not integer"); + + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42, 2), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42, 2), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42u, 2), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42u, 2), + format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42l, 2), format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42l, 2), + format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42ul, 2), + format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42ul, 2), + format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42ll, 2), + format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42ll, 2), + format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42ull, 2), + format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42ull, 2), + format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:3.{1}}"), 'x', 0), + format_error, + "precision not allowed for this argument type"); + EXPECT_EQ("1.2", fmt::format("{0:.{1}}", 1.2345, 2)); + EXPECT_EQ("1.2", fmt::format("{1:.{0}}", 2, 1.2345l)); + + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), + reinterpret_cast<void*>(0xcafe), 2), + format_error, + "precision not allowed for this argument type"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), + reinterpret_cast<void*>(0xcafe), 2), + format_error, + "precision not allowed for this argument type"); + + EXPECT_EQ("st", fmt::format("{0:.{1}}", "str", 2)); +} + +TEST(format_test, format_bool) { + EXPECT_EQ("true", fmt::format("{}", true)); + EXPECT_EQ("false", fmt::format("{}", false)); + EXPECT_EQ("1", fmt::format("{:d}", true)); + EXPECT_EQ("true ", fmt::format("{:5}", true)); + EXPECT_EQ("true", fmt::format("{:s}", true)); + EXPECT_EQ("false", fmt::format("{:s}", false)); + EXPECT_EQ("false ", fmt::format("{:6s}", false)); +} + +TEST(format_test, format_short) { + short s = 42; + EXPECT_EQ("42", fmt::format("{0:d}", s)); + unsigned short us = 42; + EXPECT_EQ("42", fmt::format("{0:d}", us)); +} + +template <typename T> +void check_unknown_types(const T& value, const char* types, const char*) { + char format_str[buffer_size]; + const char* special = ".0123456789L?}"; + for (int i = CHAR_MIN; i <= CHAR_MAX; ++i) { + char c = static_cast<char>(i); + if (std::strchr(types, c) || std::strchr(special, c) || !c) continue; + safe_sprintf(format_str, "{0:10%c}", c); + const char* message = "invalid type specifier"; + EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), value), + format_error, message) + << format_str << " " << message; + } +} + +TEST(format_test, format_int) { + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:v"), 42), format_error, + "invalid type specifier"); + check_unknown_types(42, "bBdoxXnLc", "integer"); + EXPECT_EQ("x", fmt::format("{:c}", static_cast<int>('x'))); +} + +TEST(format_test, format_bin) { + EXPECT_EQ("0", fmt::format("{0:b}", 0)); + EXPECT_EQ("101010", fmt::format("{0:b}", 42)); + EXPECT_EQ("101010", fmt::format("{0:b}", 42u)); + EXPECT_EQ("-101010", fmt::format("{0:b}", -42)); + EXPECT_EQ("11000000111001", fmt::format("{0:b}", 12345)); + EXPECT_EQ("10010001101000101011001111000", fmt::format("{0:b}", 0x12345678)); + EXPECT_EQ("10010000101010111100110111101111", + fmt::format("{0:b}", 0x90ABCDEF)); + EXPECT_EQ("11111111111111111111111111111111", + fmt::format("{0:b}", max_value<uint32_t>())); +} + +#if FMT_USE_INT128 +constexpr auto int128_max = static_cast<__int128_t>( + (static_cast<__uint128_t>(1) << ((__SIZEOF_INT128__ * CHAR_BIT) - 1)) - 1); +constexpr auto int128_min = -int128_max - 1; + +constexpr auto uint128_max = ~static_cast<__uint128_t>(0); +#endif + +TEST(format_test, format_dec) { + EXPECT_EQ("0", fmt::format("{0}", 0)); + EXPECT_EQ("42", fmt::format("{0}", 42)); + EXPECT_EQ("42", fmt::format("{0:d}", 42)); + EXPECT_EQ("42", fmt::format("{0}", 42u)); + EXPECT_EQ("-42", fmt::format("{0}", -42)); + EXPECT_EQ("12345", fmt::format("{0}", 12345)); + EXPECT_EQ("67890", fmt::format("{0}", 67890)); +#if FMT_USE_INT128 + EXPECT_EQ("0", fmt::format("{0}", static_cast<__int128_t>(0))); + EXPECT_EQ("0", fmt::format("{0}", static_cast<__uint128_t>(0))); + EXPECT_EQ("9223372036854775808", + fmt::format("{0}", static_cast<__int128_t>(INT64_MAX) + 1)); + EXPECT_EQ("-9223372036854775809", + fmt::format("{0}", static_cast<__int128_t>(INT64_MIN) - 1)); + EXPECT_EQ("18446744073709551616", + fmt::format("{0}", static_cast<__int128_t>(UINT64_MAX) + 1)); + EXPECT_EQ("170141183460469231731687303715884105727", + fmt::format("{0}", int128_max)); + EXPECT_EQ("-170141183460469231731687303715884105728", + fmt::format("{0}", int128_min)); + EXPECT_EQ("340282366920938463463374607431768211455", + fmt::format("{0}", uint128_max)); +#endif + + char buffer[buffer_size]; + safe_sprintf(buffer, "%d", INT_MIN); + EXPECT_EQ(buffer, fmt::format("{0}", INT_MIN)); + safe_sprintf(buffer, "%d", INT_MAX); + EXPECT_EQ(buffer, fmt::format("{0}", INT_MAX)); + safe_sprintf(buffer, "%u", UINT_MAX); + EXPECT_EQ(buffer, fmt::format("{0}", UINT_MAX)); + safe_sprintf(buffer, "%ld", 0 - static_cast<unsigned long>(LONG_MIN)); + EXPECT_EQ(buffer, fmt::format("{0}", LONG_MIN)); + safe_sprintf(buffer, "%ld", LONG_MAX); + EXPECT_EQ(buffer, fmt::format("{0}", LONG_MAX)); + safe_sprintf(buffer, "%lu", ULONG_MAX); + EXPECT_EQ(buffer, fmt::format("{0}", ULONG_MAX)); +} + +TEST(format_test, format_hex) { + EXPECT_EQ("0", fmt::format("{0:x}", 0)); + EXPECT_EQ("42", fmt::format("{0:x}", 0x42)); + EXPECT_EQ("42", fmt::format("{0:x}", 0x42u)); + EXPECT_EQ("-42", fmt::format("{0:x}", -0x42)); + EXPECT_EQ("12345678", fmt::format("{0:x}", 0x12345678)); + EXPECT_EQ("90abcdef", fmt::format("{0:x}", 0x90abcdef)); + EXPECT_EQ("12345678", fmt::format("{0:X}", 0x12345678)); + EXPECT_EQ("90ABCDEF", fmt::format("{0:X}", 0x90ABCDEF)); +#if FMT_USE_INT128 + EXPECT_EQ("0", fmt::format("{0:x}", static_cast<__int128_t>(0))); + EXPECT_EQ("0", fmt::format("{0:x}", static_cast<__uint128_t>(0))); + EXPECT_EQ("8000000000000000", + fmt::format("{0:x}", static_cast<__int128_t>(INT64_MAX) + 1)); + EXPECT_EQ("-8000000000000001", + fmt::format("{0:x}", static_cast<__int128_t>(INT64_MIN) - 1)); + EXPECT_EQ("10000000000000000", + fmt::format("{0:x}", static_cast<__int128_t>(UINT64_MAX) + 1)); + EXPECT_EQ("7fffffffffffffffffffffffffffffff", + fmt::format("{0:x}", int128_max)); + EXPECT_EQ("-80000000000000000000000000000000", + fmt::format("{0:x}", int128_min)); + EXPECT_EQ("ffffffffffffffffffffffffffffffff", + fmt::format("{0:x}", uint128_max)); +#endif + + char buffer[buffer_size]; + safe_sprintf(buffer, "-%x", 0 - static_cast<unsigned>(INT_MIN)); + EXPECT_EQ(buffer, fmt::format("{0:x}", INT_MIN)); + safe_sprintf(buffer, "%x", INT_MAX); + EXPECT_EQ(buffer, fmt::format("{0:x}", INT_MAX)); + safe_sprintf(buffer, "%x", UINT_MAX); + EXPECT_EQ(buffer, fmt::format("{0:x}", UINT_MAX)); + safe_sprintf(buffer, "-%lx", 0 - static_cast<unsigned long>(LONG_MIN)); + EXPECT_EQ(buffer, fmt::format("{0:x}", LONG_MIN)); + safe_sprintf(buffer, "%lx", LONG_MAX); + EXPECT_EQ(buffer, fmt::format("{0:x}", LONG_MAX)); + safe_sprintf(buffer, "%lx", ULONG_MAX); + EXPECT_EQ(buffer, fmt::format("{0:x}", ULONG_MAX)); +} + +TEST(format_test, format_oct) { + EXPECT_EQ("0", fmt::format("{0:o}", 0)); + EXPECT_EQ("42", fmt::format("{0:o}", 042)); + EXPECT_EQ("42", fmt::format("{0:o}", 042u)); + EXPECT_EQ("-42", fmt::format("{0:o}", -042)); + EXPECT_EQ("12345670", fmt::format("{0:o}", 012345670)); +#if FMT_USE_INT128 + EXPECT_EQ("0", fmt::format("{0:o}", static_cast<__int128_t>(0))); + EXPECT_EQ("0", fmt::format("{0:o}", static_cast<__uint128_t>(0))); + EXPECT_EQ("1000000000000000000000", + fmt::format("{0:o}", static_cast<__int128_t>(INT64_MAX) + 1)); + EXPECT_EQ("-1000000000000000000001", + fmt::format("{0:o}", static_cast<__int128_t>(INT64_MIN) - 1)); + EXPECT_EQ("2000000000000000000000", + fmt::format("{0:o}", static_cast<__int128_t>(UINT64_MAX) + 1)); + EXPECT_EQ("1777777777777777777777777777777777777777777", + fmt::format("{0:o}", int128_max)); + EXPECT_EQ("-2000000000000000000000000000000000000000000", + fmt::format("{0:o}", int128_min)); + EXPECT_EQ("3777777777777777777777777777777777777777777", + fmt::format("{0:o}", uint128_max)); +#endif + + char buffer[buffer_size]; + safe_sprintf(buffer, "-%o", 0 - static_cast<unsigned>(INT_MIN)); + EXPECT_EQ(buffer, fmt::format("{0:o}", INT_MIN)); + safe_sprintf(buffer, "%o", INT_MAX); + EXPECT_EQ(buffer, fmt::format("{0:o}", INT_MAX)); + safe_sprintf(buffer, "%o", UINT_MAX); + EXPECT_EQ(buffer, fmt::format("{0:o}", UINT_MAX)); + safe_sprintf(buffer, "-%lo", 0 - static_cast<unsigned long>(LONG_MIN)); + EXPECT_EQ(buffer, fmt::format("{0:o}", LONG_MIN)); + safe_sprintf(buffer, "%lo", LONG_MAX); + EXPECT_EQ(buffer, fmt::format("{0:o}", LONG_MAX)); + safe_sprintf(buffer, "%lo", ULONG_MAX); + EXPECT_EQ(buffer, fmt::format("{0:o}", ULONG_MAX)); +} + +TEST(format_test, format_int_locale) { + EXPECT_EQ("1234", fmt::format("{:L}", 1234)); +} + +TEST(format_test, format_float) { + EXPECT_EQ("0", fmt::format("{}", 0.0f)); + EXPECT_EQ("392.500000", fmt::format("{0:f}", 392.5f)); +} + +TEST(format_test, format_double) { + EXPECT_EQ(fmt::format("{}", 0.0), "0"); + check_unknown_types(1.2, "eEfFgGaAnL%", "double"); + EXPECT_EQ(fmt::format("{:}", 0.0), "0"); + EXPECT_EQ(fmt::format("{:f}", 0.0), "0.000000"); + EXPECT_EQ(fmt::format("{:g}", 0.0), "0"); + EXPECT_EQ(fmt::format("{:}", 392.65), "392.65"); + EXPECT_EQ(fmt::format("{:g}", 392.65), "392.65"); + EXPECT_EQ(fmt::format("{:G}", 392.65), "392.65"); + EXPECT_EQ(fmt::format("{:g}", 4.9014e6), "4.9014e+06"); + EXPECT_EQ(fmt::format("{:f}", 392.65), "392.650000"); + EXPECT_EQ(fmt::format("{:F}", 392.65), "392.650000"); + EXPECT_EQ(fmt::format("{:L}", 42.0), "42"); + EXPECT_EQ(fmt::format("{:24a}", 4.2), " 0x1.0cccccccccccdp+2"); + EXPECT_EQ(fmt::format("{:<24a}", 4.2), "0x1.0cccccccccccdp+2 "); + EXPECT_EQ(fmt::format("{0:e}", 392.65), "3.926500e+02"); + EXPECT_EQ(fmt::format("{0:E}", 392.65), "3.926500E+02"); + EXPECT_EQ(fmt::format("{0:+010.4g}", 392.65), "+0000392.6"); + char buffer[buffer_size]; + safe_sprintf(buffer, "%a", -42.0); + EXPECT_EQ(fmt::format("{:a}", -42.0), buffer); + safe_sprintf(buffer, "%A", -42.0); + EXPECT_EQ(fmt::format("{:A}", -42.0), buffer); + EXPECT_EQ(fmt::format("{:f}", 9223372036854775807.0), + "9223372036854775808.000000"); +} + +TEST(format_test, precision_rounding) { + EXPECT_EQ("0", fmt::format("{:.0f}", 0.0)); + EXPECT_EQ("0", fmt::format("{:.0f}", 0.01)); + EXPECT_EQ("0", fmt::format("{:.0f}", 0.1)); + EXPECT_EQ("0.000", fmt::format("{:.3f}", 0.00049)); + EXPECT_EQ("0.001", fmt::format("{:.3f}", 0.0005)); + EXPECT_EQ("0.001", fmt::format("{:.3f}", 0.00149)); + EXPECT_EQ("0.002", fmt::format("{:.3f}", 0.0015)); + EXPECT_EQ("1.000", fmt::format("{:.3f}", 0.9999)); + EXPECT_EQ("0.00123", fmt::format("{:.3}", 0.00123)); + EXPECT_EQ("0.1", fmt::format("{:.16g}", 0.1)); + EXPECT_EQ("1", fmt::format("{:.0}", 1.0)); + EXPECT_EQ("225.51575035152063720", + fmt::format("{:.17f}", 225.51575035152064)); + EXPECT_EQ("-761519619559038.2", fmt::format("{:.1f}", -761519619559038.2)); + EXPECT_EQ("1.9156918820264798e-56", + fmt::format("{}", 1.9156918820264798e-56)); + EXPECT_EQ("0.0000", fmt::format("{:.4f}", 7.2809479766055470e-15)); + + // Trigger a rounding error in Grisu by a specially chosen number. + EXPECT_EQ("3788512123356.985352", fmt::format("{:f}", 3788512123356.985352)); +} + +TEST(format_test, prettify_float) { + EXPECT_EQ("0.0001", fmt::format("{}", 1e-4)); + EXPECT_EQ("1e-05", fmt::format("{}", 1e-5)); + EXPECT_EQ("1000000000000000", fmt::format("{}", 1e15)); + EXPECT_EQ("1e+16", fmt::format("{}", 1e16)); + EXPECT_EQ("9.999e-05", fmt::format("{}", 9.999e-5)); + EXPECT_EQ("10000000000", fmt::format("{}", 1e10)); + EXPECT_EQ("100000000000", fmt::format("{}", 1e11)); + EXPECT_EQ("12340000000", fmt::format("{}", 1234e7)); + EXPECT_EQ("12.34", fmt::format("{}", 1234e-2)); + EXPECT_EQ("0.001234", fmt::format("{}", 1234e-6)); + EXPECT_EQ("0.1", fmt::format("{}", 0.1f)); + EXPECT_EQ("0.10000000149011612", fmt::format("{}", double(0.1f))); + EXPECT_EQ("1.3563156e-19", fmt::format("{}", 1.35631564e-19f)); +} + +TEST(format_test, format_nan) { + double nan = std::numeric_limits<double>::quiet_NaN(); + EXPECT_EQ("nan", fmt::format("{}", nan)); + EXPECT_EQ("+nan", fmt::format("{:+}", nan)); + EXPECT_EQ(" +nan", fmt::format("{:+06}", nan)); + EXPECT_EQ("+nan ", fmt::format("{:<+06}", nan)); + EXPECT_EQ(" +nan ", fmt::format("{:^+06}", nan)); + EXPECT_EQ(" +nan", fmt::format("{:>+06}", nan)); + if (std::signbit(-nan)) { + EXPECT_EQ("-nan", fmt::format("{}", -nan)); + EXPECT_EQ(" -nan", fmt::format("{:+06}", -nan)); + } else { + fmt::print("Warning: compiler doesn't handle negative NaN correctly"); + } + EXPECT_EQ(" nan", fmt::format("{: }", nan)); + EXPECT_EQ("NAN", fmt::format("{:F}", nan)); + EXPECT_EQ("nan ", fmt::format("{:<7}", nan)); + EXPECT_EQ(" nan ", fmt::format("{:^7}", nan)); + EXPECT_EQ(" nan", fmt::format("{:>7}", nan)); +} + +TEST(format_test, format_infinity) { + double inf = std::numeric_limits<double>::infinity(); + EXPECT_EQ("inf", fmt::format("{}", inf)); + EXPECT_EQ("+inf", fmt::format("{:+}", inf)); + EXPECT_EQ("-inf", fmt::format("{}", -inf)); + EXPECT_EQ(" +inf", fmt::format("{:+06}", inf)); + EXPECT_EQ(" -inf", fmt::format("{:+06}", -inf)); + EXPECT_EQ("+inf ", fmt::format("{:<+06}", inf)); + EXPECT_EQ(" +inf ", fmt::format("{:^+06}", inf)); + EXPECT_EQ(" +inf", fmt::format("{:>+06}", inf)); + EXPECT_EQ(" inf", fmt::format("{: }", inf)); + EXPECT_EQ("INF", fmt::format("{:F}", inf)); + EXPECT_EQ("inf ", fmt::format("{:<7}", inf)); + EXPECT_EQ(" inf ", fmt::format("{:^7}", inf)); + EXPECT_EQ(" inf", fmt::format("{:>7}", inf)); +} + +TEST(format_test, format_long_double) { + EXPECT_EQ("0", fmt::format("{0:}", 0.0l)); + EXPECT_EQ("0.000000", fmt::format("{0:f}", 0.0l)); + EXPECT_EQ("392.65", fmt::format("{0:}", 392.65l)); + EXPECT_EQ("392.65", fmt::format("{0:g}", 392.65l)); + EXPECT_EQ("392.65", fmt::format("{0:G}", 392.65l)); + EXPECT_EQ("392.650000", fmt::format("{0:f}", 392.65l)); + EXPECT_EQ("392.650000", fmt::format("{0:F}", 392.65l)); + char buffer[buffer_size]; + safe_sprintf(buffer, "%Le", 392.65l); + EXPECT_EQ(buffer, fmt::format("{0:e}", 392.65l)); + EXPECT_EQ("+0000392.6", fmt::format("{0:+010.4g}", 392.64l)); + safe_sprintf(buffer, "%La", 3.31l); + EXPECT_EQ(buffer, fmt::format("{:a}", 3.31l)); +} + +TEST(format_test, format_char) { + const char types[] = "cbBdoxX"; + check_unknown_types('a', types, "char"); + EXPECT_EQ("a", fmt::format("{0}", 'a')); + EXPECT_EQ("z", fmt::format("{0:c}", 'z')); + int n = 'x'; + for (const char* type = types + 1; *type; ++type) { + std::string format_str = fmt::format("{{:{}}}", *type); + EXPECT_EQ(fmt::format(runtime(format_str), n), + fmt::format(runtime(format_str), 'x')) + << format_str; + } + EXPECT_EQ(fmt::format("{:02X}", n), fmt::format("{:02X}", 'x')); + + EXPECT_EQ("\n", fmt::format("{}", '\n')); + EXPECT_EQ("'\\n'", fmt::format("{:?}", '\n')); +} + +TEST(format_test, format_volatile_char) { + volatile char c = 'x'; + EXPECT_EQ("x", fmt::format("{}", c)); +} + +TEST(format_test, format_unsigned_char) { + EXPECT_EQ("42", fmt::format("{}", static_cast<unsigned char>(42))); + EXPECT_EQ("42", fmt::format("{}", static_cast<uint8_t>(42))); +} + +TEST(format_test, format_cstring) { + check_unknown_types("test", "sp", "string"); + EXPECT_EQ("test", fmt::format("{0}", "test")); + EXPECT_EQ("test", fmt::format("{0:s}", "test")); + char nonconst[] = "nonconst"; + EXPECT_EQ("nonconst", fmt::format("{0}", nonconst)); + EXPECT_THROW_MSG( + (void)fmt::format(runtime("{0}"), static_cast<const char*>(nullptr)), + format_error, "string pointer is null"); +} + +void function_pointer_test(int, double, std::string) {} + +TEST(format_test, format_pointer) { + check_unknown_types(reinterpret_cast<void*>(0x1234), "p", "pointer"); + EXPECT_EQ("0x0", fmt::format("{0}", static_cast<void*>(nullptr))); + EXPECT_EQ("0x1234", fmt::format("{0}", reinterpret_cast<void*>(0x1234))); + EXPECT_EQ("0x1234", fmt::format("{0:p}", reinterpret_cast<void*>(0x1234))); + // On CHERI (or other fat-pointer) systems, the size of a pointer is greater + // than the size an integer that can hold a virtual address. There is no + // portable address-as-an-integer type (yet) in C++, so we use `size_t` as + // the closest equivalent for now. + EXPECT_EQ("0x" + std::string(sizeof(size_t) * CHAR_BIT / 4, 'f'), + fmt::format("{0}", reinterpret_cast<void*>(~uintptr_t()))); + EXPECT_EQ("0x1234", + fmt::format("{}", fmt::ptr(reinterpret_cast<int*>(0x1234)))); + std::unique_ptr<int> up(new int(1)); + EXPECT_EQ(fmt::format("{}", fmt::ptr(up.get())), + fmt::format("{}", fmt::ptr(up))); + std::shared_ptr<int> sp(new int(1)); + EXPECT_EQ(fmt::format("{}", fmt::ptr(sp.get())), + fmt::format("{}", fmt::ptr(sp))); + EXPECT_EQ(fmt::format("{}", fmt::detail::bit_cast<const void*>( + &function_pointer_test)), + fmt::format("{}", fmt::ptr(function_pointer_test))); + EXPECT_EQ("0x0", fmt::format("{}", nullptr)); +} + +TEST(format_test, write_uintptr_fallback) { + // Test that formatting a pointer by converting it to uint128_fallback works. + // This is needed to support systems without uintptr_t. + auto s = std::string(); + fmt::detail::write_ptr<char>( + std::back_inserter(s), + fmt::detail::bit_cast<fmt::detail::uint128_fallback>( + reinterpret_cast<void*>(0xface)), + nullptr); + EXPECT_EQ(s, "0xface"); +} + +enum class color { red, green, blue }; + +namespace test_ns { +enum class color { red, green, blue }; +using fmt::enums::format_as; +} // namespace test_ns + +TEST(format_test, format_enum_class) { + EXPECT_EQ(fmt::format("{}", fmt::underlying(color::red)), "0"); + EXPECT_EQ(fmt::format("{}", test_ns::color::red), "0"); +} + +TEST(format_test, format_string) { + EXPECT_EQ(fmt::format("{0}", std::string("test")), "test"); + EXPECT_EQ(fmt::format("{0}", std::string("test")), "test"); + EXPECT_EQ(fmt::format("{:?}", std::string("test")), "\"test\""); + EXPECT_EQ(fmt::format("{:*^10?}", std::string("test")), "**\"test\"**"); + EXPECT_EQ(fmt::format("{:?}", std::string("\test")), "\"\\test\""); + EXPECT_THROW((void)fmt::format(fmt::runtime("{:x}"), std::string("test")), + fmt::format_error); +} + +TEST(format_test, format_string_view) { + EXPECT_EQ("test", fmt::format("{}", string_view("test"))); + EXPECT_EQ("\"t\\nst\"", fmt::format("{:?}", string_view("t\nst"))); + EXPECT_EQ("", fmt::format("{}", string_view())); +} + +#ifdef FMT_USE_STRING_VIEW +struct string_viewable {}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter<string_viewable> : formatter<std::string_view> { + auto format(string_viewable, format_context& ctx) -> decltype(ctx.out()) { + return formatter<std::string_view>::format("foo", ctx); + } +}; +FMT_END_NAMESPACE + +TEST(format_test, format_std_string_view) { + EXPECT_EQ("test", fmt::format("{}", std::string_view("test"))); + EXPECT_EQ("foo", fmt::format("{}", string_viewable())); +} + +struct explicitly_convertible_to_std_string_view { + explicit operator std::string_view() const { return "foo"; } +}; + +template <> +struct fmt::formatter<explicitly_convertible_to_std_string_view> + : formatter<std::string_view> { + auto format(explicitly_convertible_to_std_string_view v, format_context& ctx) + -> decltype(ctx.out()) { + return format_to(ctx.out(), "'{}'", std::string_view(v)); + } +}; + +TEST(format_test, format_explicitly_convertible_to_std_string_view) { + EXPECT_EQ("'foo'", + fmt::format("{}", explicitly_convertible_to_std_string_view())); +} +#endif + +struct converible_to_anything { + template <typename T> operator T() const { return T(); } +}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter<converible_to_anything> { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + auto format(converible_to_anything, format_context& ctx) + -> decltype(ctx.out()) { + return format_to(ctx.out(), "foo"); + } +}; +FMT_END_NAMESPACE + +TEST(format_test, format_convertible_to_anything) { + EXPECT_EQ("foo", fmt::format("{}", converible_to_anything())); +} + +class Answer {}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter<date> { + template <typename ParseContext> + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + auto it = ctx.begin(); + if (it != ctx.end() && *it == 'd') ++it; + return it; + } + + auto format(const date& d, format_context& ctx) -> decltype(ctx.out()) { + format_to(ctx.out(), "{}-{}-{}", d.year(), d.month(), d.day()); + return ctx.out(); + } +}; + +template <> struct formatter<Answer> : formatter<int> { + template <typename FormatContext> + auto format(Answer, FormatContext& ctx) -> decltype(ctx.out()) { + return formatter<int>::format(42, ctx); + } +}; +FMT_END_NAMESPACE + +TEST(format_test, format_custom) { + EXPECT_THROW_MSG((void)fmt::format(runtime("{:s}"), date(2012, 12, 9)), + format_error, "unknown format specifier"); + EXPECT_EQ("42", fmt::format("{0}", Answer())); + EXPECT_EQ("0042", fmt::format("{:04}", Answer())); +} + +TEST(format_test, format_to_custom) { + char buf[10] = {}; + auto end = + &*fmt::format_to(fmt::detail::make_checked(buf, 10), "{}", Answer()); + EXPECT_EQ(end, buf + 2); + EXPECT_STREQ(buf, "42"); +} + +TEST(format_test, format_string_from_speed_test) { + EXPECT_EQ("1.2340000000:0042:+3.13:str:0x3e8:X:%", + fmt::format("{0:0.10f}:{1:04}:{2:+g}:{3}:{4}:{5}:%", 1.234, 42, + 3.13, "str", reinterpret_cast<void*>(1000), 'X')); +} + +TEST(format_test, format_examples) { + std::string message = fmt::format("The answer is {}", 42); + EXPECT_EQ("The answer is 42", message); + + EXPECT_EQ("42", fmt::format("{}", 42)); + + memory_buffer out; + format_to(std::back_inserter(out), "The answer is {}.", 42); + EXPECT_EQ("The answer is 42.", to_string(out)); + + const char* filename = "nonexistent"; + FILE* ftest = safe_fopen(filename, "r"); + if (ftest) fclose(ftest); + int error_code = errno; + EXPECT_TRUE(ftest == nullptr); + EXPECT_SYSTEM_ERROR( + { + FILE* f = safe_fopen(filename, "r"); + if (!f) + throw fmt::system_error(errno, "Cannot open file '{}'", filename); + fclose(f); + }, + error_code, "Cannot open file 'nonexistent'"); + + EXPECT_EQ("First, thou shalt count to three", + fmt::format("First, thou shalt count to {0}", "three")); + EXPECT_EQ("Bring me a shrubbery", fmt::format("Bring me a {}", "shrubbery")); + EXPECT_EQ("From 1 to 3", fmt::format("From {} to {}", 1, 3)); + + char buffer[buffer_size]; + safe_sprintf(buffer, "%03.2f", -1.2); + EXPECT_EQ(buffer, fmt::format("{:03.2f}", -1.2)); + + EXPECT_EQ("a, b, c", fmt::format("{0}, {1}, {2}", 'a', 'b', 'c')); + EXPECT_EQ("a, b, c", fmt::format("{}, {}, {}", 'a', 'b', 'c')); + EXPECT_EQ("c, b, a", fmt::format("{2}, {1}, {0}", 'a', 'b', 'c')); + EXPECT_EQ("abracadabra", fmt::format("{0}{1}{0}", "abra", "cad")); + + EXPECT_EQ("left aligned ", + fmt::format("{:<30}", "left aligned")); + EXPECT_EQ(" right aligned", + fmt::format("{:>30}", "right aligned")); + EXPECT_EQ(" centered ", + fmt::format("{:^30}", "centered")); + EXPECT_EQ("***********centered***********", + fmt::format("{:*^30}", "centered")); + + EXPECT_EQ("+3.140000; -3.140000", fmt::format("{:+f}; {:+f}", 3.14, -3.14)); + EXPECT_EQ(" 3.140000; -3.140000", fmt::format("{: f}; {: f}", 3.14, -3.14)); + EXPECT_EQ("3.140000; -3.140000", fmt::format("{:-f}; {:-f}", 3.14, -3.14)); + + EXPECT_EQ("int: 42; hex: 2a; oct: 52", + fmt::format("int: {0:d}; hex: {0:x}; oct: {0:o}", 42)); + EXPECT_EQ("int: 42; hex: 0x2a; oct: 052", + fmt::format("int: {0:d}; hex: {0:#x}; oct: {0:#o}", 42)); + + EXPECT_EQ("The answer is 42", fmt::format("The answer is {}", 42)); + EXPECT_THROW_MSG( + (void)fmt::format(runtime("The answer is {:d}"), "forty-two"), + format_error, "invalid type specifier"); + + EXPECT_WRITE( + stdout, fmt::print("{}", std::numeric_limits<double>::infinity()), "inf"); +} + +TEST(format_test, print) { + EXPECT_WRITE(stdout, fmt::print("Don't {}!", "panic"), "Don't panic!"); + EXPECT_WRITE(stderr, fmt::print(stderr, "Don't {}!", "panic"), + "Don't panic!"); +} + +TEST(format_test, variadic) { + EXPECT_EQ("abc1", fmt::format("{}c{}", "ab", 1)); +} + +TEST(format_test, dynamic) { + using ctx = fmt::format_context; + auto args = std::vector<fmt::basic_format_arg<ctx>>(); + args.emplace_back(fmt::detail::make_arg<ctx>(42)); + args.emplace_back(fmt::detail::make_arg<ctx>("abc1")); + args.emplace_back(fmt::detail::make_arg<ctx>(1.5f)); + + std::string result = fmt::vformat( + "{} and {} and {}", + fmt::format_args(args.data(), static_cast<int>(args.size()))); + + EXPECT_EQ("42 and abc1 and 1.5", result); +} + +TEST(format_test, bytes) { + auto s = fmt::format("{:10}", fmt::bytes("ёжик")); + EXPECT_EQ("ёжик ", s); + EXPECT_EQ(10, s.size()); +} + +TEST(format_test, group_digits_view) { + EXPECT_EQ(fmt::format("{}", fmt::group_digits(10000000)), "10,000,000"); + EXPECT_EQ(fmt::format("{:8}", fmt::group_digits(1000)), " 1,000"); +} + +enum test_enum { foo, bar }; +auto format_as(test_enum e) -> int { return e; } + +TEST(format_test, join) { + using fmt::join; + int v1[3] = {1, 2, 3}; + auto v2 = std::vector<float>(); + v2.push_back(1.2f); + v2.push_back(3.4f); + void* v3[2] = {&v1[0], &v1[1]}; + + EXPECT_EQ("(1, 2, 3)", fmt::format("({})", join(v1, v1 + 3, ", "))); + EXPECT_EQ("(1)", fmt::format("({})", join(v1, v1 + 1, ", "))); + EXPECT_EQ("()", fmt::format("({})", join(v1, v1, ", "))); + EXPECT_EQ("(001, 002, 003)", fmt::format("({:03})", join(v1, v1 + 3, ", "))); + EXPECT_EQ("(+01.20, +03.40)", + fmt::format("({:+06.2f})", join(v2.begin(), v2.end(), ", "))); + + EXPECT_EQ("1, 2, 3", fmt::format("{0:{1}}", join(v1, v1 + 3, ", "), 1)); + + EXPECT_EQ(fmt::format("{}, {}", v3[0], v3[1]), + fmt::format("{}", join(v3, v3 + 2, ", "))); + + EXPECT_EQ("(1, 2, 3)", fmt::format("({})", join(v1, ", "))); + EXPECT_EQ("(+01.20, +03.40)", fmt::format("({:+06.2f})", join(v2, ", "))); + + auto v4 = std::vector<test_enum>{foo, bar, foo}; + EXPECT_EQ("0 1 0", fmt::format("{}", join(v4, " "))); +} + +#ifdef __cpp_lib_byte +TEST(format_test, format_byte) { + using arg_mapper = fmt::detail::arg_mapper<fmt::format_context>; + EXPECT_EQ(arg_mapper().map(std::byte(42)), 42); + EXPECT_EQ(fmt::format("{}", std::byte(42)), "42"); +} + +TEST(format_test, join_bytes) { + auto v = std::vector<std::byte>{std::byte(1), std::byte(2), std::byte(3)}; + EXPECT_EQ(fmt::format("{}", fmt::join(v, ", ")), "1, 2, 3"); +} +#endif + +std::string vformat_message(int id, const char* format, fmt::format_args args) { + auto buffer = fmt::memory_buffer(); + format_to(fmt::appender(buffer), "[{}] ", id); + vformat_to(fmt::appender(buffer), format, args); + return to_string(buffer); +} + +template <typename... Args> +std::string format_message(int id, const char* format, const Args&... args) { + auto va = fmt::make_format_args(args...); + return vformat_message(id, format, va); +} + +TEST(format_test, format_message_example) { + EXPECT_EQ("[42] something happened", + format_message(42, "{} happened", "something")); +} + +template <typename... Args> +void print_error(const char* file, int line, const char* format, + const Args&... args) { + fmt::print("{}: {}: ", file, line); + fmt::print(format, args...); +} + +TEST(format_test, unpacked_args) { + EXPECT_EQ("0123456789abcdefg", + fmt::format("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f', 'g')); +} + +struct string_like {}; +fmt::string_view to_string_view(string_like) { return "foo"; } + +constexpr char with_null[3] = {'{', '}', '\0'}; +constexpr char no_null[2] = {'{', '}'}; +static constexpr const char static_with_null[3] = {'{', '}', '\0'}; +static constexpr const char static_no_null[2] = {'{', '}'}; + +TEST(format_test, compile_time_string) { + EXPECT_EQ("foo", fmt::format(FMT_STRING("foo"))); + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); + EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like())); + +#if FMT_USE_NONTYPE_TEMPLATE_ARGS + using namespace fmt::literals; + EXPECT_EQ("foobar", fmt::format(FMT_STRING("{foo}{bar}"), "bar"_a = "bar", + "foo"_a = "foo")); + EXPECT_EQ("", fmt::format(FMT_STRING(""))); + EXPECT_EQ("", fmt::format(FMT_STRING(""), "arg"_a = 42)); + EXPECT_EQ("42", fmt::format(FMT_STRING("{answer}"), "answer"_a = Answer())); +#endif + + (void)static_with_null; + (void)static_no_null; +#ifndef _MSC_VER + EXPECT_EQ("42", fmt::format(FMT_STRING(static_with_null), 42)); + EXPECT_EQ("42", fmt::format(FMT_STRING(static_no_null), 42)); +#endif + + (void)with_null; + (void)no_null; +#if FMT_CPLUSPLUS >= 201703L + EXPECT_EQ("42", fmt::format(FMT_STRING(with_null), 42)); + EXPECT_EQ("42", fmt::format(FMT_STRING(no_null), 42)); +#endif +#if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L + EXPECT_EQ("42", fmt::format(FMT_STRING(std::string_view("{}")), 42)); +#endif +} + +TEST(format_test, custom_format_compile_time_string) { + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), Answer())); + auto answer = Answer(); + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), answer)); + char buf[10] = {}; + fmt::format_to(buf, FMT_STRING("{}"), answer); + const Answer const_answer = Answer(); + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), const_answer)); +} + +#if FMT_USE_USER_DEFINED_LITERALS +TEST(format_test, named_arg_udl) { + using namespace fmt::literals; + auto udl_a = fmt::format("{first}{second}{first}{third}", "first"_a = "abra", + "second"_a = "cad", "third"_a = 99); + EXPECT_EQ( + fmt::format("{first}{second}{first}{third}", fmt::arg("first", "abra"), + fmt::arg("second", "cad"), fmt::arg("third", 99)), + udl_a); + + EXPECT_EQ("42", fmt::format("{answer}", "answer"_a = Answer())); +} +#endif // FMT_USE_USER_DEFINED_LITERALS + +TEST(format_test, enum) { EXPECT_EQ("0", fmt::format("{}", foo)); } + +TEST(format_test, formatter_not_specialized) { + static_assert(!fmt::has_formatter<fmt::formatter<test_enum>, + fmt::format_context>::value, + ""); +} + +#if FMT_HAS_FEATURE(cxx_strong_enums) +enum big_enum : unsigned long long { big_enum_value = 5000000000ULL }; +auto format_as(big_enum e) -> unsigned long long { return e; } + +TEST(format_test, strong_enum) { + EXPECT_EQ("5000000000", fmt::format("{}", big_enum_value)); +} +#endif + +TEST(format_test, non_null_terminated_format_string) { + EXPECT_EQ("42", fmt::format(string_view("{}foo", 2), 42)); +} + +struct variant { + enum { int_type, string_type } type; + explicit variant(int) : type(int_type) {} + explicit variant(const char*) : type(string_type) {} +}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter<variant> : dynamic_formatter<> { + auto format(variant value, format_context& ctx) -> decltype(ctx.out()) { + if (value.type == variant::int_type) + return dynamic_formatter<>::format(42, ctx); + return dynamic_formatter<>::format("foo", ctx); + } +}; +FMT_END_NAMESPACE + +TEST(format_test, dynamic_formatter) { + auto num = variant(42); + auto str = variant("foo"); + EXPECT_EQ("42", fmt::format("{:d}", num)); + EXPECT_EQ("foo", fmt::format("{:s}", str)); + EXPECT_EQ(" 42 foo ", fmt::format("{:{}} {:{}}", num, 3, str, 4)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{}}"), num), format_error, + "cannot switch from manual to automatic argument indexing"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:{0}}"), num), format_error, + "cannot switch from automatic to manual argument indexing"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:+}"), str), format_error, + "format specifier requires numeric argument"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:-}"), str), format_error, + "format specifier requires numeric argument"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{: }"), str), format_error, + "format specifier requires numeric argument"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:#}"), str), format_error, + "format specifier requires numeric argument"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:0}"), str), format_error, + "format specifier requires numeric argument"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:.2}"), num), format_error, + "precision not allowed for this argument type"); +} + +namespace adl_test { +namespace fmt { +namespace detail { +struct foo {}; +template <typename, typename OutputIt> void write(OutputIt, foo) = delete; +} // namespace detail +} // namespace fmt +} // namespace adl_test + +FMT_BEGIN_NAMESPACE +template <> +struct formatter<adl_test::fmt::detail::foo> : formatter<std::string> { + template <typename FormatContext> + auto format(adl_test::fmt::detail::foo, FormatContext& ctx) + -> decltype(ctx.out()) { + return formatter<std::string>::format("foo", ctx); + } +}; +FMT_END_NAMESPACE + +struct convertible_to_int { + operator int() const { return value; } + + int value = 42; +}; + +TEST(format_test, to_string) { + EXPECT_EQ(fmt::to_string(42), "42"); + EXPECT_EQ(fmt::to_string(reinterpret_cast<void*>(0x1234)), "0x1234"); + EXPECT_EQ(fmt::to_string(adl_test::fmt::detail::foo()), "foo"); + EXPECT_EQ(fmt::to_string(convertible_to_int()), "42"); + EXPECT_EQ(fmt::to_string(foo), "0"); + +#if FMT_USE_FLOAT128 + EXPECT_EQ(fmt::to_string(__float128(0.5)), "0.5"); +#endif +} + +TEST(format_test, output_iterators) { + std::list<char> out; + fmt::format_to(std::back_inserter(out), "{}", 42); + EXPECT_EQ("42", std::string(out.begin(), out.end())); + std::stringstream s; + fmt::format_to(std::ostream_iterator<char>(s), "{}", 42); + EXPECT_EQ("42", s.str()); +} + +TEST(format_test, formatted_size) { + EXPECT_EQ(2u, fmt::formatted_size("{}", 42)); +} + +TEST(format_test, format_to_no_args) { + std::string s; + fmt::format_to(std::back_inserter(s), "test"); + EXPECT_EQ("test", s); +} + +TEST(format_test, format_to) { + std::string s; + fmt::format_to(std::back_inserter(s), "part{0}", 1); + EXPECT_EQ("part1", s); + fmt::format_to(std::back_inserter(s), "part{0}", 2); + EXPECT_EQ("part1part2", s); +} + +TEST(format_test, format_to_memory_buffer) { + auto buf = fmt::basic_memory_buffer<char, 100>(); + fmt::format_to(fmt::appender(buf), "{}", "foo"); + EXPECT_EQ("foo", to_string(buf)); +} + +TEST(format_test, format_to_vector) { + std::vector<char> v; + fmt::format_to(std::back_inserter(v), "{}", "foo"); + EXPECT_EQ(string_view(v.data(), v.size()), "foo"); +} + +struct nongrowing_container { + using value_type = char; + void push_back(char) { throw std::runtime_error("can't take it any more"); } +}; + +TEST(format_test, format_to_propagates_exceptions) { + auto c = nongrowing_container(); + EXPECT_THROW(fmt::format_to(std::back_inserter(c), "{}", 42), + std::runtime_error); +} + +TEST(format_test, format_to_n) { + char buffer[4]; + buffer[3] = 'x'; + auto result = fmt::format_to_n(buffer, 3, "{}", 12345); + EXPECT_EQ(5u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ("123x", fmt::string_view(buffer, 4)); + + result = fmt::format_to_n(buffer, 3, "{:s}", "foobar"); + EXPECT_EQ(6u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ("foox", fmt::string_view(buffer, 4)); + + buffer[0] = 'x'; + buffer[1] = 'x'; + buffer[2] = 'x'; + result = fmt::format_to_n(buffer, 3, "{}", 'A'); + EXPECT_EQ(1u, result.size); + EXPECT_EQ(buffer + 1, result.out); + EXPECT_EQ("Axxx", fmt::string_view(buffer, 4)); + + result = fmt::format_to_n(buffer, 3, "{}{} ", 'B', 'C'); + EXPECT_EQ(3u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ("BC x", fmt::string_view(buffer, 4)); + + result = fmt::format_to_n(buffer, 4, "{}", "ABCDE"); + EXPECT_EQ(5u, result.size); + EXPECT_EQ("ABCD", fmt::string_view(buffer, 4)); + + buffer[3] = 'x'; + result = fmt::format_to_n(buffer, 3, "{}", std::string(1000, '*')); + EXPECT_EQ(1000u, result.size); + EXPECT_EQ("***x", fmt::string_view(buffer, 4)); +} + +struct test_output_iterator { + char* data; + + using iterator_category = std::output_iterator_tag; + using value_type = void; + using difference_type = void; + using pointer = void; + using reference = void; + + test_output_iterator& operator++() { + ++data; + return *this; + } + test_output_iterator operator++(int) { + auto tmp = *this; + ++data; + return tmp; + } + char& operator*() { return *data; } +}; + +TEST(format_test, format_to_n_output_iterator) { + char buf[10] = {}; + fmt::format_to_n(test_output_iterator{buf}, 10, "{}", 42); + EXPECT_STREQ(buf, "42"); +} + +#if FMT_USE_CONSTEXPR +struct test_error_handler { + const char*& error; + + FMT_CONSTEXPR test_error_handler(const char*& err) : error(err) {} + + FMT_CONSTEXPR test_error_handler(const test_error_handler& other) + : error(other.error) {} + + FMT_CONSTEXPR void on_error(const char* message) { + if (!error) error = message; + } +}; + +FMT_CONSTEXPR size_t len(const char* s) { + size_t len = 0; + while (*s++) ++len; + return len; +} + +FMT_CONSTEXPR bool equal(const char* s1, const char* s2) { + if (!s1 || !s2) return s1 == s2; + while (*s1 && *s1 == *s2) { + ++s1; + ++s2; + } + return *s1 == *s2; +} + +template <typename... Args> +FMT_CONSTEXPR bool test_error(const char* fmt, const char* expected_error) { + const char* actual_error = nullptr; + auto s = string_view(fmt, len(fmt)); + auto checker = + fmt::detail::format_string_checker<char, test_error_handler, Args...>( + s, test_error_handler(actual_error)); + fmt::detail::parse_format_string<true>(s, checker); + return equal(actual_error, expected_error); +} + +# define EXPECT_ERROR_NOARGS(fmt, error) \ + static_assert(test_error(fmt, error), "") +# define EXPECT_ERROR(fmt, error, ...) \ + static_assert(test_error<__VA_ARGS__>(fmt, error), "") + +TEST(format_test, format_string_errors) { + EXPECT_ERROR_NOARGS("foo", nullptr); + EXPECT_ERROR_NOARGS("}", "unmatched '}' in format string"); + EXPECT_ERROR("{0:s", "unknown format specifier", date); +# if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1916 + // This causes an detail compiler error in MSVC2017. + EXPECT_ERROR("{:{<}", "invalid fill character '{'", int); + EXPECT_ERROR("{:10000000000}", "number is too big", int); + EXPECT_ERROR("{:.10000000000}", "number is too big", int); + EXPECT_ERROR_NOARGS("{:x}", "argument not found"); + EXPECT_ERROR("{:+}", "format specifier requires numeric argument", + const char*); + EXPECT_ERROR("{:-}", "format specifier requires numeric argument", + const char*); + EXPECT_ERROR("{:#}", "format specifier requires numeric argument", + const char*); + EXPECT_ERROR("{: }", "format specifier requires numeric argument", + const char*); + EXPECT_ERROR("{:0}", "format specifier requires numeric argument", + const char*); + EXPECT_ERROR("{:+}", "format specifier requires signed argument", unsigned); + EXPECT_ERROR("{:-}", "format specifier requires signed argument", unsigned); + EXPECT_ERROR("{: }", "format specifier requires signed argument", unsigned); + EXPECT_ERROR("{:{}}", "argument not found", int); + EXPECT_ERROR("{:.{}}", "argument not found", double); + EXPECT_ERROR("{:{}}", "width/precision is not integer", int, double); + EXPECT_ERROR("{:.2}", "precision not allowed for this argument type", int); + EXPECT_ERROR("{:s}", "invalid type specifier", int); + EXPECT_ERROR("{:s}", "invalid type specifier", char); + EXPECT_ERROR("{:+}", "invalid format specifier for char", char); + EXPECT_ERROR("{:s}", "invalid type specifier", double); + EXPECT_ERROR("{:d}", "invalid type specifier", const char*); + EXPECT_ERROR("{:d}", "invalid type specifier", std::string); + EXPECT_ERROR("{:s}", "invalid type specifier", void*); +# else + fmt::print("warning: constexpr is broken in this version of MSVC\n"); +# endif +# if FMT_USE_NONTYPE_TEMPLATE_ARGS + using namespace fmt::literals; + EXPECT_ERROR("{foo}", "named argument is not found", decltype("bar"_a = 42)); + EXPECT_ERROR("{foo}", "named argument is not found", + decltype(fmt::arg("foo", 42))); +# else + EXPECT_ERROR("{foo}", + "compile-time checks for named arguments require C++20 support", + int); +# endif + EXPECT_ERROR_NOARGS("{10000000000}", "argument not found"); + EXPECT_ERROR_NOARGS("{0x}", "invalid format string"); + EXPECT_ERROR_NOARGS("{-}", "invalid format string"); + EXPECT_ERROR("{:{0x}}", "invalid format string", int); + EXPECT_ERROR("{:{-}}", "invalid format string", int); + EXPECT_ERROR("{:.{0x}}", "invalid format string", int); + EXPECT_ERROR("{:.{-}}", "invalid format string", int); + EXPECT_ERROR("{:.x}", "missing precision specifier", int); + EXPECT_ERROR_NOARGS("{}", "argument not found"); + EXPECT_ERROR("{1}", "argument not found", int); + EXPECT_ERROR("{1}{}", + "cannot switch from manual to automatic argument indexing", int, + int); + EXPECT_ERROR("{}{1}", + "cannot switch from automatic to manual argument indexing", int, + int); +} + +TEST(format_test, vformat_to) { + using context = fmt::format_context; + fmt::basic_format_arg<context> arg = fmt::detail::make_arg<context>(42); + auto args = fmt::basic_format_args<context>(&arg, 1); + auto s = std::string(); + fmt::vformat_to(std::back_inserter(s), "{}", args); + EXPECT_EQ("42", s); + s.clear(); + fmt::vformat_to(std::back_inserter(s), FMT_STRING("{}"), args); + EXPECT_EQ("42", s); +} + +#endif // FMT_USE_CONSTEXPR + +TEST(format_test, char_traits_is_not_ambiguous) { + // Test that we don't inject detail names into the std namespace. + using namespace std; + auto c = char_traits<char>::char_type(); + (void)c; +#if FMT_CPLUSPLUS >= 201103L + auto s = std::string(); + auto lval = begin(s); + (void)lval; +#endif +} + +struct check_back_appender {}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter<check_back_appender> { + auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template <typename Context> + auto format(check_back_appender, Context& ctx) -> decltype(ctx.out()) { + auto out = ctx.out(); + static_assert(std::is_same<decltype(++out), decltype(out)&>::value, + "needs to satisfy weakly_incrementable"); + *out = 'y'; + return ++out; + } +}; +FMT_END_NAMESPACE + +TEST(format_test, back_insert_slicing) { + EXPECT_EQ(fmt::format("{}", check_back_appender{}), "y"); +} + +template <typename Char, typename T> bool check_enabled_formatter() { + static_assert(std::is_default_constructible<fmt::formatter<T, Char>>::value, + ""); + return true; +} + +template <typename Char, typename... T> void check_enabled_formatters() { + auto dummy = {check_enabled_formatter<Char, T>()...}; + (void)dummy; +} + +TEST(format_test, test_formatters_enabled) { + check_enabled_formatters<char, bool, char, signed char, unsigned char, short, + unsigned short, int, unsigned, long, unsigned long, + long long, unsigned long long, float, double, + long double, void*, const void*, char*, const char*, + std::string, std::nullptr_t>(); + check_enabled_formatters<wchar_t, bool, wchar_t, signed char, unsigned char, + short, unsigned short, int, unsigned, long, + unsigned long, long long, unsigned long long, float, + double, long double, void*, const void*, wchar_t*, + const wchar_t*, std::wstring, std::nullptr_t>(); +} + +TEST(format_int_test, data) { + fmt::format_int format_int(42); + EXPECT_EQ("42", std::string(format_int.data(), format_int.size())); +} + +TEST(format_int_test, format_int) { + EXPECT_EQ("42", fmt::format_int(42).str()); + EXPECT_EQ(2u, fmt::format_int(42).size()); + EXPECT_EQ("-42", fmt::format_int(-42).str()); + EXPECT_EQ(3u, fmt::format_int(-42).size()); + EXPECT_EQ("42", fmt::format_int(42ul).str()); + EXPECT_EQ("-42", fmt::format_int(-42l).str()); + EXPECT_EQ("42", fmt::format_int(42ull).str()); + EXPECT_EQ("-42", fmt::format_int(-42ll).str()); + std::ostringstream os; + os << max_value<int64_t>(); + EXPECT_EQ(os.str(), fmt::format_int(max_value<int64_t>()).str()); +} diff --git a/src/fmt/test/fuzzing/.gitignore b/src/fmt/test/fuzzing/.gitignore new file mode 100644 index 000000000..ea4104026 --- /dev/null +++ b/src/fmt/test/fuzzing/.gitignore @@ -0,0 +1,3 @@ +# ignore artifacts from the build.sh script +build-*/ + diff --git a/src/fmt/test/fuzzing/CMakeLists.txt b/src/fmt/test/fuzzing/CMakeLists.txt new file mode 100644 index 000000000..0280c5cdf --- /dev/null +++ b/src/fmt/test/fuzzing/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright (c) 2019, Paul Dreik +# License: see LICENSE.rst in the fmt root directory + +# Link in the main function. Useful for reproducing, kcov, gdb, afl, valgrind. +# (Note that libFuzzer can also reproduce, just pass it the files.) +option(FMT_FUZZ_LINKMAIN "Enables the reproduce mode, instead of libFuzzer" On) + +# For oss-fuzz - insert $LIB_FUZZING_ENGINE into the link flags, but only for +# the fuzz targets, otherwise the CMake configuration step fails. +set(FMT_FUZZ_LDFLAGS "" CACHE STRING "LDFLAGS for the fuzz targets") + +# Adds a binary for reproducing, i.e. no fuzzing, just enables replaying data +# through the fuzzers. +function(add_fuzzer source) + get_filename_component(basename ${source} NAME_WE) + set(name ${basename}-fuzzer) + add_executable(${name} ${source} fuzzer-common.h) + if (FMT_FUZZ_LINKMAIN) + target_sources(${name} PRIVATE main.cc) + endif () + target_link_libraries(${name} PRIVATE fmt) + if (FMT_FUZZ_LDFLAGS) + target_link_libraries(${name} PRIVATE ${FMT_FUZZ_LDFLAGS}) + endif () + target_compile_features(${name} PRIVATE cxx_generic_lambdas) +endfunction() + +foreach (source chrono-duration.cc chrono-timepoint.cc float.cc named-arg.cc one-arg.cc two-args.cc) + add_fuzzer(${source}) +endforeach () diff --git a/src/fmt/test/fuzzing/README.md b/src/fmt/test/fuzzing/README.md new file mode 100644 index 000000000..bb3d0e04f --- /dev/null +++ b/src/fmt/test/fuzzing/README.md @@ -0,0 +1,25 @@ +# Running the fuzzers locally + +There is a [helper script](build.sh) to build the fuzzers, which has only been +tested on Debian and Ubuntu linux so far. There should be no problems fuzzing on +Windows (using clang>=8) or on Mac, but the script will probably not work out of +the box. + +Something along +```sh +mkdir build +cd build +export CXX=clang++ +export CXXFLAGS="-fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g" +cmake .. -DFMT_SAFE_DURATION_CAST=On -DFMT_FUZZ=On -DFMT_FUZZ_LINKMAIN=Off -DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer" +cmake --build . +``` +should work to build the fuzzers for all platforms which clang supports. + +Execute a fuzzer with for instance +```sh +cd build +export UBSAN_OPTIONS=halt_on_error=1 +mkdir out_chrono +bin/fuzzer_chrono_duration out_chrono +``` diff --git a/src/fmt/test/fuzzing/build.sh b/src/fmt/test/fuzzing/build.sh new file mode 100755 index 000000000..4497b62c1 --- /dev/null +++ b/src/fmt/test/fuzzing/build.sh @@ -0,0 +1,90 @@ +#!/bin/sh +# +# Creates fuzzer builds of various kinds +# - oss-fuzz emulated mode (makes sure a simulated invocation by oss-fuzz works) +# - libFuzzer build (you will need clang) +# - afl build (you will need afl) +# +# +# Copyright (c) 2019 Paul Dreik +# +# For the license information refer to format.h. + +set -e +me=$(basename $0) +root=$(readlink -f "$(dirname "$0")/../..") + + +echo $me: root=$root + +here=$(pwd) + +CXXFLAGSALL="-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g" +CMAKEFLAGSALL="$root -GNinja -DCMAKE_BUILD_TYPE=Debug -DFMT_DOC=Off -DFMT_TEST=Off -DFMT_FUZZ=On -DCMAKE_CXX_STANDARD=17" + +CLANG=clang++-11 + +# For performance analysis of the fuzzers. +builddir=$here/build-fuzzers-perfanalysis +mkdir -p $builddir +cd $builddir +CXX="ccache g++" CXXFLAGS="$CXXFLAGSALL -g" cmake \ +$CMAKEFLAGSALL \ +-DFMT_FUZZ_LINKMAIN=On \ +-DCMAKE_BUILD_TYPE=Release + +cmake --build $builddir + +# Builds the fuzzers as oss-fuzz does. +builddir=$here/build-fuzzers-ossfuzz +mkdir -p $builddir +cd $builddir +CXX=$CLANG \ +CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link" cmake \ +cmake $CMAKEFLAGSALL \ +-DFMT_FUZZ_LINKMAIN=Off \ +-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer" + +cmake --build $builddir + + +# Builds fuzzers for local fuzzing with libfuzzer with asan+usan. +builddir=$here/build-fuzzers-libfuzzer +mkdir -p $builddir +cd $builddir +CXX=$CLANG \ +CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link,address,undefined" cmake \ +cmake $CMAKEFLAGSALL \ +-DFMT_FUZZ_LINKMAIN=Off \ +-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer" + +cmake --build $builddir + +# Builds a fast fuzzer for making coverage fast. +builddir=$here/build-fuzzers-fast +mkdir -p $builddir +cd $builddir +CXX=$CLANG \ +CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link -O3" cmake \ +cmake $CMAKEFLAGSALL \ +-DFMT_FUZZ_LINKMAIN=Off \ +-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer" \ + -DCMAKE_BUILD_TYPE=Release + +cmake --build $builddir + + +# Builds fuzzers for local fuzzing with afl. +builddir=$here/build-fuzzers-afl +mkdir -p $builddir +cd $builddir +CXX="afl-g++" \ +CXXFLAGS="$CXXFLAGSALL -fsanitize=address,undefined" \ +cmake $CMAKEFLAGSALL \ +-DFMT_FUZZ_LINKMAIN=On + +cmake --build $builddir + + +echo $me: all good + diff --git a/src/fmt/test/fuzzing/chrono-duration.cc b/src/fmt/test/fuzzing/chrono-duration.cc new file mode 100644 index 000000000..d66068d9c --- /dev/null +++ b/src/fmt/test/fuzzing/chrono-duration.cc @@ -0,0 +1,136 @@ +// Copyright (c) 2019, Paul Dreik +// For the license information refer to format.h. + +#include <fmt/chrono.h> + +#include <cstdint> + +#include "fuzzer-common.h" + +template <typename Period, typename Rep> +void invoke_inner(fmt::string_view format_str, Rep rep) { + auto value = std::chrono::duration<Rep, Period>(rep); + try { +#if FMT_FUZZ_FORMAT_TO_STRING + std::string message = fmt::format(format_str, value); +#else + auto buf = fmt::memory_buffer(); + fmt::format_to(std::back_inserter(buf), format_str, value); +#endif + } catch (std::exception&) { + } +} + +// Rep is a duration's representation type. +template <typename Rep> +void invoke_outer(const uint8_t* data, size_t size, int period) { + // Always use a fixed location of the data. + static_assert(sizeof(Rep) <= fixed_size, "fixed size is too small"); + if (size <= fixed_size + 1) return; + + const Rep rep = assign_from_buf<Rep>(data); + data += fixed_size; + size -= fixed_size; + + // data is already allocated separately in libFuzzer so reading past the end + // will most likely be detected anyway. + const auto format_str = fmt::string_view(as_chars(data), size); + + // yocto, zepto, zetta and yotta are not handled. + switch (period) { + case 1: + invoke_inner<std::atto>(format_str, rep); + break; + case 2: + invoke_inner<std::femto>(format_str, rep); + break; + case 3: + invoke_inner<std::pico>(format_str, rep); + break; + case 4: + invoke_inner<std::nano>(format_str, rep); + break; + case 5: + invoke_inner<std::micro>(format_str, rep); + break; + case 6: + invoke_inner<std::milli>(format_str, rep); + break; + case 7: + invoke_inner<std::centi>(format_str, rep); + break; + case 8: + invoke_inner<std::deci>(format_str, rep); + break; + case 9: + invoke_inner<std::deca>(format_str, rep); + break; + case 10: + invoke_inner<std::kilo>(format_str, rep); + break; + case 11: + invoke_inner<std::mega>(format_str, rep); + break; + case 12: + invoke_inner<std::giga>(format_str, rep); + break; + case 13: + invoke_inner<std::tera>(format_str, rep); + break; + case 14: + invoke_inner<std::peta>(format_str, rep); + break; + case 15: + invoke_inner<std::exa>(format_str, rep); + break; + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size <= 4) return 0; + + const auto representation = data[0]; + const auto period = data[1]; + data += 2; + size -= 2; + + switch (representation) { + case 1: + invoke_outer<char>(data, size, period); + break; + case 2: + invoke_outer<signed char>(data, size, period); + break; + case 3: + invoke_outer<unsigned char>(data, size, period); + break; + case 4: + invoke_outer<short>(data, size, period); + break; + case 5: + invoke_outer<unsigned short>(data, size, period); + break; + case 6: + invoke_outer<int>(data, size, period); + break; + case 7: + invoke_outer<unsigned int>(data, size, period); + break; + case 8: + invoke_outer<long>(data, size, period); + break; + case 9: + invoke_outer<unsigned long>(data, size, period); + break; + case 10: + invoke_outer<float>(data, size, period); + break; + case 11: + invoke_outer<double>(data, size, period); + break; + case 12: + invoke_outer<long double>(data, size, period); + break; + } + return 0; +} diff --git a/src/fmt/test/fuzzing/chrono-timepoint.cc b/src/fmt/test/fuzzing/chrono-timepoint.cc new file mode 100644 index 000000000..8a1b24d29 --- /dev/null +++ b/src/fmt/test/fuzzing/chrono-timepoint.cc @@ -0,0 +1,32 @@ +// Copyright (c) 2021, Paul Dreik +// For license information refer to format.h. +#include <fmt/chrono.h> + +#include "fuzzer-common.h" + +/* + * a fuzzer for the chrono timepoints formatters + * C is a clock (std::chrono::system_clock etc) + */ +template <typename C> void doit(const uint8_t* data, size_t size) { + using Rep = typename C::time_point::rep; + constexpr auto N = sizeof(Rep); + if (size < N) return; + + const auto x = assign_from_buf<Rep>(data); + typename C::duration dur{x}; + typename C::time_point timepoint{dur}; + data += N; + size -= N; + data_to_string format_str(data, size); + + std::string message = fmt::format(format_str.get(), timepoint); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + try { + doit<std::chrono::system_clock>(data, size); + } catch (...) { + } + return 0; +} diff --git a/src/fmt/test/fuzzing/float.cc b/src/fmt/test/fuzzing/float.cc new file mode 100644 index 000000000..d4c9e4f57 --- /dev/null +++ b/src/fmt/test/fuzzing/float.cc @@ -0,0 +1,39 @@ +// A fuzzer for floating-point formatter. +// For the license information refer to format.h. + +#include <fmt/format.h> + +#include <cstdint> +#include <cstdlib> +#include <limits> +#include <stdexcept> + +#include "fuzzer-common.h" + +void check_round_trip(fmt::string_view format_str, double value) { + auto buffer = fmt::memory_buffer(); + fmt::format_to(std::back_inserter(buffer), format_str, value); + + if (std::isnan(value)) { + auto nan = std::signbit(value) ? "-nan" : "nan"; + if (fmt::string_view(buffer.data(), buffer.size()) != nan) + throw std::runtime_error("round trip failure"); + return; + } + + buffer.push_back('\0'); + char* ptr = nullptr; + if (std::strtod(buffer.data(), &ptr) != value) + throw std::runtime_error("round trip failure"); + if (ptr + 1 != buffer.end()) throw std::runtime_error("unparsed output"); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size <= sizeof(double) || !std::numeric_limits<double>::is_iec559) + return 0; + check_round_trip("{}", assign_from_buf<double>(data)); + // A larger than necessary precision is used to trigger the fallback + // formatter. + check_round_trip("{:.50g}", assign_from_buf<double>(data)); + return 0; +} diff --git a/src/fmt/test/fuzzing/fuzzer-common.h b/src/fmt/test/fuzzing/fuzzer-common.h new file mode 100644 index 000000000..c0a246722 --- /dev/null +++ b/src/fmt/test/fuzzing/fuzzer-common.h @@ -0,0 +1,77 @@ +// Copyright (c) 2019, Paul Dreik +// For the license information refer to format.h. + +#ifndef FUZZER_COMMON_H +#define FUZZER_COMMON_H + +#include <fmt/core.h> + +#include <cstdint> // std::uint8_t +#include <cstring> // memcpy +#include <vector> + +// One can format to either a string, or a buffer. The latter is faster, but +// one may be interested in formatting to a string instead to verify it works +// as intended. To avoid a combinatoric explosion, select this at compile time +// instead of dynamically from the fuzz data. +#define FMT_FUZZ_FORMAT_TO_STRING 0 + +// If {fmt} is given a buffer that is separately allocated, chances that address +// sanitizer detects out of bound reads is much higher. However, it slows down +// the fuzzing. +#define FMT_FUZZ_SEPARATE_ALLOCATION 1 + +// The size of the largest possible type in use. +// To let the the fuzzer mutation be efficient at cross pollinating between +// different types, use a fixed size format. The same bit pattern, interpreted +// as another type, is likely interesting. +constexpr auto fixed_size = 16; + +// Casts data to a char pointer. +template <typename T> inline const char* as_chars(const T* data) { + return reinterpret_cast<const char*>(data); +} + +// Casts data to a byte pointer. +template <typename T> inline const std::uint8_t* as_bytes(const T* data) { + return reinterpret_cast<const std::uint8_t*>(data); +} + +// Blits bytes from data to form an (assumed trivially constructible) object +// of type Item. +template <class Item> inline Item assign_from_buf(const std::uint8_t* data) { + auto item = Item(); + std::memcpy(&item, data, sizeof(Item)); + return item; +} + +// Reads a boolean value by looking at the first byte from data. +template <> inline bool assign_from_buf<bool>(const std::uint8_t* data) { + return *data != 0; +} + +struct data_to_string { +#if FMT_FUZZ_SEPARATE_ALLOCATION + std::vector<char> buffer; + + data_to_string(const uint8_t* data, size_t size, bool add_terminator = false) + : buffer(size + (add_terminator ? 1 : 0)) { + if (size) { + std::memcpy(buffer.data(), data, size); + } + } + + fmt::string_view get() const { return {buffer.data(), buffer.size()}; } +#else + fmt::string_view sv; + + data_to_string(const uint8_t* data, size_t size, bool = false) + : str(as_chars(data), size) {} + + fmt::string_view get() const { return sv; } +#endif + + const char* data() const { return get().data(); } +}; + +#endif // FUZZER_COMMON_H diff --git a/src/fmt/test/fuzzing/main.cc b/src/fmt/test/fuzzing/main.cc new file mode 100644 index 000000000..8f8c719b7 --- /dev/null +++ b/src/fmt/test/fuzzing/main.cc @@ -0,0 +1,22 @@ +#include <cassert> +#include <fstream> +#include <vector> + +#include "fuzzer-common.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); + +int main(int argc, char** argv) { + for (int i = 1; i < argc; ++i) { + std::ifstream in(argv[i]); + assert(in); + in.seekg(0, std::ios_base::end); + const auto size = in.tellg(); + assert(size >= 0); + in.seekg(0, std::ios_base::beg); + std::vector<char> buf(static_cast<size_t>(size)); + in.read(buf.data(), size); + assert(in.gcount() == size); + LLVMFuzzerTestOneInput(as_bytes(buf.data()), buf.size()); + } +} diff --git a/src/fmt/test/fuzzing/named-arg.cc b/src/fmt/test/fuzzing/named-arg.cc new file mode 100644 index 000000000..3bee1ae3b --- /dev/null +++ b/src/fmt/test/fuzzing/named-arg.cc @@ -0,0 +1,102 @@ +// Copyright (c) 2019, Paul Dreik +// For the license information refer to format.h. + +#include <fmt/chrono.h> + +#include <cstdint> +#include <type_traits> +#include <vector> + +#include "fuzzer-common.h" + +template <typename T> +void invoke_fmt(const uint8_t* data, size_t size, unsigned arg_name_size) { + static_assert(sizeof(T) <= fixed_size, "fixed_size too small"); + if (size <= fixed_size) return; + const T value = assign_from_buf<T>(data); + data += fixed_size; + size -= fixed_size; + + if (arg_name_size <= 0 || arg_name_size >= size) return; + data_to_string arg_name(data, arg_name_size, true); + data += arg_name_size; + size -= arg_name_size; + + data_to_string format_str(data, size); + try { +#if FMT_FUZZ_FORMAT_TO_STRING + std::string message = + fmt::format(format_str.get(), fmt::arg(arg_name.data(), value)); +#else + fmt::memory_buffer out; + fmt::format_to(std::back_inserter(out), format_str.get(), + fmt::arg(arg_name.data(), value)); +#endif + } catch (std::exception&) { + } +} + +// For dynamic dispatching to an explicit instantiation. +template <typename Callback> void invoke(int type, Callback callback) { + switch (type) { + case 0: + callback(bool()); + break; + case 1: + callback(char()); + break; + case 2: + using sc = signed char; + callback(sc()); + break; + case 3: + using uc = unsigned char; + callback(uc()); + break; + case 4: + callback(short()); + break; + case 5: + using us = unsigned short; + callback(us()); + break; + case 6: + callback(int()); + break; + case 7: + callback(unsigned()); + break; + case 8: + callback(long()); + break; + case 9: + using ul = unsigned long; + callback(ul()); + break; + case 10: + callback(float()); + break; + case 11: + callback(double()); + break; + case 12: + using LD = long double; + callback(LD()); + break; + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size <= 3) return 0; + + // Switch types depending on the first byte of the input. + const auto type = data[0] & 0x0F; + const unsigned arg_name_size = (data[0] & 0xF0) >> 4; + data++; + size--; + + invoke(type, [=](auto arg) { + invoke_fmt<decltype(arg)>(data, size, arg_name_size); + }); + return 0; +} diff --git a/src/fmt/test/fuzzing/one-arg.cc b/src/fmt/test/fuzzing/one-arg.cc new file mode 100644 index 000000000..af9787f81 --- /dev/null +++ b/src/fmt/test/fuzzing/one-arg.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2019, Paul Dreik +// For the license information refer to format.h. + +#include <fmt/chrono.h> + +#include <cstdint> +#include <exception> + +#include "fuzzer-common.h" + +template <typename T, typename Repr> const T* from_repr(const Repr& r) { + return &r; +} + +template <> const std::tm* from_repr<std::tm>(const std::time_t& t) { + return std::localtime(&t); +} + +template <typename T, typename Repr = T> +void invoke_fmt(const uint8_t* data, size_t size) { + static_assert(sizeof(Repr) <= fixed_size, "Nfixed is too small"); + if (size <= fixed_size) return; + auto repr = assign_from_buf<Repr>(data); + const T* value = from_repr<T>(repr); + if (!value) return; + data += fixed_size; + size -= fixed_size; + data_to_string format_str(data, size); + try { +#if FMT_FUZZ_FORMAT_TO_STRING + std::string message = fmt::format(format_str.get(), *value); +#else + auto buf = fmt::memory_buffer(); + fmt::format_to(std::back_inserter(buf), format_str.get(), *value); +#endif + } catch (std::exception&) { + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size <= 3) return 0; + + const auto first = data[0]; + data++; + size--; + + switch (first) { + case 0: + invoke_fmt<bool>(data, size); + break; + case 1: + invoke_fmt<char>(data, size); + break; + case 2: + invoke_fmt<unsigned char>(data, size); + break; + case 3: + invoke_fmt<signed char>(data, size); + break; + case 4: + invoke_fmt<short>(data, size); + break; + case 5: + invoke_fmt<unsigned short>(data, size); + break; + case 6: + invoke_fmt<int>(data, size); + break; + case 7: + invoke_fmt<unsigned int>(data, size); + break; + case 8: + invoke_fmt<long>(data, size); + break; + case 9: + invoke_fmt<unsigned long>(data, size); + break; + case 10: + invoke_fmt<float>(data, size); + break; + case 11: + invoke_fmt<double>(data, size); + break; + case 12: + invoke_fmt<long double>(data, size); + break; + case 13: + invoke_fmt<std::tm, std::time_t>(data, size); + break; + } + return 0; +} diff --git a/src/fmt/test/fuzzing/two-args.cc b/src/fmt/test/fuzzing/two-args.cc new file mode 100644 index 000000000..931c64656 --- /dev/null +++ b/src/fmt/test/fuzzing/two-args.cc @@ -0,0 +1,106 @@ +// Copyright (c) 2019, Paul Dreik +// For the license information refer to format.h. + +#include <fmt/format.h> + +#include <cstdint> +#include <exception> +#include <string> + +#include "fuzzer-common.h" + +template <typename Item1, typename Item2> +void invoke_fmt(const uint8_t* data, size_t size) { + static_assert(sizeof(Item1) <= fixed_size, "size1 exceeded"); + static_assert(sizeof(Item2) <= fixed_size, "size2 exceeded"); + if (size <= fixed_size + fixed_size) return; + + const Item1 item1 = assign_from_buf<Item1>(data); + data += fixed_size; + size -= fixed_size; + + const Item2 item2 = assign_from_buf<Item2>(data); + data += fixed_size; + size -= fixed_size; + + auto format_str = fmt::string_view(as_chars(data), size); +#if FMT_FUZZ_FORMAT_TO_STRING + std::string message = fmt::format(format_str, item1, item2); +#else + auto buf = fmt::memory_buffer(); + fmt::format_to(std::back_inserter(buf), format_str, item1, item2); +#endif +} + +// For dynamic dispatching to an explicit instantiation. +template <typename Callback> void invoke(int index, Callback callback) { + switch (index) { + case 0: + callback(bool()); + break; + case 1: + callback(char()); + break; + case 2: + using sc = signed char; + callback(sc()); + break; + case 3: + using uc = unsigned char; + callback(uc()); + break; + case 4: + callback(short()); + break; + case 5: + using us = unsigned short; + callback(us()); + break; + case 6: + callback(int()); + break; + case 7: + callback(unsigned()); + break; + case 8: + callback(long()); + break; + case 9: + using ul = unsigned long; + callback(ul()); + break; + case 10: + callback(float()); + break; + case 11: + callback(double()); + break; + case 12: + using LD = long double; + callback(LD()); + break; + case 13: + using ptr = void*; + callback(ptr()); + break; + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size <= 3) return 0; + + // Switch types depending on the first byte of the input. + const auto type1 = data[0] & 0x0F; + const auto type2 = (data[0] & 0xF0) >> 4; + data++; + size--; + try { + invoke(type1, [=](auto param1) { + invoke(type2, [=](auto param2) { + invoke_fmt<decltype(param1), decltype(param2)>(data, size); + }); + }); + } catch (std::exception&) { + } + return 0; +} diff --git a/src/fmt/test/gtest-extra-test.cc b/src/fmt/test/gtest-extra-test.cc new file mode 100644 index 000000000..69e42bc68 --- /dev/null +++ b/src/fmt/test/gtest-extra-test.cc @@ -0,0 +1,413 @@ +// Formatting library for C++ - tests of custom Google Test assertions +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "gtest-extra.h" + +#include <gtest/gtest-spi.h> + +#include <cstring> +#include <memory> +#include <stdexcept> + +#include "fmt/os.h" +#include "util.h" + +// Tests that assertion macros evaluate their arguments exactly once. +namespace { +class single_evaluation_test : public ::testing::Test { + protected: + single_evaluation_test() { + p_ = s_; + a_ = 0; + b_ = 0; + } + + static const char* const s_; + static const char* p_; + + static int a_; + static int b_; +}; +} // namespace + +const char* const single_evaluation_test::s_ = "01234"; +const char* single_evaluation_test::p_; +int single_evaluation_test::a_; +int single_evaluation_test::b_; + +void do_nothing() {} + +FMT_NORETURN void throw_exception() { throw std::runtime_error("test"); } + +FMT_NORETURN void throw_system_error() { + throw fmt::system_error(EDOM, "test"); +} + +// Tests that when EXPECT_THROW_MSG fails, it evaluates its message argument +// exactly once. +TEST_F(single_evaluation_test, failed_expect_throw_msg) { + EXPECT_NONFATAL_FAILURE( + EXPECT_THROW_MSG(throw_exception(), std::exception, p_++), "01234"); + EXPECT_EQ(s_ + 1, p_); +} + +// Tests that when EXPECT_SYSTEM_ERROR fails, it evaluates its message argument +// exactly once. +TEST_F(single_evaluation_test, failed_expect_system_error) { + EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, p_++), + "01234"); + EXPECT_EQ(s_ + 1, p_); +} + +// Tests that assertion arguments are evaluated exactly once. +TEST_F(single_evaluation_test, exception_tests) { + // successful EXPECT_THROW_MSG + EXPECT_THROW_MSG( + { // NOLINT + a_++; + throw_exception(); + }, + std::exception, (b_++, "test")); + EXPECT_EQ(1, a_); + EXPECT_EQ(1, b_); + + // failed EXPECT_THROW_MSG, throws different type + EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG( + { // NOLINT + a_++; + throw_exception(); + }, + std::logic_error, (b_++, "test")), + "throws a different type"); + EXPECT_EQ(2, a_); + EXPECT_EQ(2, b_); + + // failed EXPECT_THROW_MSG, throws an exception with different message + EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG( + { // NOLINT + a_++; + throw_exception(); + }, + std::exception, (b_++, "other")), + "throws an exception with a different message"); + EXPECT_EQ(3, a_); + EXPECT_EQ(3, b_); + + // failed EXPECT_THROW_MSG, throws nothing + EXPECT_NONFATAL_FAILURE( + EXPECT_THROW_MSG(a_++, std::exception, (b_++, "test")), "throws nothing"); + EXPECT_EQ(4, a_); + EXPECT_EQ(4, b_); +} + +TEST_F(single_evaluation_test, system_error_tests) { + // successful EXPECT_SYSTEM_ERROR + EXPECT_SYSTEM_ERROR( + { // NOLINT + a_++; + throw_system_error(); + }, + EDOM, (b_++, "test")); + EXPECT_EQ(1, a_); + EXPECT_EQ(1, b_); + + // failed EXPECT_SYSTEM_ERROR, throws different type + EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR( + { // NOLINT + a_++; + throw_exception(); + }, + EDOM, (b_++, "test")), + "throws a different type"); + EXPECT_EQ(2, a_); + EXPECT_EQ(2, b_); + + // failed EXPECT_SYSTEM_ERROR, throws an exception with different message + EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR( + { // NOLINT + a_++; + throw_system_error(); + }, + EDOM, (b_++, "other")), + "throws an exception with a different message"); + EXPECT_EQ(3, a_); + EXPECT_EQ(3, b_); + + // failed EXPECT_SYSTEM_ERROR, throws nothing + EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(a_++, EDOM, (b_++, "test")), + "throws nothing"); + EXPECT_EQ(4, a_); + EXPECT_EQ(4, b_); +} + +#if FMT_USE_FCNTL +// Tests that when EXPECT_WRITE fails, it evaluates its message argument +// exactly once. +TEST_F(single_evaluation_test, failed_expect_write) { + EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), p_++), + "01234"); + EXPECT_EQ(s_ + 1, p_); +} + +// Tests that assertion arguments are evaluated exactly once. +TEST_F(single_evaluation_test, write_tests) { + // successful EXPECT_WRITE + EXPECT_WRITE( + stdout, + { // NOLINT + a_++; + std::printf("test"); + }, + (b_++, "test")); + EXPECT_EQ(1, a_); + EXPECT_EQ(1, b_); + + // failed EXPECT_WRITE + EXPECT_NONFATAL_FAILURE(EXPECT_WRITE( + stdout, + { // NOLINT + a_++; + std::printf("test"); + }, + (b_++, "other")), + "Actual: test"); + EXPECT_EQ(2, a_); + EXPECT_EQ(2, b_); +} + +// Tests EXPECT_WRITE. +TEST(gtest_extra_test, expect_write) { + EXPECT_WRITE(stdout, do_nothing(), ""); + EXPECT_WRITE(stdout, std::printf("test"), "test"); + EXPECT_WRITE(stderr, std::fprintf(stderr, "test"), "test"); + EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("that"), "this"), + "Expected: this\n" + " Actual: that"); +} + +TEST(gtest_extra_test, expect_write_streaming) { + EXPECT_WRITE(stdout, std::printf("test"), "test") << "unexpected failure"; + EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), "other") + << "expected failure", + "expected failure"); +} +#endif // FMT_USE_FCNTL + +// Tests that the compiler will not complain about unreachable code in the +// EXPECT_THROW_MSG macro. +TEST(gtest_extra_test, expect_throw_no_unreachable_code_warning) { + int n = 0; + using std::runtime_error; + EXPECT_THROW_MSG(throw runtime_error(""), runtime_error, ""); + EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(n++, runtime_error, ""), ""); + EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(throw 1, runtime_error, ""), ""); + EXPECT_NONFATAL_FAILURE( + EXPECT_THROW_MSG(throw runtime_error("a"), runtime_error, "b"), ""); +} + +// Tests that the compiler will not complain about unreachable code in the +// EXPECT_SYSTEM_ERROR macro. +TEST(gtest_extra_test, expect_system_error_no_unreachable_code_warning) { + int n = 0; + EXPECT_SYSTEM_ERROR(throw fmt::system_error(EDOM, "test"), EDOM, "test"); + EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(n++, EDOM, ""), ""); + EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(throw 1, EDOM, ""), ""); + EXPECT_NONFATAL_FAILURE( + EXPECT_SYSTEM_ERROR(throw fmt::system_error(EDOM, "aaa"), EDOM, "bbb"), + ""); +} + +TEST(gtest_extra_test, expect_throw_behaves_like_single_statement) { + if (::testing::internal::AlwaysFalse()) + EXPECT_THROW_MSG(do_nothing(), std::exception, ""); + + if (::testing::internal::AlwaysTrue()) + EXPECT_THROW_MSG(throw_exception(), std::exception, "test"); + else + do_nothing(); +} + +TEST(gtest_extra_test, expect_system_error_behaves_like_single_statement) { + if (::testing::internal::AlwaysFalse()) + EXPECT_SYSTEM_ERROR(do_nothing(), EDOM, ""); + + if (::testing::internal::AlwaysTrue()) + EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "test"); + else + do_nothing(); +} + +TEST(gtest_extra_test, expect_write_behaves_like_single_statement) { + if (::testing::internal::AlwaysFalse()) + EXPECT_WRITE(stdout, std::printf("x"), "x"); + + if (::testing::internal::AlwaysTrue()) + EXPECT_WRITE(stdout, std::printf("x"), "x"); + else + do_nothing(); +} + +// Tests EXPECT_THROW_MSG. +TEST(gtest_extra_test, expect_throw_msg) { + EXPECT_THROW_MSG(throw_exception(), std::exception, "test"); + EXPECT_NONFATAL_FAILURE( + EXPECT_THROW_MSG(throw_exception(), std::logic_error, "test"), + "Expected: throw_exception() throws an exception of " + "type std::logic_error.\n Actual: it throws a different type."); + EXPECT_NONFATAL_FAILURE( + EXPECT_THROW_MSG(do_nothing(), std::exception, "test"), + "Expected: do_nothing() throws an exception of type std::exception.\n" + " Actual: it throws nothing."); + EXPECT_NONFATAL_FAILURE( + EXPECT_THROW_MSG(throw_exception(), std::exception, "other"), + "throw_exception() throws an exception with a different message.\n" + "Expected: other\n" + " Actual: test"); +} + +// Tests EXPECT_SYSTEM_ERROR. +TEST(gtest_extra_test, expect_system_error) { + EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "test"); + EXPECT_NONFATAL_FAILURE( + EXPECT_SYSTEM_ERROR(throw_exception(), EDOM, "test"), + "Expected: throw_exception() throws an exception of " + "type std::system_error.\n Actual: it throws a different type."); + EXPECT_NONFATAL_FAILURE( + EXPECT_SYSTEM_ERROR(do_nothing(), EDOM, "test"), + "Expected: do_nothing() throws an exception of type std::system_error.\n" + " Actual: it throws nothing."); + EXPECT_NONFATAL_FAILURE( + EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "other"), + fmt::format( + "throw_system_error() throws an exception with a different message.\n" + "Expected: {}\n" + " Actual: {}", + system_error_message(EDOM, "other"), + system_error_message(EDOM, "test"))); +} + +TEST(gtest_extra_test, expect_throw_msg_streaming) { + EXPECT_THROW_MSG(throw_exception(), std::exception, "test") + << "unexpected failure"; + EXPECT_NONFATAL_FAILURE( + EXPECT_THROW_MSG(throw_exception(), std::exception, "other") + << "expected failure", + "expected failure"); +} + +TEST(gtest_extra_test, expect_system_error_streaming) { + EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "test") + << "unexpected failure"; + EXPECT_NONFATAL_FAILURE( + EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "other") + << "expected failure", + "expected failure"); +} + +#if FMT_USE_FCNTL + +using fmt::buffered_file; +using fmt::file; + +TEST(output_redirect_test, scoped_redirect) { + file read_end, write_end; + file::pipe(read_end, write_end); + { + buffered_file file(write_end.fdopen("w")); + std::fprintf(file.get(), "[[["); + { + output_redirect redir(file.get()); + std::fprintf(file.get(), "censored"); + } + std::fprintf(file.get(), "]]]"); + } + EXPECT_READ(read_end, "[[[]]]"); +} + +// Test that output_redirect handles errors in flush correctly. +TEST(output_redirect_test, flush_error_in_ctor) { + file read_end, write_end; + file::pipe(read_end, write_end); + int write_fd = write_end.descriptor(); + file write_copy = write_end.dup(write_fd); + buffered_file f = write_end.fdopen("w"); + // Put a character in a file buffer. + EXPECT_EQ('x', fputc('x', f.get())); + FMT_POSIX(close(write_fd)); + std::unique_ptr<output_redirect> redir{nullptr}; + EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new output_redirect(f.get())), EBADF, + "cannot flush stream"); + redir.reset(nullptr); + write_copy.dup2(write_fd); // "undo" close or dtor will fail +} + +TEST(output_redirect_test, dup_error_in_ctor) { + buffered_file f = open_buffered_file(); + int fd = (f.descriptor)(); + file copy = file::dup(fd); + FMT_POSIX(close(fd)); + std::unique_ptr<output_redirect> redir{nullptr}; + EXPECT_SYSTEM_ERROR_NOASSERT( + redir.reset(new output_redirect(f.get())), EBADF, + fmt::format("cannot duplicate file descriptor {}", fd)); + copy.dup2(fd); // "undo" close or dtor will fail +} + +TEST(output_redirect_test, restore_and_read) { + file read_end, write_end; + file::pipe(read_end, write_end); + buffered_file file(write_end.fdopen("w")); + std::fprintf(file.get(), "[[["); + output_redirect redir(file.get()); + std::fprintf(file.get(), "censored"); + EXPECT_EQ("censored", redir.restore_and_read()); + EXPECT_EQ("", redir.restore_and_read()); + std::fprintf(file.get(), "]]]"); + file = buffered_file(); + EXPECT_READ(read_end, "[[[]]]"); +} + +// Test that OutputRedirect handles errors in flush correctly. +TEST(output_redirect_test, flush_error_in_restore_and_read) { + file read_end, write_end; + file::pipe(read_end, write_end); + int write_fd = write_end.descriptor(); + file write_copy = write_end.dup(write_fd); + buffered_file f = write_end.fdopen("w"); + output_redirect redir(f.get()); + // Put a character in a file buffer. + EXPECT_EQ('x', fputc('x', f.get())); + FMT_POSIX(close(write_fd)); + EXPECT_SYSTEM_ERROR_NOASSERT(redir.restore_and_read(), EBADF, + "cannot flush stream"); + write_copy.dup2(write_fd); // "undo" close or dtor will fail +} + +TEST(output_redirect_test, error_in_dtor) { + file read_end, write_end; + file::pipe(read_end, write_end); + int write_fd = write_end.descriptor(); + file write_copy = write_end.dup(write_fd); + buffered_file f = write_end.fdopen("w"); + std::unique_ptr<output_redirect> redir(new output_redirect(f.get())); + // Put a character in a file buffer. + EXPECT_EQ('x', fputc('x', f.get())); + EXPECT_WRITE( + stderr, + { + // The close function must be called inside EXPECT_WRITE, + // otherwise the system may recycle closed file descriptor when + // redirecting the output in EXPECT_STDERR and the second close + // will break output redirection. + FMT_POSIX(close(write_fd)); + SUPPRESS_ASSERT(redir.reset(nullptr)); + }, + system_error_message(EBADF, "cannot flush stream")); + write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail +} + +#endif // FMT_USE_FCNTL diff --git a/src/fmt/test/gtest-extra.cc b/src/fmt/test/gtest-extra.cc new file mode 100644 index 000000000..542e4b5e6 --- /dev/null +++ b/src/fmt/test/gtest-extra.cc @@ -0,0 +1,80 @@ +// Formatting library for C++ - custom Google Test assertions +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "gtest-extra.h" + +#if FMT_USE_FCNTL + +using fmt::file; + +output_redirect::output_redirect(FILE* f) : file_(f) { + flush(); + int fd = FMT_POSIX(fileno(f)); + // Create a file object referring to the original file. + original_ = file::dup(fd); + // Create a pipe. + file write_end; + file::pipe(read_end_, write_end); + // Connect the passed FILE object to the write end of the pipe. + write_end.dup2(fd); +} + +output_redirect::~output_redirect() noexcept { + try { + restore(); + } catch (const std::exception& e) { + std::fputs(e.what(), stderr); + } +} + +void output_redirect::flush() { + int result = 0; + do { + result = fflush(file_); + } while (result == EOF && errno == EINTR); + if (result != 0) throw fmt::system_error(errno, "cannot flush stream"); +} + +void output_redirect::restore() { + if (original_.descriptor() == -1) return; // Already restored. + flush(); + // Restore the original file. + original_.dup2(FMT_POSIX(fileno(file_))); + original_.close(); +} + +std::string output_redirect::restore_and_read() { + // Restore output. + restore(); + + // Read everything from the pipe. + std::string content; + if (read_end_.descriptor() == -1) return content; // Already read. + enum { BUFFER_SIZE = 4096 }; + char buffer[BUFFER_SIZE]; + size_t count = 0; + do { + count = read_end_.read(buffer, BUFFER_SIZE); + content.append(buffer, count); + } while (count != 0); + read_end_.close(); + return content; +} + +std::string read(file& f, size_t count) { + std::string buffer(count, '\0'); + size_t n = 0, offset = 0; + do { + n = f.read(&buffer[offset], count - offset); + // We can't read more than size_t bytes since count has type size_t. + offset += n; + } while (offset < count && n != 0); + buffer.resize(offset); + return buffer; +} + +#endif // FMT_USE_FCNTL diff --git a/src/fmt/test/gtest-extra.h b/src/fmt/test/gtest-extra.h new file mode 100644 index 000000000..ef2a04e7c --- /dev/null +++ b/src/fmt/test/gtest-extra.h @@ -0,0 +1,170 @@ +// Formatting library for C++ - custom Google Test assertions +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_GTEST_EXTRA_H_ +#define FMT_GTEST_EXTRA_H_ + +#include <stdlib.h> // _invalid_parameter_handler + +#include <string> + +#ifdef FMT_MODULE_TEST +import fmt; +#else +# include "fmt/os.h" +#endif // FMG_MODULE_TEST + +#include "gmock/gmock.h" + +#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \ + std::string gtest_expected_message = expected_message; \ + bool gtest_caught_expected = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (expected_exception const& e) { \ + if (gtest_expected_message != e.what()) { \ + gtest_ar << #statement \ + " throws an exception with a different message.\n" \ + << "Expected: " << gtest_expected_message << "\n" \ + << " Actual: " << e.what(); \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + gtest_caught_expected = true; \ + } catch (...) { \ + gtest_ar << "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws a different type."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + if (!gtest_caught_expected) { \ + gtest_ar << "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws nothing."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \ + : fail(gtest_ar.failure_message()) + +// Tests that the statement throws the expected exception and the exception's +// what() method returns expected message. +#define EXPECT_THROW_MSG(statement, expected_exception, expected_message) \ + FMT_TEST_THROW_(statement, expected_exception, expected_message, \ + GTEST_NONFATAL_FAILURE_) + +inline std::string system_error_message(int error_code, + const std::string& message) { + auto ec = std::error_code(error_code, std::generic_category()); + return std::system_error(ec, message).what(); +} + +#define EXPECT_SYSTEM_ERROR(statement, error_code, message) \ + EXPECT_THROW_MSG(statement, std::system_error, \ + system_error_message(error_code, message)) + +#if FMT_USE_FCNTL + +// Captures file output by redirecting it to a pipe. +// The output it can handle is limited by the pipe capacity. +class output_redirect { + private: + FILE* file_; + fmt::file original_; // Original file passed to redirector. + fmt::file read_end_; // Read end of the pipe where the output is redirected. + + void flush(); + void restore(); + + public: + explicit output_redirect(FILE* file); + ~output_redirect() noexcept; + + output_redirect(const output_redirect&) = delete; + void operator=(const output_redirect&) = delete; + + // Restores the original file, reads output from the pipe into a string + // and returns it. + std::string restore_and_read(); +}; + +# define FMT_TEST_WRITE_(statement, expected_output, file, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \ + std::string gtest_expected_output = expected_output; \ + output_redirect gtest_redir(file); \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + std::string gtest_output = gtest_redir.restore_and_read(); \ + if (gtest_output != gtest_expected_output) { \ + gtest_ar << #statement " produces different output.\n" \ + << "Expected: " << gtest_expected_output << "\n" \ + << " Actual: " << gtest_output; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \ + : fail(gtest_ar.failure_message()) + +// Tests that the statement writes the expected output to file. +# define EXPECT_WRITE(file, statement, expected_output) \ + FMT_TEST_WRITE_(statement, expected_output, file, GTEST_NONFATAL_FAILURE_) + +# ifdef _MSC_VER + +// Suppresses Windows assertions on invalid file descriptors, making +// POSIX functions return proper error codes instead of crashing on Windows. +class suppress_assert { + private: + _invalid_parameter_handler original_handler_; + int original_report_mode_; + + static void handle_invalid_parameter(const wchar_t*, const wchar_t*, + const wchar_t*, unsigned, uintptr_t) {} + + public: + suppress_assert() + : original_handler_( + _set_invalid_parameter_handler(handle_invalid_parameter)), + original_report_mode_(_CrtSetReportMode(_CRT_ASSERT, 0)) {} + ~suppress_assert() { + _set_invalid_parameter_handler(original_handler_); + _CrtSetReportMode(_CRT_ASSERT, original_report_mode_); + (void)original_report_mode_; + } +}; + +# define SUPPRESS_ASSERT(statement) \ + { \ + suppress_assert sa; \ + statement; \ + } +# else +# define SUPPRESS_ASSERT(statement) statement +# endif // _MSC_VER + +# define EXPECT_SYSTEM_ERROR_NOASSERT(statement, error_code, message) \ + EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message) + +// Attempts to read count characters from a file. +std::string read(fmt::file& f, size_t count); + +# define EXPECT_READ(file, expected_content) \ + EXPECT_EQ(expected_content, \ + read(file, fmt::string_view(expected_content).size())) + +#else +# define EXPECT_WRITE(file, statement, expected_output) \ + do { \ + (void)(file); \ + (void)(statement); \ + (void)(expected_output); \ + SUCCEED(); \ + } while (false) +#endif // FMT_USE_FCNTL + +#endif // FMT_GTEST_EXTRA_H_ diff --git a/src/fmt/test/gtest/.clang-format b/src/fmt/test/gtest/.clang-format new file mode 100644 index 000000000..ec09b9b6b --- /dev/null +++ b/src/fmt/test/gtest/.clang-format @@ -0,0 +1,3 @@ +# Disable clang-format here +DisableFormat: true +SortIncludes: Never diff --git a/src/fmt/test/gtest/CMakeLists.txt b/src/fmt/test/gtest/CMakeLists.txt new file mode 100644 index 000000000..ed0e59d5f --- /dev/null +++ b/src/fmt/test/gtest/CMakeLists.txt @@ -0,0 +1,38 @@ +#------------------------------------------------------------------------------ +# Build the google test library + +# We compile Google Test ourselves instead of using pre-compiled libraries. +# See the Google Test FAQ "Why is it not recommended to install a +# pre-compiled copy of Google Test (for example, into /usr/local)?" +# at http://code.google.com/p/googletest/wiki/FAQ for more details. +add_library(gtest STATIC + gmock-gtest-all.cc gmock/gmock.h gtest/gtest.h gtest/gtest-spi.h) +target_compile_definitions(gtest PUBLIC GTEST_HAS_STD_WSTRING=1) +target_include_directories(gtest SYSTEM PUBLIC .) + +find_package(Threads) +if (Threads_FOUND) + target_link_libraries(gtest ${CMAKE_THREAD_LIBS_INIT}) +else () + target_compile_definitions(gtest PUBLIC GTEST_HAS_PTHREAD=0) +endif () + +# Workaround GTest bug https://github.com/google/googletest/issues/705. +fmt_check_cxx_compiler_flag( + -fno-delete-null-pointer-checks HAVE_FNO_DELETE_NULL_POINTER_CHECKS) +if (HAVE_FNO_DELETE_NULL_POINTER_CHECKS) + target_compile_options(gtest PUBLIC -fno-delete-null-pointer-checks) +endif () + +if (MSVC) + # Disable MSVC warnings of _CRT_INSECURE_DEPRECATE functions. + target_compile_definitions(gtest PRIVATE _CRT_SECURE_NO_WARNINGS) + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # Disable MSVC warnings of POSIX functions. + target_compile_options(gtest PUBLIC -Wno-deprecated-declarations) + endif () +endif () + +# Silence MSVC tr1 deprecation warning in gmock. +target_compile_definitions(gtest + PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=1) diff --git a/src/fmt/test/gtest/gmock-gtest-all.cc b/src/fmt/test/gtest/gmock-gtest-all.cc new file mode 100644 index 000000000..439ebc621 --- /dev/null +++ b/src/fmt/test/gtest/gmock-gtest-all.cc @@ -0,0 +1,14434 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// +// Google C++ Testing and Mocking Framework (Google Test) +// +// Sometimes it's desirable to build Google Test by compiling a single file. +// This file serves this purpose. + +// This line ensures that gtest.h can be compiled on its own, even +// when it's fused. +#include "gtest/gtest.h" + +// The following lines pull in the real gtest *.cc files. +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// +// The Google C++ Testing and Mocking Framework (Google Test) + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +// GOOGLETEST_CM0004 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ + + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// This helper class can be used to mock out Google Test failure reporting +// so that we can test Google Test or code that builds on Google Test. +// +// An object of this class appends a TestPartResult object to the +// TestPartResultArray object given in the constructor whenever a Google Test +// failure is reported. It can either intercept only failures that are +// generated in the same thread that created this object or it can intercept +// all generated failures. The scope of this mock object can be controlled with +// the second argument to the two arguments constructor. +class GTEST_API_ ScopedFakeTestPartResultReporter + : public TestPartResultReporterInterface { + public: + // The two possible mocking modes of this object. + enum InterceptMode { + INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. + INTERCEPT_ALL_THREADS // Intercepts all failures. + }; + + // The c'tor sets this object as the test part result reporter used + // by Google Test. The 'result' parameter specifies where to report the + // results. This reporter will only catch failures generated in the current + // thread. DEPRECATED + explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); + + // Same as above, but you can choose the interception scope of this object. + ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, + TestPartResultArray* result); + + // The d'tor restores the previous test part result reporter. + ~ScopedFakeTestPartResultReporter() override; + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + void ReportTestPartResult(const TestPartResult& result) override; + + private: + void Init(); + + const InterceptMode intercept_mode_; + TestPartResultReporterInterface* old_reporter_; + TestPartResultArray* const result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter); +}; + +namespace internal { + +// A helper class for implementing EXPECT_FATAL_FAILURE() and +// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +class GTEST_API_ SingleFailureChecker { + public: + // The constructor remembers the arguments. + SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, const std::string& substr); + ~SingleFailureChecker(); + private: + const TestPartResultArray* const results_; + const TestPartResult::Type type_; + const std::string substr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); +}; + +} // namespace internal + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// A set of macros for testing Google Test assertions or code that's expected +// to generate Google Test fatal failures. It verifies that the given +// statement will cause exactly one fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_FATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - 'statement' cannot reference local non-static variables or +// non-static members of the current object. +// - 'statement' cannot return a value. +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. The AcceptsMacroThatExpandsToUnprotectedComma test in +// gtest_unittest.cc will fail to compile if we do that. +#define EXPECT_FATAL_FAILURE(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ALL_THREADS, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test non-fatal failures. It asserts that the given +// statement will cause exactly one non-fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// 'statement' is allowed to reference local variables and members of +// the current object. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. If we do that, the code won't compile when the user gives +// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that +// expands to code containing an unprotected comma. The +// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc +// catches that. +// +// For the same reason, we have to write +// if (::testing::internal::AlwaysTrue()) { statement; } +// instead of +// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) +// to avoid an MSVC warning on unreachable code. +#define EXPECT_NONFATAL_FAILURE(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ + >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <wchar.h> +#include <wctype.h> + +#include <algorithm> +#include <chrono> // NOLINT +#include <cmath> +#include <cstdint> +#include <iomanip> +#include <limits> +#include <list> +#include <map> +#include <ostream> // NOLINT +#include <sstream> +#include <vector> + +#if GTEST_OS_LINUX + +# include <fcntl.h> // NOLINT +# include <limits.h> // NOLINT +# include <sched.h> // NOLINT +// Declares vsnprintf(). This header is not available on Windows. +# include <strings.h> // NOLINT +# include <sys/mman.h> // NOLINT +# include <sys/time.h> // NOLINT +# include <unistd.h> // NOLINT +# include <string> + +#elif GTEST_OS_ZOS +# include <sys/time.h> // NOLINT + +// On z/OS we additionally need strings.h for strcasecmp. +# include <strings.h> // NOLINT + +#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE. + +# include <windows.h> // NOLINT +# undef min + +#elif GTEST_OS_WINDOWS // We are on Windows proper. + +# include <windows.h> // NOLINT +# undef min + +#ifdef _MSC_VER +# include <crtdbg.h> // NOLINT +#endif + +# include <io.h> // NOLINT +# include <sys/timeb.h> // NOLINT +# include <sys/types.h> // NOLINT +# include <sys/stat.h> // NOLINT + +# if GTEST_OS_WINDOWS_MINGW +# include <sys/time.h> // NOLINT +# endif // GTEST_OS_WINDOWS_MINGW + +#else + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include <sys/time.h> // NOLINT +# include <unistd.h> // NOLINT + +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include <stdexcept> +#endif + +#if GTEST_CAN_STREAM_RESULTS_ +# include <arpa/inet.h> // NOLINT +# include <netdb.h> // NOLINT +# include <sys/socket.h> // NOLINT +# include <sys/types.h> // NOLINT +#endif + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// Utility functions and classes used by the Google C++ testing framework.// +// This file contains purely Google Test's internal implementation. Please +// DO NOT #INCLUDE IT IN A USER PROGRAM. + +#ifndef GOOGLETEST_SRC_GTEST_INTERNAL_INL_H_ +#define GOOGLETEST_SRC_GTEST_INTERNAL_INL_H_ + +#ifndef _WIN32_WCE +# include <errno.h> +#endif // !_WIN32_WCE +#include <stddef.h> +#include <stdlib.h> // For strtoll/_strtoul64/malloc/free. +#include <string.h> // For memmove. + +#include <algorithm> +#include <cstdint> +#include <memory> +#include <string> +#include <vector> + + +#if GTEST_CAN_STREAM_RESULTS_ +# include <arpa/inet.h> // NOLINT +# include <netdb.h> // NOLINT +#endif + +#if GTEST_OS_WINDOWS +# include <windows.h> // NOLINT +#endif // GTEST_OS_WINDOWS + + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// Declares the flags. +// +// We don't want the users to modify this flag in the code, but want +// Google Test's own unit tests to be able to access it. Therefore we +// declare it here as opposed to in gtest.h. +GTEST_DECLARE_bool_(death_test_use_fork); + +namespace internal { + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest; + +// Names of the flags (needed for parsing Google Test flags). +const char kAlsoRunDisabledTestsFlag[] = "also_run_disabled_tests"; +const char kBreakOnFailureFlag[] = "break_on_failure"; +const char kCatchExceptionsFlag[] = "catch_exceptions"; +const char kColorFlag[] = "color"; +const char kFailFast[] = "fail_fast"; +const char kFilterFlag[] = "filter"; +const char kListTestsFlag[] = "list_tests"; +const char kOutputFlag[] = "output"; +const char kBriefFlag[] = "brief"; +const char kPrintTimeFlag[] = "print_time"; +const char kPrintUTF8Flag[] = "print_utf8"; +const char kRandomSeedFlag[] = "random_seed"; +const char kRepeatFlag[] = "repeat"; +const char kShuffleFlag[] = "shuffle"; +const char kStackTraceDepthFlag[] = "stack_trace_depth"; +const char kStreamResultToFlag[] = "stream_result_to"; +const char kThrowOnFailureFlag[] = "throw_on_failure"; +const char kFlagfileFlag[] = "flagfile"; + +// A valid random seed must be in [1, kMaxRandomSeed]. +const int kMaxRandomSeed = 99999; + +// g_help_flag is true if and only if the --help flag or an equivalent form +// is specified on the command line. +GTEST_API_ extern bool g_help_flag; + +// Returns the current time in milliseconds. +GTEST_API_ TimeInMillis GetTimeInMillis(); + +// Returns true if and only if Google Test should use colors in the output. +GTEST_API_ bool ShouldUseColor(bool stdout_is_tty); + +// Formats the given time in milliseconds as seconds. +GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms); + +// Converts the given time in milliseconds to a date string in the ISO 8601 +// format, without the timezone information. N.B.: due to the use the +// non-reentrant localtime() function, this function is not thread safe. Do +// not use it in any code that can be called from multiple threads. +GTEST_API_ std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms); + +// Parses a string for an Int32 flag, in the form of "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +GTEST_API_ bool ParseInt32Flag( + const char* str, const char* flag, int32_t* value); + +// Returns a random seed in range [1, kMaxRandomSeed] based on the +// given --gtest_random_seed flag value. +inline int GetRandomSeedFromFlag(int32_t random_seed_flag) { + const unsigned int raw_seed = (random_seed_flag == 0) ? + static_cast<unsigned int>(GetTimeInMillis()) : + static_cast<unsigned int>(random_seed_flag); + + // Normalizes the actual seed to range [1, kMaxRandomSeed] such that + // it's easy to type. + const int normalized_seed = + static_cast<int>((raw_seed - 1U) % + static_cast<unsigned int>(kMaxRandomSeed)) + 1; + return normalized_seed; +} + +// Returns the first valid random seed after 'seed'. The behavior is +// undefined if 'seed' is invalid. The seed after kMaxRandomSeed is +// considered to be 1. +inline int GetNextRandomSeed(int seed) { + GTEST_CHECK_(1 <= seed && seed <= kMaxRandomSeed) + << "Invalid random seed " << seed << " - must be in [1, " + << kMaxRandomSeed << "]."; + const int next_seed = seed + 1; + return (next_seed > kMaxRandomSeed) ? 1 : next_seed; +} + +// This class saves the values of all Google Test flags in its c'tor, and +// restores them in its d'tor. +class GTestFlagSaver { + public: + // The c'tor. + GTestFlagSaver() { + also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests); + break_on_failure_ = GTEST_FLAG(break_on_failure); + catch_exceptions_ = GTEST_FLAG(catch_exceptions); + color_ = GTEST_FLAG(color); + death_test_style_ = GTEST_FLAG(death_test_style); + death_test_use_fork_ = GTEST_FLAG(death_test_use_fork); + fail_fast_ = GTEST_FLAG(fail_fast); + filter_ = GTEST_FLAG(filter); + internal_run_death_test_ = GTEST_FLAG(internal_run_death_test); + list_tests_ = GTEST_FLAG(list_tests); + output_ = GTEST_FLAG(output); + brief_ = GTEST_FLAG(brief); + print_time_ = GTEST_FLAG(print_time); + print_utf8_ = GTEST_FLAG(print_utf8); + random_seed_ = GTEST_FLAG(random_seed); + repeat_ = GTEST_FLAG(repeat); + shuffle_ = GTEST_FLAG(shuffle); + stack_trace_depth_ = GTEST_FLAG(stack_trace_depth); + stream_result_to_ = GTEST_FLAG(stream_result_to); + throw_on_failure_ = GTEST_FLAG(throw_on_failure); + } + + // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS. + ~GTestFlagSaver() { + GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_; + GTEST_FLAG(break_on_failure) = break_on_failure_; + GTEST_FLAG(catch_exceptions) = catch_exceptions_; + GTEST_FLAG(color) = color_; + GTEST_FLAG(death_test_style) = death_test_style_; + GTEST_FLAG(death_test_use_fork) = death_test_use_fork_; + GTEST_FLAG(filter) = filter_; + GTEST_FLAG(fail_fast) = fail_fast_; + GTEST_FLAG(internal_run_death_test) = internal_run_death_test_; + GTEST_FLAG(list_tests) = list_tests_; + GTEST_FLAG(output) = output_; + GTEST_FLAG(brief) = brief_; + GTEST_FLAG(print_time) = print_time_; + GTEST_FLAG(print_utf8) = print_utf8_; + GTEST_FLAG(random_seed) = random_seed_; + GTEST_FLAG(repeat) = repeat_; + GTEST_FLAG(shuffle) = shuffle_; + GTEST_FLAG(stack_trace_depth) = stack_trace_depth_; + GTEST_FLAG(stream_result_to) = stream_result_to_; + GTEST_FLAG(throw_on_failure) = throw_on_failure_; + } + + private: + // Fields for saving the original values of flags. + bool also_run_disabled_tests_; + bool break_on_failure_; + bool catch_exceptions_; + std::string color_; + std::string death_test_style_; + bool death_test_use_fork_; + bool fail_fast_; + std::string filter_; + std::string internal_run_death_test_; + bool list_tests_; + std::string output_; + bool brief_; + bool print_time_; + bool print_utf8_; + int32_t random_seed_; + int32_t repeat_; + bool shuffle_; + int32_t stack_trace_depth_; + std::string stream_result_to_; + bool throw_on_failure_; +} GTEST_ATTRIBUTE_UNUSED_; + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +GTEST_API_ std::string CodePointToUtf8(uint32_t code_point); + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +GTEST_API_ std::string WideStringToUtf8(const wchar_t* str, int num_chars); + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded(); + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (e.g., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +GTEST_API_ bool ShouldShard(const char* total_shards_str, + const char* shard_index_str, + bool in_subprocess_for_death_test); + +// Parses the environment variable var as a 32-bit integer. If it is unset, +// returns default_val. If it is not a 32-bit integer, prints an error and +// and aborts. +GTEST_API_ int32_t Int32FromEnvOrDie(const char* env_var, int32_t default_val); + +// Given the total number of shards, the shard index, and the test id, +// returns true if and only if the test should be run on this shard. The test id +// is some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +GTEST_API_ bool ShouldRunTestOnShard( + int total_shards, int shard_index, int test_id); + +// STL container utilities. + +// Returns the number of elements in the given container that satisfy +// the given predicate. +template <class Container, typename Predicate> +inline int CountIf(const Container& c, Predicate predicate) { + // Implemented as an explicit loop since std::count_if() in libCstd on + // Solaris has a non-standard signature. + int count = 0; + for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) { + if (predicate(*it)) + ++count; + } + return count; +} + +// Applies a function/functor to each element in the container. +template <class Container, typename Functor> +void ForEach(const Container& c, Functor functor) { + std::for_each(c.begin(), c.end(), functor); +} + +// Returns the i-th element of the vector, or default_value if i is not +// in range [0, v.size()). +template <typename E> +inline E GetElementOr(const std::vector<E>& v, int i, E default_value) { + return (i < 0 || i >= static_cast<int>(v.size())) ? default_value + : v[static_cast<size_t>(i)]; +} + +// Performs an in-place shuffle of a range of the vector's elements. +// 'begin' and 'end' are element indices as an STL-style range; +// i.e. [begin, end) are shuffled, where 'end' == size() means to +// shuffle to the end of the vector. +template <typename E> +void ShuffleRange(internal::Random* random, int begin, int end, + std::vector<E>* v) { + const int size = static_cast<int>(v->size()); + GTEST_CHECK_(0 <= begin && begin <= size) + << "Invalid shuffle range start " << begin << ": must be in range [0, " + << size << "]."; + GTEST_CHECK_(begin <= end && end <= size) + << "Invalid shuffle range finish " << end << ": must be in range [" + << begin << ", " << size << "]."; + + // Fisher-Yates shuffle, from + // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle + for (int range_width = end - begin; range_width >= 2; range_width--) { + const int last_in_range = begin + range_width - 1; + const int selected = + begin + + static_cast<int>(random->Generate(static_cast<uint32_t>(range_width))); + std::swap((*v)[static_cast<size_t>(selected)], + (*v)[static_cast<size_t>(last_in_range)]); + } +} + +// Performs an in-place shuffle of the vector's elements. +template <typename E> +inline void Shuffle(internal::Random* random, std::vector<E>* v) { + ShuffleRange(random, 0, static_cast<int>(v->size()), v); +} + +// A function for deleting an object. Handy for being used as a +// functor. +template <typename T> +static void Delete(T* x) { + delete x; +} + +// A predicate that checks the key of a TestProperty against a known key. +// +// TestPropertyKeyIs is copyable. +class TestPropertyKeyIs { + public: + // Constructor. + // + // TestPropertyKeyIs has NO default constructor. + explicit TestPropertyKeyIs(const std::string& key) : key_(key) {} + + // Returns true if and only if the test name of test property matches on key_. + bool operator()(const TestProperty& test_property) const { + return test_property.key() == key_; + } + + private: + std::string key_; +}; + +// Class UnitTestOptions. +// +// This class contains functions for processing options the user +// specifies when running the tests. It has only static members. +// +// In most cases, the user can specify an option using either an +// environment variable or a command line flag. E.g. you can set the +// test filter using either GTEST_FILTER or --gtest_filter. If both +// the variable and the flag are present, the latter overrides the +// former. +class GTEST_API_ UnitTestOptions { + public: + // Functions for processing the gtest_output flag. + + // Returns the output format, or "" for normal printed output. + static std::string GetOutputFormat(); + + // Returns the absolute path of the requested output file, or the + // default (test_detail.xml in the original working directory) if + // none was explicitly specified. + static std::string GetAbsolutePathToOutputFile(); + + // Functions for processing the gtest_filter flag. + + // Returns true if and only if the user-specified filter matches the test + // suite name and the test name. + static bool FilterMatchesTest(const std::string& test_suite_name, + const std::string& test_name); + +#if GTEST_OS_WINDOWS + // Function for supporting the gtest_catch_exception flag. + + // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the + // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. + // This function is useful as an __except condition. + static int GTestShouldProcessSEH(DWORD exception_code); +#endif // GTEST_OS_WINDOWS + + // Returns true if "name" matches the ':' separated list of glob-style + // filters in "filter". + static bool MatchesFilter(const std::string& name, const char* filter); +}; + +// Returns the current application's name, removing directory path if that +// is present. Used by UnitTestOptions::GetOutputFile. +GTEST_API_ FilePath GetCurrentExecutableName(); + +// The role interface for getting the OS stack trace as a string. +class OsStackTraceGetterInterface { + public: + OsStackTraceGetterInterface() {} + virtual ~OsStackTraceGetterInterface() {} + + // Returns the current OS stack trace as an std::string. Parameters: + // + // max_depth - the maximum number of stack frames to be included + // in the trace. + // skip_count - the number of top frames to be skipped; doesn't count + // against max_depth. + virtual std::string CurrentStackTrace(int max_depth, int skip_count) = 0; + + // UponLeavingGTest() should be called immediately before Google Test calls + // user code. It saves some information about the current stack that + // CurrentStackTrace() will use to find and hide Google Test stack frames. + virtual void UponLeavingGTest() = 0; + + // This string is inserted in place of stack frames that are part of + // Google Test's implementation. + static const char* const kElidedFramesMarker; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface); +}; + +// A working implementation of the OsStackTraceGetterInterface interface. +class OsStackTraceGetter : public OsStackTraceGetterInterface { + public: + OsStackTraceGetter() {} + + std::string CurrentStackTrace(int max_depth, int skip_count) override; + void UponLeavingGTest() override; + + private: +#if GTEST_HAS_ABSL + Mutex mutex_; // Protects all internal state. + + // We save the stack frame below the frame that calls user code. + // We do this because the address of the frame immediately below + // the user code changes between the call to UponLeavingGTest() + // and any calls to the stack trace code from within the user code. + void* caller_frame_ = nullptr; +#endif // GTEST_HAS_ABSL + + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter); +}; + +// Information about a Google Test trace point. +struct TraceInfo { + const char* file; + int line; + std::string message; +}; + +// This is the default global test part result reporter used in UnitTestImpl. +// This class should only be used by UnitTestImpl. +class DefaultGlobalTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. Reports the test part + // result in the current test. + void ReportTestPartResult(const TestPartResult& result) override; + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultGlobalTestPartResultReporter); +}; + +// This is the default per thread test part result reporter used in +// UnitTestImpl. This class should only be used by UnitTestImpl. +class DefaultPerThreadTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. The implementation just + // delegates to the current global test part result reporter of *unit_test_. + void ReportTestPartResult(const TestPartResult& result) override; + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultPerThreadTestPartResultReporter); +}; + +// The private implementation of the UnitTest class. We don't protect +// the methods under a mutex, as this class is not accessible by a +// user and the UnitTest class that delegates work to this class does +// proper locking. +class GTEST_API_ UnitTestImpl { + public: + explicit UnitTestImpl(UnitTest* parent); + virtual ~UnitTestImpl(); + + // There are two different ways to register your own TestPartResultReporter. + // You can register your own repoter to listen either only for test results + // from the current thread or for results from all threads. + // By default, each per-thread test result repoter just passes a new + // TestPartResult to the global test result reporter, which registers the + // test part result for the currently running test. + + // Returns the global test part result reporter. + TestPartResultReporterInterface* GetGlobalTestPartResultReporter(); + + // Sets the global test part result reporter. + void SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter); + + // Returns the test part result reporter for the current thread. + TestPartResultReporterInterface* GetTestPartResultReporterForCurrentThread(); + + // Sets the test part result reporter for the current thread. + void SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter); + + // Gets the number of successful test suites. + int successful_test_suite_count() const; + + // Gets the number of failed test suites. + int failed_test_suite_count() const; + + // Gets the number of all test suites. + int total_test_suite_count() const; + + // Gets the number of all test suites that contain at least one test + // that should run. + int test_suite_to_run_count() const; + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of skipped tests. + int skipped_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns true if and only if the unit test passed (i.e. all test suites + // passed). + bool Passed() const { return !Failed(); } + + // Returns true if and only if the unit test failed (i.e. some test suite + // failed or something outside of all tests failed). + bool Failed() const { + return failed_test_suite_count() > 0 || ad_hoc_test_result()->Failed(); + } + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + const TestSuite* GetTestSuite(int i) const { + const int index = GetElementOr(test_suite_indices_, i, -1); + return index < 0 ? nullptr : test_suites_[static_cast<size_t>(i)]; + } + + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const TestCase* GetTestCase(int i) const { return GetTestSuite(i); } +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + TestSuite* GetMutableSuiteCase(int i) { + const int index = GetElementOr(test_suite_indices_, i, -1); + return index < 0 ? nullptr : test_suites_[static_cast<size_t>(index)]; + } + + // Provides access to the event listener list. + TestEventListeners* listeners() { return &listeners_; } + + // Returns the TestResult for the test that's currently running, or + // the TestResult for the ad hoc test if no test is running. + TestResult* current_test_result(); + + // Returns the TestResult for the ad hoc test. + const TestResult* ad_hoc_test_result() const { return &ad_hoc_test_result_; } + + // Sets the OS stack trace getter. + // + // Does nothing if the input and the current OS stack trace getter + // are the same; otherwise, deletes the old getter and makes the + // input the current getter. + void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter); + + // Returns the current OS stack trace getter if it is not NULL; + // otherwise, creates an OsStackTraceGetter, makes it the current + // getter, and returns it. + OsStackTraceGetterInterface* os_stack_trace_getter(); + + // Returns the current OS stack trace as an std::string. + // + // The maximum number of stack frames to be included is specified by + // the gtest_stack_trace_depth flag. The skip_count parameter + // specifies the number of top frames to be skipped, which doesn't + // count against the number of frames to be included. + // + // For example, if Foo() calls Bar(), which in turn calls + // CurrentOsStackTraceExceptTop(1), Foo() will be included in the + // trace but Bar() and CurrentOsStackTraceExceptTop() won't. + std::string CurrentOsStackTraceExceptTop(int skip_count) GTEST_NO_INLINE_; + + // Finds and returns a TestSuite with the given name. If one doesn't + // exist, creates one and returns it. + // + // Arguments: + // + // test_suite_name: name of the test suite + // type_param: the name of the test's type parameter, or NULL if + // this is not a typed or a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test suite + // tear_down_tc: pointer to the function that tears down the test suite + TestSuite* GetTestSuite(const char* test_suite_name, const char* type_param, + internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc); + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + TestCase* GetTestCase(const char* test_case_name, const char* type_param, + internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc) { + return GetTestSuite(test_case_name, type_param, set_up_tc, tear_down_tc); + } +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Adds a TestInfo to the unit test. + // + // Arguments: + // + // set_up_tc: pointer to the function that sets up the test suite + // tear_down_tc: pointer to the function that tears down the test suite + // test_info: the TestInfo object + void AddTestInfo(internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc, + TestInfo* test_info) { +#if GTEST_HAS_DEATH_TEST + // In order to support thread-safe death tests, we need to + // remember the original working directory when the test program + // was first invoked. We cannot do this in RUN_ALL_TESTS(), as + // the user may have changed the current directory before calling + // RUN_ALL_TESTS(). Therefore we capture the current directory in + // AddTestInfo(), which is called to register a TEST or TEST_F + // before main() is reached. + if (original_working_dir_.IsEmpty()) { + original_working_dir_.Set(FilePath::GetCurrentDir()); + GTEST_CHECK_(!original_working_dir_.IsEmpty()) + << "Failed to get the current working directory."; + } +#endif // GTEST_HAS_DEATH_TEST + + GetTestSuite(test_info->test_suite_name(), test_info->type_param(), + set_up_tc, tear_down_tc) + ->AddTestInfo(test_info); + } + + // Returns ParameterizedTestSuiteRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + internal::ParameterizedTestSuiteRegistry& parameterized_test_registry() { + return parameterized_test_registry_; + } + + std::set<std::string>* ignored_parameterized_test_suites() { + return &ignored_parameterized_test_suites_; + } + + // Returns TypeParameterizedTestSuiteRegistry object used to keep track of + // type-parameterized tests and instantiations of them. + internal::TypeParameterizedTestSuiteRegistry& + type_parameterized_test_registry() { + return type_parameterized_test_registry_; + } + + // Sets the TestSuite object for the test that's currently running. + void set_current_test_suite(TestSuite* a_current_test_suite) { + current_test_suite_ = a_current_test_suite; + } + + // Sets the TestInfo object for the test that's currently running. If + // current_test_info is NULL, the assertion results will be stored in + // ad_hoc_test_result_. + void set_current_test_info(TestInfo* a_current_test_info) { + current_test_info_ = a_current_test_info; + } + + // Registers all parameterized tests defined using TEST_P and + // INSTANTIATE_TEST_SUITE_P, creating regular tests for each test/parameter + // combination. This method can be called more then once; it has guards + // protecting from registering the tests more then once. If + // value-parameterized tests are disabled, RegisterParameterizedTests is + // present but does nothing. + void RegisterParameterizedTests(); + + // Runs all tests in this UnitTest object, prints the result, and + // returns true if all tests are successful. If any exception is + // thrown during a test, this test is considered to be failed, but + // the rest of the tests will still be run. + bool RunAllTests(); + + // Clears the results of all tests, except the ad hoc tests. + void ClearNonAdHocTestResult() { + ForEach(test_suites_, TestSuite::ClearTestSuiteResult); + } + + // Clears the results of ad-hoc test assertions. + void ClearAdHocTestResult() { + ad_hoc_test_result_.Clear(); + } + + // Adds a TestProperty to the current TestResult object when invoked in a + // context of a test or a test suite, or to the global property set. If the + // result already contains a property with the same key, the value will be + // updated. + void RecordProperty(const TestProperty& test_property); + + enum ReactionToSharding { + HONOR_SHARDING_PROTOCOL, + IGNORE_SHARDING_PROTOCOL + }; + + // Matches the full name of each test against the user-specified + // filter to decide whether the test should run, then records the + // result in each TestSuite and TestInfo object. + // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests + // based on sharding variables in the environment. + // Returns the number of tests that should run. + int FilterTests(ReactionToSharding shard_tests); + + // Prints the names of the tests matching the user-specified filter flag. + void ListTestsMatchingFilter(); + + const TestSuite* current_test_suite() const { return current_test_suite_; } + TestInfo* current_test_info() { return current_test_info_; } + const TestInfo* current_test_info() const { return current_test_info_; } + + // Returns the vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector<Environment*>& environments() { return environments_; } + + // Getters for the per-thread Google Test trace stack. + std::vector<TraceInfo>& gtest_trace_stack() { + return *(gtest_trace_stack_.pointer()); + } + const std::vector<TraceInfo>& gtest_trace_stack() const { + return gtest_trace_stack_.get(); + } + +#if GTEST_HAS_DEATH_TEST + void InitDeathTestSubprocessControlInfo() { + internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag()); + } + // Returns a pointer to the parsed --gtest_internal_run_death_test + // flag, or NULL if that flag was not specified. + // This information is useful only in a death test child process. + // Must not be called before a call to InitGoogleTest. + const InternalRunDeathTestFlag* internal_run_death_test_flag() const { + return internal_run_death_test_flag_.get(); + } + + // Returns a pointer to the current death test factory. + internal::DeathTestFactory* death_test_factory() { + return death_test_factory_.get(); + } + + void SuppressTestEventsIfInSubprocess(); + + friend class ReplaceDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + + // Initializes the event listener performing XML output as specified by + // UnitTestOptions. Must not be called before InitGoogleTest. + void ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Initializes the event listener for streaming test results to a socket. + // Must not be called before InitGoogleTest. + void ConfigureStreamingOutput(); +#endif + + // Performs initialization dependent upon flag values obtained in + // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to + // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest + // this function is also called from RunAllTests. Since this function can be + // called more than once, it has to be idempotent. + void PostFlagParsingInit(); + + // Gets the random seed used at the start of the current test iteration. + int random_seed() const { return random_seed_; } + + // Gets the random number generator. + internal::Random* random() { return &random_; } + + // Shuffles all test suites, and the tests within each test suite, + // making sure that death tests are still run first. + void ShuffleTests(); + + // Restores the test suites and tests to their order before the first shuffle. + void UnshuffleTests(); + + // Returns the value of GTEST_FLAG(catch_exceptions) at the moment + // UnitTest::Run() starts. + bool catch_exceptions() const { return catch_exceptions_; } + + private: + friend class ::testing::UnitTest; + + // Used by UnitTest::Run() to capture the state of + // GTEST_FLAG(catch_exceptions) at the moment it starts. + void set_catch_exceptions(bool value) { catch_exceptions_ = value; } + + // The UnitTest object that owns this implementation object. + UnitTest* const parent_; + + // The working directory when the first TEST() or TEST_F() was + // executed. + internal::FilePath original_working_dir_; + + // The default test part result reporters. + DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_; + DefaultPerThreadTestPartResultReporter + default_per_thread_test_part_result_reporter_; + + // Points to (but doesn't own) the global test part result reporter. + TestPartResultReporterInterface* global_test_part_result_repoter_; + + // Protects read and write access to global_test_part_result_reporter_. + internal::Mutex global_test_part_result_reporter_mutex_; + + // Points to (but doesn't own) the per-thread test part result reporter. + internal::ThreadLocal<TestPartResultReporterInterface*> + per_thread_test_part_result_reporter_; + + // The vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector<Environment*> environments_; + + // The vector of TestSuites in their original order. It owns the + // elements in the vector. + std::vector<TestSuite*> test_suites_; + + // Provides a level of indirection for the test suite list to allow + // easy shuffling and restoring the test suite order. The i-th + // element of this vector is the index of the i-th test suite in the + // shuffled order. + std::vector<int> test_suite_indices_; + + // ParameterizedTestRegistry object used to register value-parameterized + // tests. + internal::ParameterizedTestSuiteRegistry parameterized_test_registry_; + internal::TypeParameterizedTestSuiteRegistry + type_parameterized_test_registry_; + + // The set holding the name of parameterized + // test suites that may go uninstantiated. + std::set<std::string> ignored_parameterized_test_suites_; + + // Indicates whether RegisterParameterizedTests() has been called already. + bool parameterized_tests_registered_; + + // Index of the last death test suite registered. Initially -1. + int last_death_test_suite_; + + // This points to the TestSuite for the currently running test. It + // changes as Google Test goes through one test suite after another. + // When no test is running, this is set to NULL and Google Test + // stores assertion results in ad_hoc_test_result_. Initially NULL. + TestSuite* current_test_suite_; + + // This points to the TestInfo for the currently running test. It + // changes as Google Test goes through one test after another. When + // no test is running, this is set to NULL and Google Test stores + // assertion results in ad_hoc_test_result_. Initially NULL. + TestInfo* current_test_info_; + + // Normally, a user only writes assertions inside a TEST or TEST_F, + // or inside a function called by a TEST or TEST_F. Since Google + // Test keeps track of which test is current running, it can + // associate such an assertion with the test it belongs to. + // + // If an assertion is encountered when no TEST or TEST_F is running, + // Google Test attributes the assertion result to an imaginary "ad hoc" + // test, and records the result in ad_hoc_test_result_. + TestResult ad_hoc_test_result_; + + // The list of event listeners that can be used to track events inside + // Google Test. + TestEventListeners listeners_; + + // The OS stack trace getter. Will be deleted when the UnitTest + // object is destructed. By default, an OsStackTraceGetter is used, + // but the user can set this field to use a custom getter if that is + // desired. + OsStackTraceGetterInterface* os_stack_trace_getter_; + + // True if and only if PostFlagParsingInit() has been called. + bool post_flag_parse_init_performed_; + + // The random number seed used at the beginning of the test run. + int random_seed_; + + // Our random number generator. + internal::Random random_; + + // The time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp_; + + // How long the test took to run, in milliseconds. + TimeInMillis elapsed_time_; + +#if GTEST_HAS_DEATH_TEST + // The decomposed components of the gtest_internal_run_death_test flag, + // parsed when RUN_ALL_TESTS is called. + std::unique_ptr<InternalRunDeathTestFlag> internal_run_death_test_flag_; + std::unique_ptr<internal::DeathTestFactory> death_test_factory_; +#endif // GTEST_HAS_DEATH_TEST + + // A per-thread stack of traces created by the SCOPED_TRACE() macro. + internal::ThreadLocal<std::vector<TraceInfo> > gtest_trace_stack_; + + // The value of GTEST_FLAG(catch_exceptions) at the moment RunAllTests() + // starts. + bool catch_exceptions_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl); +}; // class UnitTestImpl + +// Convenience function for accessing the global UnitTest +// implementation object. +inline UnitTestImpl* GetUnitTestImpl() { + return UnitTest::GetInstance()->impl(); +} + +#if GTEST_USES_SIMPLE_RE + +// Internal helper functions for implementing the simple regular +// expression matcher. +GTEST_API_ bool IsInSet(char ch, const char* str); +GTEST_API_ bool IsAsciiDigit(char ch); +GTEST_API_ bool IsAsciiPunct(char ch); +GTEST_API_ bool IsRepeat(char ch); +GTEST_API_ bool IsAsciiWhiteSpace(char ch); +GTEST_API_ bool IsAsciiWordChar(char ch); +GTEST_API_ bool IsValidEscape(char ch); +GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch); +GTEST_API_ bool ValidateRegex(const char* regex); +GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str); +GTEST_API_ bool MatchRepetitionAndRegexAtHead( + bool escaped, char ch, char repeat, const char* regex, const char* str); +GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str); + +#endif // GTEST_USES_SIMPLE_RE + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv); +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv); + +#if GTEST_HAS_DEATH_TEST + +// Returns the message describing the last system error, regardless of the +// platform. +GTEST_API_ std::string GetLastErrnoDescription(); + +// Attempts to parse a string into a positive integer pointed to by the +// number parameter. Returns true if that is possible. +// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use +// it here. +template <typename Integer> +bool ParseNaturalNumber(const ::std::string& str, Integer* number) { + // Fail fast if the given string does not begin with a digit; + // this bypasses strtoXXX's "optional leading whitespace and plus + // or minus sign" semantics, which are undesirable here. + if (str.empty() || !IsDigit(str[0])) { + return false; + } + errno = 0; + + char* end; + // BiggestConvertible is the largest integer type that system-provided + // string-to-number conversion routines can return. + using BiggestConvertible = unsigned long long; // NOLINT + + const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10); // NOLINT + const bool parse_success = *end == '\0' && errno == 0; + + GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed)); + + const Integer result = static_cast<Integer>(parsed); + if (parse_success && static_cast<BiggestConvertible>(result) == parsed) { + *number = result; + return true; + } + return false; +} +#endif // GTEST_HAS_DEATH_TEST + +// TestResult contains some private methods that should be hidden from +// Google Test user but are required for testing. This class allow our tests +// to access them. +// +// This class is supplied only for the purpose of testing Google Test's own +// constructs. Do not use it in user tests, either directly or indirectly. +class TestResultAccessor { + public: + static void RecordProperty(TestResult* test_result, + const std::string& xml_element, + const TestProperty& property) { + test_result->RecordProperty(xml_element, property); + } + + static void ClearTestPartResults(TestResult* test_result) { + test_result->ClearTestPartResults(); + } + + static const std::vector<testing::TestPartResult>& test_part_results( + const TestResult& test_result) { + return test_result.test_part_results(); + } +}; + +#if GTEST_CAN_STREAM_RESULTS_ + +// Streams test results to the given port on the given host machine. +class StreamingListener : public EmptyTestEventListener { + public: + // Abstract base class for writing strings to a socket. + class AbstractSocketWriter { + public: + virtual ~AbstractSocketWriter() {} + + // Sends a string to the socket. + virtual void Send(const std::string& message) = 0; + + // Closes the socket. + virtual void CloseConnection() {} + + // Sends a string and a newline to the socket. + void SendLn(const std::string& message) { Send(message + "\n"); } + }; + + // Concrete class for actually writing strings to a socket. + class SocketWriter : public AbstractSocketWriter { + public: + SocketWriter(const std::string& host, const std::string& port) + : sockfd_(-1), host_name_(host), port_num_(port) { + MakeConnection(); + } + + ~SocketWriter() override { + if (sockfd_ != -1) + CloseConnection(); + } + + // Sends a string to the socket. + void Send(const std::string& message) override { + GTEST_CHECK_(sockfd_ != -1) + << "Send() can be called only when there is a connection."; + + const auto len = static_cast<size_t>(message.length()); + if (write(sockfd_, message.c_str(), len) != static_cast<ssize_t>(len)) { + GTEST_LOG_(WARNING) + << "stream_result_to: failed to stream to " + << host_name_ << ":" << port_num_; + } + } + + private: + // Creates a client socket and connects to the server. + void MakeConnection(); + + // Closes the socket. + void CloseConnection() override { + GTEST_CHECK_(sockfd_ != -1) + << "CloseConnection() can be called only when there is a connection."; + + close(sockfd_); + sockfd_ = -1; + } + + int sockfd_; // socket file descriptor + const std::string host_name_; + const std::string port_num_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter); + }; // class SocketWriter + + // Escapes '=', '&', '%', and '\n' characters in str as "%xx". + static std::string UrlEncode(const char* str); + + StreamingListener(const std::string& host, const std::string& port) + : socket_writer_(new SocketWriter(host, port)) { + Start(); + } + + explicit StreamingListener(AbstractSocketWriter* socket_writer) + : socket_writer_(socket_writer) { Start(); } + + void OnTestProgramStart(const UnitTest& /* unit_test */) override { + SendLn("event=TestProgramStart"); + } + + void OnTestProgramEnd(const UnitTest& unit_test) override { + // Note that Google Test current only report elapsed time for each + // test iteration, not for the entire test program. + SendLn("event=TestProgramEnd&passed=" + FormatBool(unit_test.Passed())); + + // Notify the streaming server to stop. + socket_writer_->CloseConnection(); + } + + void OnTestIterationStart(const UnitTest& /* unit_test */, + int iteration) override { + SendLn("event=TestIterationStart&iteration=" + + StreamableToString(iteration)); + } + + void OnTestIterationEnd(const UnitTest& unit_test, + int /* iteration */) override { + SendLn("event=TestIterationEnd&passed=" + + FormatBool(unit_test.Passed()) + "&elapsed_time=" + + StreamableToString(unit_test.elapsed_time()) + "ms"); + } + + // Note that "event=TestCaseStart" is a wire format and has to remain + // "case" for compatibility + void OnTestCaseStart(const TestCase& test_case) override { + SendLn(std::string("event=TestCaseStart&name=") + test_case.name()); + } + + // Note that "event=TestCaseEnd" is a wire format and has to remain + // "case" for compatibility + void OnTestCaseEnd(const TestCase& test_case) override { + SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed()) + + "&elapsed_time=" + StreamableToString(test_case.elapsed_time()) + + "ms"); + } + + void OnTestStart(const TestInfo& test_info) override { + SendLn(std::string("event=TestStart&name=") + test_info.name()); + } + + void OnTestEnd(const TestInfo& test_info) override { + SendLn("event=TestEnd&passed=" + + FormatBool((test_info.result())->Passed()) + + "&elapsed_time=" + + StreamableToString((test_info.result())->elapsed_time()) + "ms"); + } + + void OnTestPartResult(const TestPartResult& test_part_result) override { + const char* file_name = test_part_result.file_name(); + if (file_name == nullptr) file_name = ""; + SendLn("event=TestPartResult&file=" + UrlEncode(file_name) + + "&line=" + StreamableToString(test_part_result.line_number()) + + "&message=" + UrlEncode(test_part_result.message())); + } + + private: + // Sends the given message and a newline to the socket. + void SendLn(const std::string& message) { socket_writer_->SendLn(message); } + + // Called at the start of streaming to notify the receiver what + // protocol we are using. + void Start() { SendLn("gtest_streaming_protocol_version=1.0"); } + + std::string FormatBool(bool value) { return value ? "1" : "0"; } + + const std::unique_ptr<AbstractSocketWriter> socket_writer_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); +}; // class StreamingListener + +#endif // GTEST_CAN_STREAM_RESULTS_ + +} // namespace internal +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_SRC_GTEST_INTERNAL_INL_H_ + +#if GTEST_OS_WINDOWS +# define vsnprintf _vsnprintf +#endif // GTEST_OS_WINDOWS + +#if GTEST_OS_MAC +#ifndef GTEST_OS_IOS +#include <crt_externs.h> +#endif +#endif + +#if GTEST_HAS_ABSL +#include "absl/debugging/failure_signal_handler.h" +#include "absl/debugging/stacktrace.h" +#include "absl/debugging/symbolize.h" +#include "absl/strings/str_cat.h" +#endif // GTEST_HAS_ABSL + +namespace testing { + +using internal::CountIf; +using internal::ForEach; +using internal::GetElementOr; +using internal::Shuffle; + +// Constants. + +// A test whose test suite name or test name matches this filter is +// disabled and not run. +static const char kDisableTestFilter[] = "DISABLED_*:*/DISABLED_*"; + +// A test suite whose name matches this filter is considered a death +// test suite and will be run before test suites whose name doesn't +// match this filter. +static const char kDeathTestSuiteFilter[] = "*DeathTest:*DeathTest/*"; + +// A test filter that matches everything. +static const char kUniversalFilter[] = "*"; + +// The default output format. +static const char kDefaultOutputFormat[] = "xml"; +// The default output file. +static const char kDefaultOutputFile[] = "test_detail"; + +// The environment variable name for the test shard index. +static const char kTestShardIndex[] = "GTEST_SHARD_INDEX"; +// The environment variable name for the total number of test shards. +static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS"; +// The environment variable name for the test shard status file. +static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE"; + +namespace internal { + +// The text used in failure messages to indicate the start of the +// stack trace. +const char kStackTraceMarker[] = "\nStack trace:\n"; + +// g_help_flag is true if and only if the --help flag or an equivalent form +// is specified on the command line. +bool g_help_flag = false; + +// Utilty function to Open File for Writing +static FILE* OpenFileForWriting(const std::string& output_file) { + FILE* fileout = nullptr; + FilePath output_file_path(output_file); + FilePath output_dir(output_file_path.RemoveFileName()); + + if (output_dir.CreateDirectoriesRecursively()) { + fileout = posix::FOpen(output_file.c_str(), "w"); + } + if (fileout == nullptr) { + GTEST_LOG_(FATAL) << "Unable to open file \"" << output_file << "\""; + } + return fileout; +} + +} // namespace internal + +// Bazel passes in the argument to '--test_filter' via the TESTBRIDGE_TEST_ONLY +// environment variable. +static const char* GetDefaultFilter() { + const char* const testbridge_test_only = + internal::posix::GetEnv("TESTBRIDGE_TEST_ONLY"); + if (testbridge_test_only != nullptr) { + return testbridge_test_only; + } + return kUniversalFilter; +} + +// Bazel passes in the argument to '--test_runner_fail_fast' via the +// TESTBRIDGE_TEST_RUNNER_FAIL_FAST environment variable. +static bool GetDefaultFailFast() { + const char* const testbridge_test_runner_fail_fast = + internal::posix::GetEnv("TESTBRIDGE_TEST_RUNNER_FAIL_FAST"); + if (testbridge_test_runner_fail_fast != nullptr) { + return strcmp(testbridge_test_runner_fail_fast, "1") == 0; + } + return false; +} + +GTEST_DEFINE_bool_( + fail_fast, internal::BoolFromGTestEnv("fail_fast", GetDefaultFailFast()), + "True if and only if a test failure should stop further test execution."); + +GTEST_DEFINE_bool_( + also_run_disabled_tests, + internal::BoolFromGTestEnv("also_run_disabled_tests", false), + "Run disabled tests too, in addition to the tests normally being run."); + +GTEST_DEFINE_bool_( + break_on_failure, internal::BoolFromGTestEnv("break_on_failure", false), + "True if and only if a failed assertion should be a debugger " + "break-point."); + +GTEST_DEFINE_bool_(catch_exceptions, + internal::BoolFromGTestEnv("catch_exceptions", true), + "True if and only if " GTEST_NAME_ + " should catch exceptions and treat them as test failures."); + +GTEST_DEFINE_string_( + color, + internal::StringFromGTestEnv("color", "auto"), + "Whether to use colors in the output. Valid values: yes, no, " + "and auto. 'auto' means to use colors if the output is " + "being sent to a terminal and the TERM environment variable " + "is set to a terminal type that supports colors."); + +GTEST_DEFINE_string_( + filter, + internal::StringFromGTestEnv("filter", GetDefaultFilter()), + "A colon-separated list of glob (not regex) patterns " + "for filtering the tests to run, optionally followed by a " + "'-' and a : separated list of negative patterns (tests to " + "exclude). A test is run if it matches one of the positive " + "patterns and does not match any of the negative patterns."); + +GTEST_DEFINE_bool_( + install_failure_signal_handler, + internal::BoolFromGTestEnv("install_failure_signal_handler", false), + "If true and supported on the current platform, " GTEST_NAME_ " should " + "install a signal handler that dumps debugging information when fatal " + "signals are raised."); + +GTEST_DEFINE_bool_(list_tests, false, + "List all tests without running them."); + +// The net priority order after flag processing is thus: +// --gtest_output command line flag +// GTEST_OUTPUT environment variable +// XML_OUTPUT_FILE environment variable +// '' +GTEST_DEFINE_string_( + output, + internal::StringFromGTestEnv("output", + internal::OutputFlagAlsoCheckEnvVar().c_str()), + "A format (defaults to \"xml\" but can be specified to be \"json\"), " + "optionally followed by a colon and an output file name or directory. " + "A directory is indicated by a trailing pathname separator. " + "Examples: \"xml:filename.xml\", \"xml::directoryname/\". " + "If a directory is specified, output files will be created " + "within that directory, with file-names based on the test " + "executable's name and, if necessary, made unique by adding " + "digits."); + +GTEST_DEFINE_bool_( + brief, internal::BoolFromGTestEnv("brief", false), + "True if only test failures should be displayed in text output."); + +GTEST_DEFINE_bool_(print_time, internal::BoolFromGTestEnv("print_time", true), + "True if and only if " GTEST_NAME_ + " should display elapsed time in text output."); + +GTEST_DEFINE_bool_(print_utf8, internal::BoolFromGTestEnv("print_utf8", true), + "True if and only if " GTEST_NAME_ + " prints UTF8 characters as text."); + +GTEST_DEFINE_int32_( + random_seed, + internal::Int32FromGTestEnv("random_seed", 0), + "Random number seed to use when shuffling test orders. Must be in range " + "[1, 99999], or 0 to use a seed based on the current time."); + +GTEST_DEFINE_int32_( + repeat, + internal::Int32FromGTestEnv("repeat", 1), + "How many times to repeat each test. Specify a negative number " + "for repeating forever. Useful for shaking out flaky tests."); + +GTEST_DEFINE_bool_(show_internal_stack_frames, false, + "True if and only if " GTEST_NAME_ + " should include internal stack frames when " + "printing test failure stack traces."); + +GTEST_DEFINE_bool_(shuffle, internal::BoolFromGTestEnv("shuffle", false), + "True if and only if " GTEST_NAME_ + " should randomize tests' order on every run."); + +GTEST_DEFINE_int32_( + stack_trace_depth, + internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth), + "The maximum number of stack frames to print when an " + "assertion fails. The valid range is 0 through 100, inclusive."); + +GTEST_DEFINE_string_( + stream_result_to, + internal::StringFromGTestEnv("stream_result_to", ""), + "This flag specifies the host name and the port number on which to stream " + "test results. Example: \"localhost:555\". The flag is effective only on " + "Linux."); + +GTEST_DEFINE_bool_( + throw_on_failure, + internal::BoolFromGTestEnv("throw_on_failure", false), + "When this flag is specified, a failed assertion will throw an exception " + "if exceptions are enabled or exit the program with a non-zero code " + "otherwise. For use with an external test framework."); + +#if GTEST_USE_OWN_FLAGFILE_FLAG_ +GTEST_DEFINE_string_( + flagfile, + internal::StringFromGTestEnv("flagfile", ""), + "This flag specifies the flagfile to read command-line flags from."); +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + +namespace internal { + +// Generates a random number from [0, range), using a Linear +// Congruential Generator (LCG). Crashes if 'range' is 0 or greater +// than kMaxRange. +uint32_t Random::Generate(uint32_t range) { + // These constants are the same as are used in glibc's rand(3). + // Use wider types than necessary to prevent unsigned overflow diagnostics. + state_ = static_cast<uint32_t>(1103515245ULL*state_ + 12345U) % kMaxRange; + + GTEST_CHECK_(range > 0) + << "Cannot generate a number in the range [0, 0)."; + GTEST_CHECK_(range <= kMaxRange) + << "Generation of a number in [0, " << range << ") was requested, " + << "but this can only generate numbers in [0, " << kMaxRange << ")."; + + // Converting via modulus introduces a bit of downward bias, but + // it's simple, and a linear congruential generator isn't too good + // to begin with. + return state_ % range; +} + +// GTestIsInitialized() returns true if and only if the user has initialized +// Google Test. Useful for catching the user mistake of not initializing +// Google Test before calling RUN_ALL_TESTS(). +static bool GTestIsInitialized() { return GetArgvs().size() > 0; } + +// Iterates over a vector of TestSuites, keeping a running sum of the +// results of calling a given int-returning method on each. +// Returns the sum. +static int SumOverTestSuiteList(const std::vector<TestSuite*>& case_list, + int (TestSuite::*method)() const) { + int sum = 0; + for (size_t i = 0; i < case_list.size(); i++) { + sum += (case_list[i]->*method)(); + } + return sum; +} + +// Returns true if and only if the test suite passed. +static bool TestSuitePassed(const TestSuite* test_suite) { + return test_suite->should_run() && test_suite->Passed(); +} + +// Returns true if and only if the test suite failed. +static bool TestSuiteFailed(const TestSuite* test_suite) { + return test_suite->should_run() && test_suite->Failed(); +} + +// Returns true if and only if test_suite contains at least one test that +// should run. +static bool ShouldRunTestSuite(const TestSuite* test_suite) { + return test_suite->should_run(); +} + +// AssertHelper constructor. +AssertHelper::AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message) + : data_(new AssertHelperData(type, file, line, message)) { +} + +AssertHelper::~AssertHelper() { + delete data_; +} + +// Message assignment, for assertion streaming support. +void AssertHelper::operator=(const Message& message) const { + UnitTest::GetInstance()-> + AddTestPartResult(data_->type, data_->file, data_->line, + AppendUserMessage(data_->message, message), + UnitTest::GetInstance()->impl() + ->CurrentOsStackTraceExceptTop(1) + // Skips the stack frame for this function itself. + ); // NOLINT +} + +namespace { + +// When TEST_P is found without a matching INSTANTIATE_TEST_SUITE_P +// to creates test cases for it, a syntetic test case is +// inserted to report ether an error or a log message. +// +// This configuration bit will likely be removed at some point. +constexpr bool kErrorOnUninstantiatedParameterizedTest = true; +constexpr bool kErrorOnUninstantiatedTypeParameterizedTest = true; + +// A test that fails at a given file/line location with a given message. +class FailureTest : public Test { + public: + explicit FailureTest(const CodeLocation& loc, std::string error_message, + bool as_error) + : loc_(loc), + error_message_(std::move(error_message)), + as_error_(as_error) {} + + void TestBody() override { + if (as_error_) { + AssertHelper(TestPartResult::kNonFatalFailure, loc_.file.c_str(), + loc_.line, "") = Message() << error_message_; + } else { + std::cout << error_message_ << std::endl; + } + } + + private: + const CodeLocation loc_; + const std::string error_message_; + const bool as_error_; +}; + + +} // namespace + +std::set<std::string>* GetIgnoredParameterizedTestSuites() { + return UnitTest::GetInstance()->impl()->ignored_parameterized_test_suites(); +} + +// Add a given test_suit to the list of them allow to go un-instantiated. +MarkAsIgnored::MarkAsIgnored(const char* test_suite) { + GetIgnoredParameterizedTestSuites()->insert(test_suite); +} + +// If this parameterized test suite has no instantiations (and that +// has not been marked as okay), emit a test case reporting that. +void InsertSyntheticTestCase(const std::string& name, CodeLocation location, + bool has_test_p) { + const auto& ignored = *GetIgnoredParameterizedTestSuites(); + if (ignored.find(name) != ignored.end()) return; + + const char kMissingInstantiation[] = // + " is defined via TEST_P, but never instantiated. None of the test cases " + "will run. Either no INSTANTIATE_TEST_SUITE_P is provided or the only " + "ones provided expand to nothing." + "\n\n" + "Ideally, TEST_P definitions should only ever be included as part of " + "binaries that intend to use them. (As opposed to, for example, being " + "placed in a library that may be linked in to get other utilities.)"; + + const char kMissingTestCase[] = // + " is instantiated via INSTANTIATE_TEST_SUITE_P, but no tests are " + "defined via TEST_P . No test cases will run." + "\n\n" + "Ideally, INSTANTIATE_TEST_SUITE_P should only ever be invoked from " + "code that always depend on code that provides TEST_P. Failing to do " + "so is often an indication of dead code, e.g. the last TEST_P was " + "removed but the rest got left behind."; + + std::string message = + "Parameterized test suite " + name + + (has_test_p ? kMissingInstantiation : kMissingTestCase) + + "\n\n" + "To suppress this error for this test suite, insert the following line " + "(in a non-header) in the namespace it is defined in:" + "\n\n" + "GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(" + name + ");"; + + std::string full_name = "UninstantiatedParameterizedTestSuite<" + name + ">"; + RegisterTest( // + "GoogleTestVerification", full_name.c_str(), + nullptr, // No type parameter. + nullptr, // No value parameter. + location.file.c_str(), location.line, [message, location] { + return new FailureTest(location, message, + kErrorOnUninstantiatedParameterizedTest); + }); +} + +void RegisterTypeParameterizedTestSuite(const char* test_suite_name, + CodeLocation code_location) { + GetUnitTestImpl()->type_parameterized_test_registry().RegisterTestSuite( + test_suite_name, code_location); +} + +void RegisterTypeParameterizedTestSuiteInstantiation(const char* case_name) { + GetUnitTestImpl() + ->type_parameterized_test_registry() + .RegisterInstantiation(case_name); +} + +void TypeParameterizedTestSuiteRegistry::RegisterTestSuite( + const char* test_suite_name, CodeLocation code_location) { + suites_.emplace(std::string(test_suite_name), + TypeParameterizedTestSuiteInfo(code_location)); +} + +void TypeParameterizedTestSuiteRegistry::RegisterInstantiation( + const char* test_suite_name) { + auto it = suites_.find(std::string(test_suite_name)); + if (it != suites_.end()) { + it->second.instantiated = true; + } else { + GTEST_LOG_(ERROR) << "Unknown type parameterized test suit '" + << test_suite_name << "'"; + } +} + +void TypeParameterizedTestSuiteRegistry::CheckForInstantiations() { + const auto& ignored = *GetIgnoredParameterizedTestSuites(); + for (const auto& testcase : suites_) { + if (testcase.second.instantiated) continue; + if (ignored.find(testcase.first) != ignored.end()) continue; + + std::string message = + "Type parameterized test suite " + testcase.first + + " is defined via REGISTER_TYPED_TEST_SUITE_P, but never instantiated " + "via INSTANTIATE_TYPED_TEST_SUITE_P. None of the test cases will run." + "\n\n" + "Ideally, TYPED_TEST_P definitions should only ever be included as " + "part of binaries that intend to use them. (As opposed to, for " + "example, being placed in a library that may be linked in to get other " + "utilities.)" + "\n\n" + "To suppress this error for this test suite, insert the following line " + "(in a non-header) in the namespace it is defined in:" + "\n\n" + "GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(" + + testcase.first + ");"; + + std::string full_name = + "UninstantiatedTypeParameterizedTestSuite<" + testcase.first + ">"; + RegisterTest( // + "GoogleTestVerification", full_name.c_str(), + nullptr, // No type parameter. + nullptr, // No value parameter. + testcase.second.code_location.file.c_str(), + testcase.second.code_location.line, [message, testcase] { + return new FailureTest(testcase.second.code_location, message, + kErrorOnUninstantiatedTypeParameterizedTest); + }); + } +} + +// A copy of all command line arguments. Set by InitGoogleTest(). +static ::std::vector<std::string> g_argvs; + +::std::vector<std::string> GetArgvs() { +#if defined(GTEST_CUSTOM_GET_ARGVS_) + // GTEST_CUSTOM_GET_ARGVS_() may return a container of std::string or + // ::string. This code converts it to the appropriate type. + const auto& custom = GTEST_CUSTOM_GET_ARGVS_(); + return ::std::vector<std::string>(custom.begin(), custom.end()); +#else // defined(GTEST_CUSTOM_GET_ARGVS_) + return g_argvs; +#endif // defined(GTEST_CUSTOM_GET_ARGVS_) +} + +// Returns the current application's name, removing directory path if that +// is present. +FilePath GetCurrentExecutableName() { + FilePath result; + +#if GTEST_OS_WINDOWS || GTEST_OS_OS2 + result.Set(FilePath(GetArgvs()[0]).RemoveExtension("exe")); +#else + result.Set(FilePath(GetArgvs()[0])); +#endif // GTEST_OS_WINDOWS + + return result.RemoveDirectoryName(); +} + +// Functions for processing the gtest_output flag. + +// Returns the output format, or "" for normal printed output. +std::string UnitTestOptions::GetOutputFormat() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + const char* const colon = strchr(gtest_output_flag, ':'); + return (colon == nullptr) + ? std::string(gtest_output_flag) + : std::string(gtest_output_flag, + static_cast<size_t>(colon - gtest_output_flag)); +} + +// Returns the name of the requested output file, or the default if none +// was explicitly specified. +std::string UnitTestOptions::GetAbsolutePathToOutputFile() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + + std::string format = GetOutputFormat(); + if (format.empty()) + format = std::string(kDefaultOutputFormat); + + const char* const colon = strchr(gtest_output_flag, ':'); + if (colon == nullptr) + return internal::FilePath::MakeFileName( + internal::FilePath( + UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(kDefaultOutputFile), 0, + format.c_str()).string(); + + internal::FilePath output_name(colon + 1); + if (!output_name.IsAbsolutePath()) + output_name = internal::FilePath::ConcatPaths( + internal::FilePath(UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(colon + 1)); + + if (!output_name.IsDirectory()) + return output_name.string(); + + internal::FilePath result(internal::FilePath::GenerateUniqueFileName( + output_name, internal::GetCurrentExecutableName(), + GetOutputFormat().c_str())); + return result.string(); +} + +// Returns true if and only if the wildcard pattern matches the string. Each +// pattern consists of regular characters, single-character wildcards (?), and +// multi-character wildcards (*). +// +// This function implements a linear-time string globbing algorithm based on +// https://research.swtch.com/glob. +static bool PatternMatchesString(const std::string& name_str, + const char* pattern, const char* pattern_end) { + const char* name = name_str.c_str(); + const char* const name_begin = name; + const char* const name_end = name + name_str.size(); + + const char* pattern_next = pattern; + const char* name_next = name; + + while (pattern < pattern_end || name < name_end) { + if (pattern < pattern_end) { + switch (*pattern) { + default: // Match an ordinary character. + if (name < name_end && *name == *pattern) { + ++pattern; + ++name; + continue; + } + break; + case '?': // Match any single character. + if (name < name_end) { + ++pattern; + ++name; + continue; + } + break; + case '*': + // Match zero or more characters. Start by skipping over the wildcard + // and matching zero characters from name. If that fails, restart and + // match one more character than the last attempt. + pattern_next = pattern; + name_next = name + 1; + ++pattern; + continue; + } + } + // Failed to match a character. Restart if possible. + if (name_begin < name_next && name_next <= name_end) { + pattern = pattern_next; + name = name_next; + continue; + } + return false; + } + return true; +} + +bool UnitTestOptions::MatchesFilter(const std::string& name_str, + const char* filter) { + // The filter is a list of patterns separated by colons (:). + const char* pattern = filter; + while (true) { + // Find the bounds of this pattern. + const char* const next_sep = strchr(pattern, ':'); + const char* const pattern_end = + next_sep != nullptr ? next_sep : pattern + strlen(pattern); + + // Check if this pattern matches name_str. + if (PatternMatchesString(name_str, pattern, pattern_end)) { + break; + } + + // Give up on this pattern. However, if we found a pattern separator (:), + // advance to the next pattern (skipping over the separator) and restart. + if (next_sep == nullptr) { + return false; + } + pattern = next_sep + 1; + } + return true; +} + +// Returns true if and only if the user-specified filter matches the test +// suite name and the test name. +bool UnitTestOptions::FilterMatchesTest(const std::string& test_suite_name, + const std::string& test_name) { + const std::string& full_name = test_suite_name + "." + test_name.c_str(); + + // Split --gtest_filter at '-', if there is one, to separate into + // positive filter and negative filter portions + const char* const p = GTEST_FLAG(filter).c_str(); + const char* const dash = strchr(p, '-'); + std::string positive; + std::string negative; + if (dash == nullptr) { + positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter + negative = ""; + } else { + positive = std::string(p, dash); // Everything up to the dash + negative = std::string(dash + 1); // Everything after the dash + if (positive.empty()) { + // Treat '-test1' as the same as '*-test1' + positive = kUniversalFilter; + } + } + + // A filter is a colon-separated list of patterns. It matches a + // test if any pattern in it matches the test. + return (MatchesFilter(full_name, positive.c_str()) && + !MatchesFilter(full_name, negative.c_str())); +} + +#if GTEST_HAS_SEH +// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the +// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. +// This function is useful as an __except condition. +int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) { + // Google Test should handle a SEH exception if: + // 1. the user wants it to, AND + // 2. this is not a breakpoint exception, AND + // 3. this is not a C++ exception (VC++ implements them via SEH, + // apparently). + // + // SEH exception code for C++ exceptions. + // (see http://support.microsoft.com/kb/185294 for more information). + const DWORD kCxxExceptionCode = 0xe06d7363; + + bool should_handle = true; + + if (!GTEST_FLAG(catch_exceptions)) + should_handle = false; + else if (exception_code == EXCEPTION_BREAKPOINT) + should_handle = false; + else if (exception_code == kCxxExceptionCode) + should_handle = false; + + return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; +} +#endif // GTEST_HAS_SEH + +} // namespace internal + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. Intercepts only failures from the current thread. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + TestPartResultArray* result) + : intercept_mode_(INTERCEPT_ONLY_CURRENT_THREAD), + result_(result) { + Init(); +} + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + InterceptMode intercept_mode, TestPartResultArray* result) + : intercept_mode_(intercept_mode), + result_(result) { + Init(); +} + +void ScopedFakeTestPartResultReporter::Init() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + old_reporter_ = impl->GetGlobalTestPartResultReporter(); + impl->SetGlobalTestPartResultReporter(this); + } else { + old_reporter_ = impl->GetTestPartResultReporterForCurrentThread(); + impl->SetTestPartResultReporterForCurrentThread(this); + } +} + +// The d'tor restores the test part result reporter used by Google Test +// before. +ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + impl->SetGlobalTestPartResultReporter(old_reporter_); + } else { + impl->SetTestPartResultReporterForCurrentThread(old_reporter_); + } +} + +// Increments the test part result count and remembers the result. +// This method is from the TestPartResultReporterInterface interface. +void ScopedFakeTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + result_->Append(result); +} + +namespace internal { + +// Returns the type ID of ::testing::Test. We should always call this +// instead of GetTypeId< ::testing::Test>() to get the type ID of +// testing::Test. This is to work around a suspected linker bug when +// using Google Test as a framework on Mac OS X. The bug causes +// GetTypeId< ::testing::Test>() to return different values depending +// on whether the call is from the Google Test framework itself or +// from user test code. GetTestTypeId() is guaranteed to always +// return the same value, as it always calls GetTypeId<>() from the +// gtest.cc, which is within the Google Test framework. +TypeId GetTestTypeId() { + return GetTypeId<Test>(); +} + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId(); + +// This predicate-formatter checks that 'results' contains a test part +// failure of the given type and that the failure message contains the +// given substring. +static AssertionResult HasOneFailure(const char* /* results_expr */, + const char* /* type_expr */, + const char* /* substr_expr */, + const TestPartResultArray& results, + TestPartResult::Type type, + const std::string& substr) { + const std::string expected(type == TestPartResult::kFatalFailure ? + "1 fatal failure" : + "1 non-fatal failure"); + Message msg; + if (results.size() != 1) { + msg << "Expected: " << expected << "\n" + << " Actual: " << results.size() << " failures"; + for (int i = 0; i < results.size(); i++) { + msg << "\n" << results.GetTestPartResult(i); + } + return AssertionFailure() << msg; + } + + const TestPartResult& r = results.GetTestPartResult(0); + if (r.type() != type) { + return AssertionFailure() << "Expected: " << expected << "\n" + << " Actual:\n" + << r; + } + + if (strstr(r.message(), substr.c_str()) == nullptr) { + return AssertionFailure() << "Expected: " << expected << " containing \"" + << substr << "\"\n" + << " Actual:\n" + << r; + } + + return AssertionSuccess(); +} + +// The constructor of SingleFailureChecker remembers where to look up +// test part results, what type of failure we expect, and what +// substring the failure message should contain. +SingleFailureChecker::SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, + const std::string& substr) + : results_(results), type_(type), substr_(substr) {} + +// The destructor of SingleFailureChecker verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +SingleFailureChecker::~SingleFailureChecker() { + EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_); +} + +DefaultGlobalTestPartResultReporter::DefaultGlobalTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultGlobalTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->current_test_result()->AddTestPartResult(result); + unit_test_->listeners()->repeater()->OnTestPartResult(result); +} + +DefaultPerThreadTestPartResultReporter::DefaultPerThreadTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultPerThreadTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->GetGlobalTestPartResultReporter()->ReportTestPartResult(result); +} + +// Returns the global test part result reporter. +TestPartResultReporterInterface* +UnitTestImpl::GetGlobalTestPartResultReporter() { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + return global_test_part_result_repoter_; +} + +// Sets the global test part result reporter. +void UnitTestImpl::SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter) { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + global_test_part_result_repoter_ = reporter; +} + +// Returns the test part result reporter for the current thread. +TestPartResultReporterInterface* +UnitTestImpl::GetTestPartResultReporterForCurrentThread() { + return per_thread_test_part_result_reporter_.get(); +} + +// Sets the test part result reporter for the current thread. +void UnitTestImpl::SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter) { + per_thread_test_part_result_reporter_.set(reporter); +} + +// Gets the number of successful test suites. +int UnitTestImpl::successful_test_suite_count() const { + return CountIf(test_suites_, TestSuitePassed); +} + +// Gets the number of failed test suites. +int UnitTestImpl::failed_test_suite_count() const { + return CountIf(test_suites_, TestSuiteFailed); +} + +// Gets the number of all test suites. +int UnitTestImpl::total_test_suite_count() const { + return static_cast<int>(test_suites_.size()); +} + +// Gets the number of all test suites that contain at least one test +// that should run. +int UnitTestImpl::test_suite_to_run_count() const { + return CountIf(test_suites_, ShouldRunTestSuite); +} + +// Gets the number of successful tests. +int UnitTestImpl::successful_test_count() const { + return SumOverTestSuiteList(test_suites_, &TestSuite::successful_test_count); +} + +// Gets the number of skipped tests. +int UnitTestImpl::skipped_test_count() const { + return SumOverTestSuiteList(test_suites_, &TestSuite::skipped_test_count); +} + +// Gets the number of failed tests. +int UnitTestImpl::failed_test_count() const { + return SumOverTestSuiteList(test_suites_, &TestSuite::failed_test_count); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTestImpl::reportable_disabled_test_count() const { + return SumOverTestSuiteList(test_suites_, + &TestSuite::reportable_disabled_test_count); +} + +// Gets the number of disabled tests. +int UnitTestImpl::disabled_test_count() const { + return SumOverTestSuiteList(test_suites_, &TestSuite::disabled_test_count); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTestImpl::reportable_test_count() const { + return SumOverTestSuiteList(test_suites_, &TestSuite::reportable_test_count); +} + +// Gets the number of all tests. +int UnitTestImpl::total_test_count() const { + return SumOverTestSuiteList(test_suites_, &TestSuite::total_test_count); +} + +// Gets the number of tests that should run. +int UnitTestImpl::test_to_run_count() const { + return SumOverTestSuiteList(test_suites_, &TestSuite::test_to_run_count); +} + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// CurrentOsStackTraceExceptTop(1), Foo() will be included in the +// trace but Bar() and CurrentOsStackTraceExceptTop() won't. +std::string UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { + return os_stack_trace_getter()->CurrentStackTrace( + static_cast<int>(GTEST_FLAG(stack_trace_depth)), + skip_count + 1 + // Skips the user-specified number of frames plus this function + // itself. + ); // NOLINT +} + +// A helper class for measuring elapsed times. +class Timer { + public: + Timer() : start_(std::chrono::steady_clock::now()) {} + + // Return time elapsed in milliseconds since the timer was created. + TimeInMillis Elapsed() { + return std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now() - start_) + .count(); + } + + private: + std::chrono::steady_clock::time_point start_; +}; + +// Returns a timestamp as milliseconds since the epoch. Note this time may jump +// around subject to adjustments by the system, to measure elapsed time use +// Timer instead. +TimeInMillis GetTimeInMillis() { + return std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::system_clock::now() - + std::chrono::system_clock::from_time_t(0)) + .count(); +} + +// Utilities + +// class String. + +#if GTEST_OS_WINDOWS_MOBILE +// Creates a UTF-16 wide string from the given ANSI string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the wide string, or NULL if the +// input is NULL. +LPCWSTR String::AnsiToUtf16(const char* ansi) { + if (!ansi) return nullptr; + const int length = strlen(ansi); + const int unicode_length = + MultiByteToWideChar(CP_ACP, 0, ansi, length, nullptr, 0); + WCHAR* unicode = new WCHAR[unicode_length + 1]; + MultiByteToWideChar(CP_ACP, 0, ansi, length, + unicode, unicode_length); + unicode[unicode_length] = 0; + return unicode; +} + +// Creates an ANSI string from the given wide string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the ANSI string, or NULL if the +// input is NULL. +const char* String::Utf16ToAnsi(LPCWSTR utf16_str) { + if (!utf16_str) return nullptr; + const int ansi_length = WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, nullptr, + 0, nullptr, nullptr); + char* ansi = new char[ansi_length + 1]; + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, ansi, ansi_length, nullptr, + nullptr); + ansi[ansi_length] = 0; + return ansi; +} + +#endif // GTEST_OS_WINDOWS_MOBILE + +// Compares two C strings. Returns true if and only if they have the same +// content. +// +// Unlike strcmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CStringEquals(const char * lhs, const char * rhs) { + if (lhs == nullptr) return rhs == nullptr; + + if (rhs == nullptr) return false; + + return strcmp(lhs, rhs) == 0; +} + +#if GTEST_HAS_STD_WSTRING + +// Converts an array of wide chars to a narrow string using the UTF-8 +// encoding, and streams the result to the given Message object. +static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, + Message* msg) { + for (size_t i = 0; i != length; ) { // NOLINT + if (wstr[i] != L'\0') { + *msg << WideStringToUtf8(wstr + i, static_cast<int>(length - i)); + while (i != length && wstr[i] != L'\0') + i++; + } else { + *msg << '\0'; + i++; + } + } +} + +#endif // GTEST_HAS_STD_WSTRING + +void SplitString(const ::std::string& str, char delimiter, + ::std::vector< ::std::string>* dest) { + ::std::vector< ::std::string> parsed; + ::std::string::size_type pos = 0; + while (::testing::internal::AlwaysTrue()) { + const ::std::string::size_type colon = str.find(delimiter, pos); + if (colon == ::std::string::npos) { + parsed.push_back(str.substr(pos)); + break; + } else { + parsed.push_back(str.substr(pos, colon - pos)); + pos = colon + 1; + } + } + dest->swap(parsed); +} + +} // namespace internal + +// Constructs an empty Message. +// We allocate the stringstream separately because otherwise each use of +// ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's +// stack frame leading to huge stack frames in some cases; gcc does not reuse +// the stack space. +Message::Message() : ss_(new ::std::stringstream) { + // By default, we want there to be enough precision when printing + // a double to a Message. + *ss_ << std::setprecision(std::numeric_limits<double>::digits10 + 2); +} + +// These two overloads allow streaming a wide C string to a Message +// using the UTF-8 encoding. +Message& Message::operator <<(const wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} +Message& Message::operator <<(wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} + +#if GTEST_HAS_STD_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::std::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_STD_WSTRING + +// Gets the text streamed to this object so far as an std::string. +// Each '\0' character in the buffer is replaced with "\\0". +std::string Message::GetString() const { + return internal::StringStreamToString(ss_.get()); +} + +// AssertionResult constructors. +// Used in EXPECT_TRUE/FALSE(assertion_result). +AssertionResult::AssertionResult(const AssertionResult& other) + : success_(other.success_), + message_(other.message_.get() != nullptr + ? new ::std::string(*other.message_) + : static_cast< ::std::string*>(nullptr)) {} + +// Swaps two AssertionResults. +void AssertionResult::swap(AssertionResult& other) { + using std::swap; + swap(success_, other.success_); + swap(message_, other.message_); +} + +// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. +AssertionResult AssertionResult::operator!() const { + AssertionResult negation(!success_); + if (message_.get() != nullptr) negation << *message_; + return negation; +} + +// Makes a successful assertion result. +AssertionResult AssertionSuccess() { + return AssertionResult(true); +} + +// Makes a failed assertion result. +AssertionResult AssertionFailure() { + return AssertionResult(false); +} + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << message. +AssertionResult AssertionFailure(const Message& message) { + return AssertionFailure() << message; +} + +namespace internal { + +namespace edit_distance { +std::vector<EditType> CalculateOptimalEdits(const std::vector<size_t>& left, + const std::vector<size_t>& right) { + std::vector<std::vector<double> > costs( + left.size() + 1, std::vector<double>(right.size() + 1)); + std::vector<std::vector<EditType> > best_move( + left.size() + 1, std::vector<EditType>(right.size() + 1)); + + // Populate for empty right. + for (size_t l_i = 0; l_i < costs.size(); ++l_i) { + costs[l_i][0] = static_cast<double>(l_i); + best_move[l_i][0] = kRemove; + } + // Populate for empty left. + for (size_t r_i = 1; r_i < costs[0].size(); ++r_i) { + costs[0][r_i] = static_cast<double>(r_i); + best_move[0][r_i] = kAdd; + } + + for (size_t l_i = 0; l_i < left.size(); ++l_i) { + for (size_t r_i = 0; r_i < right.size(); ++r_i) { + if (left[l_i] == right[r_i]) { + // Found a match. Consume it. + costs[l_i + 1][r_i + 1] = costs[l_i][r_i]; + best_move[l_i + 1][r_i + 1] = kMatch; + continue; + } + + const double add = costs[l_i + 1][r_i]; + const double remove = costs[l_i][r_i + 1]; + const double replace = costs[l_i][r_i]; + if (add < remove && add < replace) { + costs[l_i + 1][r_i + 1] = add + 1; + best_move[l_i + 1][r_i + 1] = kAdd; + } else if (remove < add && remove < replace) { + costs[l_i + 1][r_i + 1] = remove + 1; + best_move[l_i + 1][r_i + 1] = kRemove; + } else { + // We make replace a little more expensive than add/remove to lower + // their priority. + costs[l_i + 1][r_i + 1] = replace + 1.00001; + best_move[l_i + 1][r_i + 1] = kReplace; + } + } + } + + // Reconstruct the best path. We do it in reverse order. + std::vector<EditType> best_path; + for (size_t l_i = left.size(), r_i = right.size(); l_i > 0 || r_i > 0;) { + EditType move = best_move[l_i][r_i]; + best_path.push_back(move); + l_i -= move != kAdd; + r_i -= move != kRemove; + } + std::reverse(best_path.begin(), best_path.end()); + return best_path; +} + +namespace { + +// Helper class to convert string into ids with deduplication. +class InternalStrings { + public: + size_t GetId(const std::string& str) { + IdMap::iterator it = ids_.find(str); + if (it != ids_.end()) return it->second; + size_t id = ids_.size(); + return ids_[str] = id; + } + + private: + typedef std::map<std::string, size_t> IdMap; + IdMap ids_; +}; + +} // namespace + +std::vector<EditType> CalculateOptimalEdits( + const std::vector<std::string>& left, + const std::vector<std::string>& right) { + std::vector<size_t> left_ids, right_ids; + { + InternalStrings intern_table; + for (size_t i = 0; i < left.size(); ++i) { + left_ids.push_back(intern_table.GetId(left[i])); + } + for (size_t i = 0; i < right.size(); ++i) { + right_ids.push_back(intern_table.GetId(right[i])); + } + } + return CalculateOptimalEdits(left_ids, right_ids); +} + +namespace { + +// Helper class that holds the state for one hunk and prints it out to the +// stream. +// It reorders adds/removes when possible to group all removes before all +// adds. It also adds the hunk header before printint into the stream. +class Hunk { + public: + Hunk(size_t left_start, size_t right_start) + : left_start_(left_start), + right_start_(right_start), + adds_(), + removes_(), + common_() {} + + void PushLine(char edit, const char* line) { + switch (edit) { + case ' ': + ++common_; + FlushEdits(); + hunk_.push_back(std::make_pair(' ', line)); + break; + case '-': + ++removes_; + hunk_removes_.push_back(std::make_pair('-', line)); + break; + case '+': + ++adds_; + hunk_adds_.push_back(std::make_pair('+', line)); + break; + } + } + + void PrintTo(std::ostream* os) { + PrintHeader(os); + FlushEdits(); + for (std::list<std::pair<char, const char*> >::const_iterator it = + hunk_.begin(); + it != hunk_.end(); ++it) { + *os << it->first << it->second << "\n"; + } + } + + bool has_edits() const { return adds_ || removes_; } + + private: + void FlushEdits() { + hunk_.splice(hunk_.end(), hunk_removes_); + hunk_.splice(hunk_.end(), hunk_adds_); + } + + // Print a unified diff header for one hunk. + // The format is + // "@@ -<left_start>,<left_length> +<right_start>,<right_length> @@" + // where the left/right parts are omitted if unnecessary. + void PrintHeader(std::ostream* ss) const { + *ss << "@@ "; + if (removes_) { + *ss << "-" << left_start_ << "," << (removes_ + common_); + } + if (removes_ && adds_) { + *ss << " "; + } + if (adds_) { + *ss << "+" << right_start_ << "," << (adds_ + common_); + } + *ss << " @@\n"; + } + + size_t left_start_, right_start_; + size_t adds_, removes_, common_; + std::list<std::pair<char, const char*> > hunk_, hunk_adds_, hunk_removes_; +}; + +} // namespace + +// Create a list of diff hunks in Unified diff format. +// Each hunk has a header generated by PrintHeader above plus a body with +// lines prefixed with ' ' for no change, '-' for deletion and '+' for +// addition. +// 'context' represents the desired unchanged prefix/suffix around the diff. +// If two hunks are close enough that their contexts overlap, then they are +// joined into one hunk. +std::string CreateUnifiedDiff(const std::vector<std::string>& left, + const std::vector<std::string>& right, + size_t context) { + const std::vector<EditType> edits = CalculateOptimalEdits(left, right); + + size_t l_i = 0, r_i = 0, edit_i = 0; + std::stringstream ss; + while (edit_i < edits.size()) { + // Find first edit. + while (edit_i < edits.size() && edits[edit_i] == kMatch) { + ++l_i; + ++r_i; + ++edit_i; + } + + // Find the first line to include in the hunk. + const size_t prefix_context = std::min(l_i, context); + Hunk hunk(l_i - prefix_context + 1, r_i - prefix_context + 1); + for (size_t i = prefix_context; i > 0; --i) { + hunk.PushLine(' ', left[l_i - i].c_str()); + } + + // Iterate the edits until we found enough suffix for the hunk or the input + // is over. + size_t n_suffix = 0; + for (; edit_i < edits.size(); ++edit_i) { + if (n_suffix >= context) { + // Continue only if the next hunk is very close. + auto it = edits.begin() + static_cast<int>(edit_i); + while (it != edits.end() && *it == kMatch) ++it; + if (it == edits.end() || + static_cast<size_t>(it - edits.begin()) - edit_i >= context) { + // There is no next edit or it is too far away. + break; + } + } + + EditType edit = edits[edit_i]; + // Reset count when a non match is found. + n_suffix = edit == kMatch ? n_suffix + 1 : 0; + + if (edit == kMatch || edit == kRemove || edit == kReplace) { + hunk.PushLine(edit == kMatch ? ' ' : '-', left[l_i].c_str()); + } + if (edit == kAdd || edit == kReplace) { + hunk.PushLine('+', right[r_i].c_str()); + } + + // Advance indices, depending on edit type. + l_i += edit != kAdd; + r_i += edit != kRemove; + } + + if (!hunk.has_edits()) { + // We are done. We don't want this hunk. + break; + } + + hunk.PrintTo(&ss); + } + return ss.str(); +} + +} // namespace edit_distance + +namespace { + +// The string representation of the values received in EqFailure() are already +// escaped. Split them on escaped '\n' boundaries. Leave all other escaped +// characters the same. +std::vector<std::string> SplitEscapedString(const std::string& str) { + std::vector<std::string> lines; + size_t start = 0, end = str.size(); + if (end > 2 && str[0] == '"' && str[end - 1] == '"') { + ++start; + --end; + } + bool escaped = false; + for (size_t i = start; i + 1 < end; ++i) { + if (escaped) { + escaped = false; + if (str[i] == 'n') { + lines.push_back(str.substr(start, i - start - 1)); + start = i + 1; + } + } else { + escaped = str[i] == '\\'; + } + } + lines.push_back(str.substr(start, end - start)); + return lines; +} + +} // namespace + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// lhs_expression: "foo" +// rhs_expression: "bar" +// lhs_value: "5" +// rhs_value: "6" +// +// The ignoring_case parameter is true if and only if the assertion is a +// *_STRCASEEQ*. When it's true, the string "Ignoring case" will +// be inserted into the message. +AssertionResult EqFailure(const char* lhs_expression, + const char* rhs_expression, + const std::string& lhs_value, + const std::string& rhs_value, + bool ignoring_case) { + Message msg; + msg << "Expected equality of these values:"; + msg << "\n " << lhs_expression; + if (lhs_value != lhs_expression) { + msg << "\n Which is: " << lhs_value; + } + msg << "\n " << rhs_expression; + if (rhs_value != rhs_expression) { + msg << "\n Which is: " << rhs_value; + } + + if (ignoring_case) { + msg << "\nIgnoring case"; + } + + if (!lhs_value.empty() && !rhs_value.empty()) { + const std::vector<std::string> lhs_lines = + SplitEscapedString(lhs_value); + const std::vector<std::string> rhs_lines = + SplitEscapedString(rhs_value); + if (lhs_lines.size() > 1 || rhs_lines.size() > 1) { + msg << "\nWith diff:\n" + << edit_distance::CreateUnifiedDiff(lhs_lines, rhs_lines); + } + } + + return AssertionFailure() << msg; +} + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value) { + const char* actual_message = assertion_result.message(); + Message msg; + msg << "Value of: " << expression_text + << "\n Actual: " << actual_predicate_value; + if (actual_message[0] != '\0') + msg << " (" << actual_message << ")"; + msg << "\nExpected: " << expected_predicate_value; + return msg.GetString(); +} + +// Helper function for implementing ASSERT_NEAR. +AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error) { + const double diff = fabs(val1 - val2); + if (diff <= abs_error) return AssertionSuccess(); + + // Find the value which is closest to zero. + const double min_abs = std::min(fabs(val1), fabs(val2)); + // Find the distance to the next double from that value. + const double epsilon = + nextafter(min_abs, std::numeric_limits<double>::infinity()) - min_abs; + // Detect the case where abs_error is so small that EXPECT_NEAR is + // effectively the same as EXPECT_EQUAL, and give an informative error + // message so that the situation can be more easily understood without + // requiring exotic floating-point knowledge. + // Don't do an epsilon check if abs_error is zero because that implies + // that an equality check was actually intended. + if (!(std::isnan)(val1) && !(std::isnan)(val2) && abs_error > 0 && + abs_error < epsilon) { + return AssertionFailure() + << "The difference between " << expr1 << " and " << expr2 << " is " + << diff << ", where\n" + << expr1 << " evaluates to " << val1 << ",\n" + << expr2 << " evaluates to " << val2 << ".\nThe abs_error parameter " + << abs_error_expr << " evaluates to " << abs_error + << " which is smaller than the minimum distance between doubles for " + "numbers of this magnitude which is " + << epsilon + << ", thus making this EXPECT_NEAR check equivalent to " + "EXPECT_EQUAL. Consider using EXPECT_DOUBLE_EQ instead."; + } + return AssertionFailure() + << "The difference between " << expr1 << " and " << expr2 + << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n" + << expr1 << " evaluates to " << val1 << ",\n" + << expr2 << " evaluates to " << val2 << ", and\n" + << abs_error_expr << " evaluates to " << abs_error << "."; +} + + +// Helper template for implementing FloatLE() and DoubleLE(). +template <typename RawType> +AssertionResult FloatingPointLE(const char* expr1, + const char* expr2, + RawType val1, + RawType val2) { + // Returns success if val1 is less than val2, + if (val1 < val2) { + return AssertionSuccess(); + } + + // or if val1 is almost equal to val2. + const FloatingPoint<RawType> lhs(val1), rhs(val2); + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + // Note that the above two checks will both fail if either val1 or + // val2 is NaN, as the IEEE floating-point standard requires that + // any predicate involving a NaN must return false. + + ::std::stringstream val1_ss; + val1_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2) + << val1; + + ::std::stringstream val2_ss; + val2_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2) + << val2; + + return AssertionFailure() + << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n" + << " Actual: " << StringStreamToString(&val1_ss) << " vs " + << StringStreamToString(&val2_ss); +} + +} // namespace internal + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2) { + return internal::FloatingPointLE<float>(expr1, expr2, val1, val2); +} + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2) { + return internal::FloatingPointLE<double>(expr1, expr2, val1, val2); +} + +namespace internal { + +// The helper function for {ASSERT|EXPECT}_STREQ. +AssertionResult CmpHelperSTREQ(const char* lhs_expression, + const char* rhs_expression, + const char* lhs, + const char* rhs) { + if (String::CStringEquals(lhs, rhs)) { + return AssertionSuccess(); + } + + return EqFailure(lhs_expression, + rhs_expression, + PrintToString(lhs), + PrintToString(rhs), + false); +} + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +AssertionResult CmpHelperSTRCASEEQ(const char* lhs_expression, + const char* rhs_expression, + const char* lhs, + const char* rhs) { + if (String::CaseInsensitiveCStringEquals(lhs, rhs)) { + return AssertionSuccess(); + } + + return EqFailure(lhs_expression, + rhs_expression, + PrintToString(lhs), + PrintToString(rhs), + true); +} + +// The helper function for {ASSERT|EXPECT}_STRNE. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CaseInsensitiveCStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() + << "Expected: (" << s1_expression << ") != (" + << s2_expression << ") (ignoring case), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +} // namespace internal + +namespace { + +// Helper functions for implementing IsSubString() and IsNotSubstring(). + +// This group of overloaded functions return true if and only if needle +// is a substring of haystack. NULL is considered a substring of +// itself only. + +bool IsSubstringPred(const char* needle, const char* haystack) { + if (needle == nullptr || haystack == nullptr) return needle == haystack; + + return strstr(haystack, needle) != nullptr; +} + +bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) { + if (needle == nullptr || haystack == nullptr) return needle == haystack; + + return wcsstr(haystack, needle) != nullptr; +} + +// StringType here can be either ::std::string or ::std::wstring. +template <typename StringType> +bool IsSubstringPred(const StringType& needle, + const StringType& haystack) { + return haystack.find(needle) != StringType::npos; +} + +// This function implements either IsSubstring() or IsNotSubstring(), +// depending on the value of the expected_to_be_substring parameter. +// StringType here can be const char*, const wchar_t*, ::std::string, +// or ::std::wstring. +template <typename StringType> +AssertionResult IsSubstringImpl( + bool expected_to_be_substring, + const char* needle_expr, const char* haystack_expr, + const StringType& needle, const StringType& haystack) { + if (IsSubstringPred(needle, haystack) == expected_to_be_substring) + return AssertionSuccess(); + + const bool is_wide_string = sizeof(needle[0]) > 1; + const char* const begin_string_quote = is_wide_string ? "L\"" : "\""; + return AssertionFailure() + << "Value of: " << needle_expr << "\n" + << " Actual: " << begin_string_quote << needle << "\"\n" + << "Expected: " << (expected_to_be_substring ? "" : "not ") + << "a substring of " << haystack_expr << "\n" + << "Which is: " << begin_string_quote << haystack << "\""; +} + +} // namespace + +// IsSubstring() and IsNotSubstring() check whether needle is a +// substring of haystack (NULL is considered a substring of itself +// only), and return an appropriate error message when they fail. + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +#if GTEST_HAS_STD_WSTRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +#if GTEST_OS_WINDOWS + +namespace { + +// Helper function for IsHRESULT{SuccessFailure} predicates +AssertionResult HRESULTFailureHelper(const char* expr, + const char* expected, + long hr) { // NOLINT +# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_TV_TITLE + + // Windows CE doesn't support FormatMessage. + const char error_text[] = ""; + +# else + + // Looks up the human-readable system message for the HRESULT code + // and since we're not passing any params to FormatMessage, we don't + // want inserts expanded. + const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS; + const DWORD kBufSize = 4096; + // Gets the system's human readable message string for this HRESULT. + char error_text[kBufSize] = { '\0' }; + DWORD message_length = ::FormatMessageA(kFlags, + 0, // no source, we're asking system + static_cast<DWORD>(hr), // the error + 0, // no line width restrictions + error_text, // output buffer + kBufSize, // buf size + nullptr); // no arguments for inserts + // Trims tailing white space (FormatMessage leaves a trailing CR-LF) + for (; message_length && IsSpace(error_text[message_length - 1]); + --message_length) { + error_text[message_length - 1] = '\0'; + } + +# endif // GTEST_OS_WINDOWS_MOBILE + + const std::string error_hex("0x" + String::FormatHexInt(hr)); + return ::testing::AssertionFailure() + << "Expected: " << expr << " " << expected << ".\n" + << " Actual: " << error_hex << " " << error_text << "\n"; +} + +} // namespace + +AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT + if (SUCCEEDED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "succeeds", hr); +} + +AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT + if (FAILED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "fails", hr); +} + +#endif // GTEST_OS_WINDOWS + +// Utility functions for encoding Unicode text (wide strings) in +// UTF-8. + +// A Unicode code-point can have up to 21 bits, and is encoded in UTF-8 +// like this: +// +// Code-point length Encoding +// 0 - 7 bits 0xxxxxxx +// 8 - 11 bits 110xxxxx 10xxxxxx +// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx +// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + +// The maximum code-point a one-byte UTF-8 sequence can represent. +constexpr uint32_t kMaxCodePoint1 = (static_cast<uint32_t>(1) << 7) - 1; + +// The maximum code-point a two-byte UTF-8 sequence can represent. +constexpr uint32_t kMaxCodePoint2 = (static_cast<uint32_t>(1) << (5 + 6)) - 1; + +// The maximum code-point a three-byte UTF-8 sequence can represent. +constexpr uint32_t kMaxCodePoint3 = (static_cast<uint32_t>(1) << (4 + 2*6)) - 1; + +// The maximum code-point a four-byte UTF-8 sequence can represent. +constexpr uint32_t kMaxCodePoint4 = (static_cast<uint32_t>(1) << (3 + 3*6)) - 1; + +// Chops off the n lowest bits from a bit pattern. Returns the n +// lowest bits. As a side effect, the original bit pattern will be +// shifted to the right by n bits. +inline uint32_t ChopLowBits(uint32_t* bits, int n) { + const uint32_t low_bits = *bits & ((static_cast<uint32_t>(1) << n) - 1); + *bits >>= n; + return low_bits; +} + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type uint32_t because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +std::string CodePointToUtf8(uint32_t code_point) { + if (code_point > kMaxCodePoint4) { + return "(Invalid Unicode 0x" + String::FormatHexUInt32(code_point) + ")"; + } + + char str[5]; // Big enough for the largest valid code point. + if (code_point <= kMaxCodePoint1) { + str[1] = '\0'; + str[0] = static_cast<char>(code_point); // 0xxxxxxx + } else if (code_point <= kMaxCodePoint2) { + str[2] = '\0'; + str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast<char>(0xC0 | code_point); // 110xxxxx + } else if (code_point <= kMaxCodePoint3) { + str[3] = '\0'; + str[2] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast<char>(0xE0 | code_point); // 1110xxxx + } else { // code_point <= kMaxCodePoint4 + str[4] = '\0'; + str[3] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[2] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast<char>(0xF0 | code_point); // 11110xxx + } + return str; +} + +// The following two functions only make sense if the system +// uses UTF-16 for wide string encoding. All supported systems +// with 16 bit wchar_t (Windows, Cygwin) do use UTF-16. + +// Determines if the arguments constitute UTF-16 surrogate pair +// and thus should be combined into a single Unicode code point +// using CreateCodePointFromUtf16SurrogatePair. +inline bool IsUtf16SurrogatePair(wchar_t first, wchar_t second) { + return sizeof(wchar_t) == 2 && + (first & 0xFC00) == 0xD800 && (second & 0xFC00) == 0xDC00; +} + +// Creates a Unicode code point from UTF16 surrogate pair. +inline uint32_t CreateCodePointFromUtf16SurrogatePair(wchar_t first, + wchar_t second) { + const auto first_u = static_cast<uint32_t>(first); + const auto second_u = static_cast<uint32_t>(second); + const uint32_t mask = (1 << 10) - 1; + return (sizeof(wchar_t) == 2) + ? (((first_u & mask) << 10) | (second_u & mask)) + 0x10000 + : + // This function should not be called when the condition is + // false, but we provide a sensible default in case it is. + first_u; +} + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +std::string WideStringToUtf8(const wchar_t* str, int num_chars) { + if (num_chars == -1) + num_chars = static_cast<int>(wcslen(str)); + + ::std::stringstream stream; + for (int i = 0; i < num_chars; ++i) { + uint32_t unicode_code_point; + + if (str[i] == L'\0') { + break; + } else if (i + 1 < num_chars && IsUtf16SurrogatePair(str[i], str[i + 1])) { + unicode_code_point = CreateCodePointFromUtf16SurrogatePair(str[i], + str[i + 1]); + i++; + } else { + unicode_code_point = static_cast<uint32_t>(str[i]); + } + + stream << CodePointToUtf8(unicode_code_point); + } + return StringStreamToString(&stream); +} + +// Converts a wide C string to an std::string using the UTF-8 encoding. +// NULL will be converted to "(null)". +std::string String::ShowWideCString(const wchar_t * wide_c_str) { + if (wide_c_str == nullptr) return "(null)"; + + return internal::WideStringToUtf8(wide_c_str, -1); +} + +// Compares two wide C strings. Returns true if and only if they have the +// same content. +// +// Unlike wcscmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) { + if (lhs == nullptr) return rhs == nullptr; + + if (rhs == nullptr) return false; + + return wcscmp(lhs, rhs) == 0; +} + +// Helper function for *_STREQ on wide strings. +AssertionResult CmpHelperSTREQ(const char* lhs_expression, + const char* rhs_expression, + const wchar_t* lhs, + const wchar_t* rhs) { + if (String::WideCStringEquals(lhs, rhs)) { + return AssertionSuccess(); + } + + return EqFailure(lhs_expression, + rhs_expression, + PrintToString(lhs), + PrintToString(rhs), + false); +} + +// Helper function for *_STRNE on wide strings. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2) { + if (!String::WideCStringEquals(s1, s2)) { + return AssertionSuccess(); + } + + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: " + << PrintToString(s1) + << " vs " << PrintToString(s2); +} + +// Compares two C strings, ignoring case. Returns true if and only if they have +// the same content. +// +// Unlike strcasecmp(), this function can handle NULL argument(s). A +// NULL C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) { + if (lhs == nullptr) return rhs == nullptr; + if (rhs == nullptr) return false; + return posix::StrCaseCmp(lhs, rhs) == 0; +} + +// Compares two wide C strings, ignoring case. Returns true if and only if they +// have the same content. +// +// Unlike wcscasecmp(), this function can handle NULL argument(s). +// A NULL C string is considered different to any non-NULL wide C string, +// including the empty string. +// NB: The implementations on different platforms slightly differ. +// On windows, this method uses _wcsicmp which compares according to LC_CTYPE +// environment variable. On GNU platform this method uses wcscasecmp +// which compares according to LC_CTYPE category of the current locale. +// On MacOS X, it uses towlower, which also uses LC_CTYPE category of the +// current locale. +bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs) { + if (lhs == nullptr) return rhs == nullptr; + + if (rhs == nullptr) return false; + +#if GTEST_OS_WINDOWS + return _wcsicmp(lhs, rhs) == 0; +#elif GTEST_OS_LINUX && !GTEST_OS_LINUX_ANDROID + return wcscasecmp(lhs, rhs) == 0; +#else + // Android, Mac OS X and Cygwin don't define wcscasecmp. + // Other unknown OSes may not define it either. + wint_t left, right; + do { + left = towlower(static_cast<wint_t>(*lhs++)); + right = towlower(static_cast<wint_t>(*rhs++)); + } while (left && left == right); + return left == right; +#endif // OS selector +} + +// Returns true if and only if str ends with the given suffix, ignoring case. +// Any string is considered to end with an empty suffix. +bool String::EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix) { + const size_t str_len = str.length(); + const size_t suffix_len = suffix.length(); + return (str_len >= suffix_len) && + CaseInsensitiveCStringEquals(str.c_str() + str_len - suffix_len, + suffix.c_str()); +} + +// Formats an int value as "%02d". +std::string String::FormatIntWidth2(int value) { + return FormatIntWidthN(value, 2); +} + +// Formats an int value to given width with leading zeros. +std::string String::FormatIntWidthN(int value, int width) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(width) << value; + return ss.str(); +} + +// Formats an int value as "%X". +std::string String::FormatHexUInt32(uint32_t value) { + std::stringstream ss; + ss << std::hex << std::uppercase << value; + return ss.str(); +} + +// Formats an int value as "%X". +std::string String::FormatHexInt(int value) { + return FormatHexUInt32(static_cast<uint32_t>(value)); +} + +// Formats a byte as "%02X". +std::string String::FormatByte(unsigned char value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << std::hex << std::uppercase + << static_cast<unsigned int>(value); + return ss.str(); +} + +// Converts the buffer in a stringstream to an std::string, converting NUL +// bytes to "\\0" along the way. +std::string StringStreamToString(::std::stringstream* ss) { + const ::std::string& str = ss->str(); + const char* const start = str.c_str(); + const char* const end = start + str.length(); + + std::string result; + result.reserve(static_cast<size_t>(2 * (end - start))); + for (const char* ch = start; ch != end; ++ch) { + if (*ch == '\0') { + result += "\\0"; // Replaces NUL with "\\0"; + } else { + result += *ch; + } + } + + return result; +} + +// Appends the user-supplied message to the Google-Test-generated message. +std::string AppendUserMessage(const std::string& gtest_msg, + const Message& user_msg) { + // Appends the user message if it's non-empty. + const std::string user_msg_string = user_msg.GetString(); + if (user_msg_string.empty()) { + return gtest_msg; + } + if (gtest_msg.empty()) { + return user_msg_string; + } + return gtest_msg + "\n" + user_msg_string; +} + +} // namespace internal + +// class TestResult + +// Creates an empty TestResult. +TestResult::TestResult() + : death_test_count_(0), start_timestamp_(0), elapsed_time_(0) {} + +// D'tor. +TestResult::~TestResult() { +} + +// Returns the i-th test part result among all the results. i can +// range from 0 to total_part_count() - 1. If i is not in that range, +// aborts the program. +const TestPartResult& TestResult::GetTestPartResult(int i) const { + if (i < 0 || i >= total_part_count()) + internal::posix::Abort(); + return test_part_results_.at(static_cast<size_t>(i)); +} + +// Returns the i-th test property. i can range from 0 to +// test_property_count() - 1. If i is not in that range, aborts the +// program. +const TestProperty& TestResult::GetTestProperty(int i) const { + if (i < 0 || i >= test_property_count()) + internal::posix::Abort(); + return test_properties_.at(static_cast<size_t>(i)); +} + +// Clears the test part results. +void TestResult::ClearTestPartResults() { + test_part_results_.clear(); +} + +// Adds a test part result to the list. +void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { + test_part_results_.push_back(test_part_result); +} + +// Adds a test property to the list. If a property with the same key as the +// supplied property is already represented, the value of this test_property +// replaces the old value for that key. +void TestResult::RecordProperty(const std::string& xml_element, + const TestProperty& test_property) { + if (!ValidateTestProperty(xml_element, test_property)) { + return; + } + internal::MutexLock lock(&test_properties_mutex_); + const std::vector<TestProperty>::iterator property_with_matching_key = + std::find_if(test_properties_.begin(), test_properties_.end(), + internal::TestPropertyKeyIs(test_property.key())); + if (property_with_matching_key == test_properties_.end()) { + test_properties_.push_back(test_property); + return; + } + property_with_matching_key->SetValue(test_property.value()); +} + +// The list of reserved attributes used in the <testsuites> element of XML +// output. +static const char* const kReservedTestSuitesAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "random_seed", + "tests", + "time", + "timestamp" +}; + +// The list of reserved attributes used in the <testsuite> element of XML +// output. +static const char* const kReservedTestSuiteAttributes[] = { + "disabled", "errors", "failures", "name", + "tests", "time", "timestamp", "skipped"}; + +// The list of reserved attributes used in the <testcase> element of XML output. +static const char* const kReservedTestCaseAttributes[] = { + "classname", "name", "status", "time", "type_param", + "value_param", "file", "line"}; + +// Use a slightly different set for allowed output to ensure existing tests can +// still RecordProperty("result") or "RecordProperty(timestamp") +static const char* const kReservedOutputTestCaseAttributes[] = { + "classname", "name", "status", "time", "type_param", + "value_param", "file", "line", "result", "timestamp"}; + +template <size_t kSize> +std::vector<std::string> ArrayAsVector(const char* const (&array)[kSize]) { + return std::vector<std::string>(array, array + kSize); +} + +static std::vector<std::string> GetReservedAttributesForElement( + const std::string& xml_element) { + if (xml_element == "testsuites") { + return ArrayAsVector(kReservedTestSuitesAttributes); + } else if (xml_element == "testsuite") { + return ArrayAsVector(kReservedTestSuiteAttributes); + } else if (xml_element == "testcase") { + return ArrayAsVector(kReservedTestCaseAttributes); + } else { + GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element; + } + // This code is unreachable but some compilers may not realizes that. + return std::vector<std::string>(); +} + +// TODO(jdesprez): Merge the two getReserved attributes once skip is improved +static std::vector<std::string> GetReservedOutputAttributesForElement( + const std::string& xml_element) { + if (xml_element == "testsuites") { + return ArrayAsVector(kReservedTestSuitesAttributes); + } else if (xml_element == "testsuite") { + return ArrayAsVector(kReservedTestSuiteAttributes); + } else if (xml_element == "testcase") { + return ArrayAsVector(kReservedOutputTestCaseAttributes); + } else { + GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element; + } + // This code is unreachable but some compilers may not realizes that. + return std::vector<std::string>(); +} + +static std::string FormatWordList(const std::vector<std::string>& words) { + Message word_list; + for (size_t i = 0; i < words.size(); ++i) { + if (i > 0 && words.size() > 2) { + word_list << ", "; + } + if (i == words.size() - 1) { + word_list << "and "; + } + word_list << "'" << words[i] << "'"; + } + return word_list.GetString(); +} + +static bool ValidateTestPropertyName( + const std::string& property_name, + const std::vector<std::string>& reserved_names) { + if (std::find(reserved_names.begin(), reserved_names.end(), property_name) != + reserved_names.end()) { + ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name + << " (" << FormatWordList(reserved_names) + << " are reserved by " << GTEST_NAME_ << ")"; + return false; + } + return true; +} + +// Adds a failure if the key is a reserved attribute of the element named +// xml_element. Returns true if the property is valid. +bool TestResult::ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property) { + return ValidateTestPropertyName(test_property.key(), + GetReservedAttributesForElement(xml_element)); +} + +// Clears the object. +void TestResult::Clear() { + test_part_results_.clear(); + test_properties_.clear(); + death_test_count_ = 0; + elapsed_time_ = 0; +} + +// Returns true off the test part was skipped. +static bool TestPartSkipped(const TestPartResult& result) { + return result.skipped(); +} + +// Returns true if and only if the test was skipped. +bool TestResult::Skipped() const { + return !Failed() && CountIf(test_part_results_, TestPartSkipped) > 0; +} + +// Returns true if and only if the test failed. +bool TestResult::Failed() const { + for (int i = 0; i < total_part_count(); ++i) { + if (GetTestPartResult(i).failed()) + return true; + } + return false; +} + +// Returns true if and only if the test part fatally failed. +static bool TestPartFatallyFailed(const TestPartResult& result) { + return result.fatally_failed(); +} + +// Returns true if and only if the test fatally failed. +bool TestResult::HasFatalFailure() const { + return CountIf(test_part_results_, TestPartFatallyFailed) > 0; +} + +// Returns true if and only if the test part non-fatally failed. +static bool TestPartNonfatallyFailed(const TestPartResult& result) { + return result.nonfatally_failed(); +} + +// Returns true if and only if the test has a non-fatal failure. +bool TestResult::HasNonfatalFailure() const { + return CountIf(test_part_results_, TestPartNonfatallyFailed) > 0; +} + +// Gets the number of all test parts. This is the sum of the number +// of successful test parts and the number of failed test parts. +int TestResult::total_part_count() const { + return static_cast<int>(test_part_results_.size()); +} + +// Returns the number of the test properties. +int TestResult::test_property_count() const { + return static_cast<int>(test_properties_.size()); +} + +// class Test + +// Creates a Test object. + +// The c'tor saves the states of all flags. +Test::Test() + : gtest_flag_saver_(new GTEST_FLAG_SAVER_) { +} + +// The d'tor restores the states of all flags. The actual work is +// done by the d'tor of the gtest_flag_saver_ field, and thus not +// visible here. +Test::~Test() { +} + +// Sets up the test fixture. +// +// A sub-class may override this. +void Test::SetUp() { +} + +// Tears down the test fixture. +// +// A sub-class may override this. +void Test::TearDown() { +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, const std::string& value) { + UnitTest::GetInstance()->RecordProperty(key, value); +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, int value) { + Message value_message; + value_message << value; + RecordProperty(key, value_message.GetString().c_str()); +} + +namespace internal { + +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message) { + // This function is a friend of UnitTest and as such has access to + // AddTestPartResult. + UnitTest::GetInstance()->AddTestPartResult( + result_type, + nullptr, // No info about the source file where the exception occurred. + -1, // We have no info on which line caused the exception. + message, + ""); // No stack trace, either. +} + +} // namespace internal + +// Google Test requires all tests in the same test suite to use the same test +// fixture class. This function checks if the current test has the +// same fixture class as the first test in the current test suite. If +// yes, it returns true; otherwise it generates a Google Test failure and +// returns false. +bool Test::HasSameFixtureClass() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + const TestSuite* const test_suite = impl->current_test_suite(); + + // Info about the first test in the current test suite. + const TestInfo* const first_test_info = test_suite->test_info_list()[0]; + const internal::TypeId first_fixture_id = first_test_info->fixture_class_id_; + const char* const first_test_name = first_test_info->name(); + + // Info about the current test. + const TestInfo* const this_test_info = impl->current_test_info(); + const internal::TypeId this_fixture_id = this_test_info->fixture_class_id_; + const char* const this_test_name = this_test_info->name(); + + if (this_fixture_id != first_fixture_id) { + // Is the first test defined using TEST? + const bool first_is_TEST = first_fixture_id == internal::GetTestTypeId(); + // Is this test defined using TEST? + const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId(); + + if (first_is_TEST || this_is_TEST) { + // Both TEST and TEST_F appear in same test suite, which is incorrect. + // Tell the user how to fix this. + + // Gets the name of the TEST and the name of the TEST_F. Note + // that first_is_TEST and this_is_TEST cannot both be true, as + // the fixture IDs are different for the two tests. + const char* const TEST_name = + first_is_TEST ? first_test_name : this_test_name; + const char* const TEST_F_name = + first_is_TEST ? this_test_name : first_test_name; + + ADD_FAILURE() + << "All tests in the same test suite must use the same test fixture\n" + << "class, so mixing TEST_F and TEST in the same test suite is\n" + << "illegal. In test suite " << this_test_info->test_suite_name() + << ",\n" + << "test " << TEST_F_name << " is defined using TEST_F but\n" + << "test " << TEST_name << " is defined using TEST. You probably\n" + << "want to change the TEST to TEST_F or move it to another test\n" + << "case."; + } else { + // Two fixture classes with the same name appear in two different + // namespaces, which is not allowed. Tell the user how to fix this. + ADD_FAILURE() + << "All tests in the same test suite must use the same test fixture\n" + << "class. However, in test suite " + << this_test_info->test_suite_name() << ",\n" + << "you defined test " << first_test_name << " and test " + << this_test_name << "\n" + << "using two different test fixture classes. This can happen if\n" + << "the two classes are from different namespaces or translation\n" + << "units and have the same name. You should probably rename one\n" + << "of the classes to put the tests into different test suites."; + } + return false; + } + + return true; +} + +#if GTEST_HAS_SEH + +// Adds an "exception thrown" fatal failure to the current test. This +// function returns its result via an output parameter pointer because VC++ +// prohibits creation of objects with destructors on stack in functions +// using __try (see error C2712). +static std::string* FormatSehExceptionMessage(DWORD exception_code, + const char* location) { + Message message; + message << "SEH exception with code 0x" << std::setbase(16) << + exception_code << std::setbase(10) << " thrown in " << location << "."; + + return new std::string(message.GetString()); +} + +#endif // GTEST_HAS_SEH + +namespace internal { + +#if GTEST_HAS_EXCEPTIONS + +// Adds an "exception thrown" fatal failure to the current test. +static std::string FormatCxxExceptionMessage(const char* description, + const char* location) { + Message message; + if (description != nullptr) { + message << "C++ exception with description \"" << description << "\""; + } else { + message << "Unknown C++ exception"; + } + message << " thrown in " << location << "."; + + return message.GetString(); +} + +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result); + +GoogleTestFailureException::GoogleTestFailureException( + const TestPartResult& failure) + : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} + +#endif // GTEST_HAS_EXCEPTIONS + +// We put these helper functions in the internal namespace as IBM's xlC +// compiler rejects the code if they were declared static. + +// Runs the given method and handles SEH exceptions it throws, when +// SEH is supported; returns the 0-value for type Result in case of an +// SEH exception. (Microsoft compilers cannot handle SEH and C++ +// exceptions in the same function. Therefore, we provide a separate +// wrapper function for handling SEH exceptions.) +template <class T, typename Result> +Result HandleSehExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { +#if GTEST_HAS_SEH + __try { + return (object->*method)(); + } __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT + GetExceptionCode())) { + // We create the exception message on the heap because VC++ prohibits + // creation of objects with destructors on stack in functions using __try + // (see error C2712). + std::string* exception_message = FormatSehExceptionMessage( + GetExceptionCode(), location); + internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, + *exception_message); + delete exception_message; + return static_cast<Result>(0); + } +#else + (void)location; + return (object->*method)(); +#endif // GTEST_HAS_SEH +} + +// Runs the given method and catches and reports C++ and/or SEH-style +// exceptions, if they are supported; returns the 0-value for type +// Result in case of an SEH exception. +template <class T, typename Result> +Result HandleExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { + // NOTE: The user code can affect the way in which Google Test handles + // exceptions by setting GTEST_FLAG(catch_exceptions), but only before + // RUN_ALL_TESTS() starts. It is technically possible to check the flag + // after the exception is caught and either report or re-throw the + // exception based on the flag's value: + // + // try { + // // Perform the test method. + // } catch (...) { + // if (GTEST_FLAG(catch_exceptions)) + // // Report the exception as failure. + // else + // throw; // Re-throws the original exception. + // } + // + // However, the purpose of this flag is to allow the program to drop into + // the debugger when the exception is thrown. On most platforms, once the + // control enters the catch block, the exception origin information is + // lost and the debugger will stop the program at the point of the + // re-throw in this function -- instead of at the point of the original + // throw statement in the code under test. For this reason, we perform + // the check early, sacrificing the ability to affect Google Test's + // exception handling in the method where the exception is thrown. + if (internal::GetUnitTestImpl()->catch_exceptions()) { +#if GTEST_HAS_EXCEPTIONS + try { + return HandleSehExceptionsInMethodIfSupported(object, method, location); + } catch (const AssertionException&) { // NOLINT + // This failure was reported already. + } catch (const internal::GoogleTestFailureException&) { // NOLINT + // This exception type can only be thrown by a failed Google + // Test assertion with the intention of letting another testing + // framework catch it. Therefore we just re-throw it. + throw; + } catch (const std::exception& e) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(e.what(), location)); + } catch (...) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(nullptr, location)); + } + return static_cast<Result>(0); +#else + return HandleSehExceptionsInMethodIfSupported(object, method, location); +#endif // GTEST_HAS_EXCEPTIONS + } else { + return (object->*method)(); + } +} + +} // namespace internal + +// Runs the test and updates the test result. +void Test::Run() { + if (!HasSameFixtureClass()) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()"); + // We will run the test only if SetUp() was successful and didn't call + // GTEST_SKIP(). + if (!HasFatalFailure() && !IsSkipped()) { + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TestBody, "the test body"); + } + + // However, we want to clean up as much as possible. Hence we will + // always call TearDown(), even if SetUp() or the test body has + // failed. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TearDown, "TearDown()"); +} + +// Returns true if and only if the current test has a fatal failure. +bool Test::HasFatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure(); +} + +// Returns true if and only if the current test has a non-fatal failure. +bool Test::HasNonfatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()-> + HasNonfatalFailure(); +} + +// Returns true if and only if the current test was skipped. +bool Test::IsSkipped() { + return internal::GetUnitTestImpl()->current_test_result()->Skipped(); +} + +// class TestInfo + +// Constructs a TestInfo object. It assumes ownership of the test factory +// object. +TestInfo::TestInfo(const std::string& a_test_suite_name, + const std::string& a_name, const char* a_type_param, + const char* a_value_param, + internal::CodeLocation a_code_location, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory) + : test_suite_name_(a_test_suite_name), + name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : nullptr), + value_param_(a_value_param ? new std::string(a_value_param) : nullptr), + location_(a_code_location), + fixture_class_id_(fixture_class_id), + should_run_(false), + is_disabled_(false), + matches_filter_(false), + is_in_another_shard_(false), + factory_(factory), + result_() {} + +// Destructs a TestInfo object. +TestInfo::~TestInfo() { delete factory_; } + +namespace internal { + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_suite_name: name of the test suite +// name: name of the test +// type_param: the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param: text representation of the test's value parameter, +// or NULL if this is not a value-parameterized test. +// code_location: code location where the test is defined +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test suite +// tear_down_tc: pointer to the function that tears down the test suite +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +TestInfo* MakeAndRegisterTestInfo( + const char* test_suite_name, const char* name, const char* type_param, + const char* value_param, CodeLocation code_location, + TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc, + TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory) { + TestInfo* const test_info = + new TestInfo(test_suite_name, name, type_param, value_param, + code_location, fixture_class_id, factory); + GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); + return test_info; +} + +void ReportInvalidTestSuiteType(const char* test_suite_name, + CodeLocation code_location) { + Message errors; + errors + << "Attempted redefinition of test suite " << test_suite_name << ".\n" + << "All tests in the same test suite must use the same test fixture\n" + << "class. However, in test suite " << test_suite_name << ", you tried\n" + << "to define a test using a fixture class different from the one\n" + << "used earlier. This can happen if the two fixture classes are\n" + << "from different namespaces and have the same name. You should\n" + << "probably rename one of the classes to put the tests into different\n" + << "test suites."; + + GTEST_LOG_(ERROR) << FormatFileLocation(code_location.file.c_str(), + code_location.line) + << " " << errors.GetString(); +} +} // namespace internal + +namespace { + +// A predicate that checks the test name of a TestInfo against a known +// value. +// +// This is used for implementation of the TestSuite class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestNameIs is copyable. +class TestNameIs { + public: + // Constructor. + // + // TestNameIs has NO default constructor. + explicit TestNameIs(const char* name) + : name_(name) {} + + // Returns true if and only if the test name of test_info matches name_. + bool operator()(const TestInfo * test_info) const { + return test_info && test_info->name() == name_; + } + + private: + std::string name_; +}; + +} // namespace + +namespace internal { + +// This method expands all parameterized tests registered with macros TEST_P +// and INSTANTIATE_TEST_SUITE_P into regular tests and registers those. +// This will be done just once during the program runtime. +void UnitTestImpl::RegisterParameterizedTests() { + if (!parameterized_tests_registered_) { + parameterized_test_registry_.RegisterTests(); + type_parameterized_test_registry_.CheckForInstantiations(); + parameterized_tests_registered_ = true; + } +} + +} // namespace internal + +// Creates the test object, runs it, records its result, and then +// deletes it. +void TestInfo::Run() { + if (!should_run_) return; + + // Tells UnitTest where to store test result. + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_info(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + // Notifies the unit test event listeners that a test is about to start. + repeater->OnTestStart(*this); + + result_.set_start_timestamp(internal::GetTimeInMillis()); + internal::Timer timer; + + impl->os_stack_trace_getter()->UponLeavingGTest(); + + // Creates the test object. + Test* const test = internal::HandleExceptionsInMethodIfSupported( + factory_, &internal::TestFactoryBase::CreateTest, + "the test fixture's constructor"); + + // Runs the test if the constructor didn't generate a fatal failure or invoke + // GTEST_SKIP(). + // Note that the object will not be null + if (!Test::HasFatalFailure() && !Test::IsSkipped()) { + // This doesn't throw as all user code that can throw are wrapped into + // exception handling code. + test->Run(); + } + + if (test != nullptr) { + // Deletes the test object. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + test, &Test::DeleteSelf_, "the test fixture's destructor"); + } + + result_.set_elapsed_time(timer.Elapsed()); + + // Notifies the unit test event listener that a test has just finished. + repeater->OnTestEnd(*this); + + // Tells UnitTest to stop associating assertion results to this + // test. + impl->set_current_test_info(nullptr); +} + +// Skip and records a skipped test result for this object. +void TestInfo::Skip() { + if (!should_run_) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_info(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + // Notifies the unit test event listeners that a test is about to start. + repeater->OnTestStart(*this); + + const TestPartResult test_part_result = + TestPartResult(TestPartResult::kSkip, this->file(), this->line(), ""); + impl->GetTestPartResultReporterForCurrentThread()->ReportTestPartResult( + test_part_result); + + // Notifies the unit test event listener that a test has just finished. + repeater->OnTestEnd(*this); + impl->set_current_test_info(nullptr); +} + +// class TestSuite + +// Gets the number of successful tests in this test suite. +int TestSuite::successful_test_count() const { + return CountIf(test_info_list_, TestPassed); +} + +// Gets the number of successful tests in this test suite. +int TestSuite::skipped_test_count() const { + return CountIf(test_info_list_, TestSkipped); +} + +// Gets the number of failed tests in this test suite. +int TestSuite::failed_test_count() const { + return CountIf(test_info_list_, TestFailed); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int TestSuite::reportable_disabled_test_count() const { + return CountIf(test_info_list_, TestReportableDisabled); +} + +// Gets the number of disabled tests in this test suite. +int TestSuite::disabled_test_count() const { + return CountIf(test_info_list_, TestDisabled); +} + +// Gets the number of tests to be printed in the XML report. +int TestSuite::reportable_test_count() const { + return CountIf(test_info_list_, TestReportable); +} + +// Get the number of tests in this test suite that should run. +int TestSuite::test_to_run_count() const { + return CountIf(test_info_list_, ShouldRunTest); +} + +// Gets the number of all tests. +int TestSuite::total_test_count() const { + return static_cast<int>(test_info_list_.size()); +} + +// Creates a TestSuite with the given name. +// +// Arguments: +// +// a_name: name of the test suite +// a_type_param: the name of the test suite's type parameter, or NULL if +// this is not a typed or a type-parameterized test suite. +// set_up_tc: pointer to the function that sets up the test suite +// tear_down_tc: pointer to the function that tears down the test suite +TestSuite::TestSuite(const char* a_name, const char* a_type_param, + internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc) + : name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : nullptr), + set_up_tc_(set_up_tc), + tear_down_tc_(tear_down_tc), + should_run_(false), + start_timestamp_(0), + elapsed_time_(0) {} + +// Destructor of TestSuite. +TestSuite::~TestSuite() { + // Deletes every Test in the collection. + ForEach(test_info_list_, internal::Delete<TestInfo>); +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +const TestInfo* TestSuite::GetTestInfo(int i) const { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? nullptr : test_info_list_[static_cast<size_t>(index)]; +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +TestInfo* TestSuite::GetMutableTestInfo(int i) { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? nullptr : test_info_list_[static_cast<size_t>(index)]; +} + +// Adds a test to this test suite. Will delete the test upon +// destruction of the TestSuite object. +void TestSuite::AddTestInfo(TestInfo* test_info) { + test_info_list_.push_back(test_info); + test_indices_.push_back(static_cast<int>(test_indices_.size())); +} + +// Runs every test in this TestSuite. +void TestSuite::Run() { + if (!should_run_) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_suite(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + // Call both legacy and the new API + repeater->OnTestSuiteStart(*this); +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + repeater->OnTestCaseStart(*this); +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestSuite::RunSetUpTestSuite, "SetUpTestSuite()"); + + start_timestamp_ = internal::GetTimeInMillis(); + internal::Timer timer; + for (int i = 0; i < total_test_count(); i++) { + GetMutableTestInfo(i)->Run(); + if (GTEST_FLAG(fail_fast) && GetMutableTestInfo(i)->result()->Failed()) { + for (int j = i + 1; j < total_test_count(); j++) { + GetMutableTestInfo(j)->Skip(); + } + break; + } + } + elapsed_time_ = timer.Elapsed(); + + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestSuite::RunTearDownTestSuite, "TearDownTestSuite()"); + + // Call both legacy and the new API + repeater->OnTestSuiteEnd(*this); +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + repeater->OnTestCaseEnd(*this); +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + impl->set_current_test_suite(nullptr); +} + +// Skips all tests under this TestSuite. +void TestSuite::Skip() { + if (!should_run_) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_suite(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + // Call both legacy and the new API + repeater->OnTestSuiteStart(*this); +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + repeater->OnTestCaseStart(*this); +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + for (int i = 0; i < total_test_count(); i++) { + GetMutableTestInfo(i)->Skip(); + } + + // Call both legacy and the new API + repeater->OnTestSuiteEnd(*this); + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + repeater->OnTestCaseEnd(*this); +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + impl->set_current_test_suite(nullptr); +} + +// Clears the results of all tests in this test suite. +void TestSuite::ClearResult() { + ad_hoc_test_result_.Clear(); + ForEach(test_info_list_, TestInfo::ClearTestResult); +} + +// Shuffles the tests in this test suite. +void TestSuite::ShuffleTests(internal::Random* random) { + Shuffle(random, &test_indices_); +} + +// Restores the test order to before the first shuffle. +void TestSuite::UnshuffleTests() { + for (size_t i = 0; i < test_indices_.size(); i++) { + test_indices_[i] = static_cast<int>(i); + } +} + +// Formats a countable noun. Depending on its quantity, either the +// singular form or the plural form is used. e.g. +// +// FormatCountableNoun(1, "formula", "formuli") returns "1 formula". +// FormatCountableNoun(5, "book", "books") returns "5 books". +static std::string FormatCountableNoun(int count, + const char * singular_form, + const char * plural_form) { + return internal::StreamableToString(count) + " " + + (count == 1 ? singular_form : plural_form); +} + +// Formats the count of tests. +static std::string FormatTestCount(int test_count) { + return FormatCountableNoun(test_count, "test", "tests"); +} + +// Formats the count of test suites. +static std::string FormatTestSuiteCount(int test_suite_count) { + return FormatCountableNoun(test_suite_count, "test suite", "test suites"); +} + +// Converts a TestPartResult::Type enum to human-friendly string +// representation. Both kNonFatalFailure and kFatalFailure are translated +// to "Failure", as the user usually doesn't care about the difference +// between the two when viewing the test result. +static const char * TestPartResultTypeToString(TestPartResult::Type type) { + switch (type) { + case TestPartResult::kSkip: + return "Skipped\n"; + case TestPartResult::kSuccess: + return "Success"; + + case TestPartResult::kNonFatalFailure: + case TestPartResult::kFatalFailure: +#ifdef _MSC_VER + return "error: "; +#else + return "Failure\n"; +#endif + default: + return "Unknown result type"; + } +} + +namespace internal { +namespace { +enum class GTestColor { kDefault, kRed, kGreen, kYellow }; +} // namespace + +// Prints a TestPartResult to an std::string. +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result) { + return (Message() + << internal::FormatFileLocation(test_part_result.file_name(), + test_part_result.line_number()) + << " " << TestPartResultTypeToString(test_part_result.type()) + << test_part_result.message()).GetString(); +} + +// Prints a TestPartResult. +static void PrintTestPartResult(const TestPartResult& test_part_result) { + const std::string& result = + PrintTestPartResultToString(test_part_result); + printf("%s\n", result.c_str()); + fflush(stdout); + // If the test program runs in Visual Studio or a debugger, the + // following statements add the test part result message to the Output + // window such that the user can double-click on it to jump to the + // corresponding source code location; otherwise they do nothing. +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + // We don't call OutputDebugString*() on Windows Mobile, as printing + // to stdout is done by OutputDebugString() there already - we don't + // want the same message printed twice. + ::OutputDebugStringA(result.c_str()); + ::OutputDebugStringA("\n"); +#endif +} + +// class PrettyUnitTestResultPrinter +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \ + !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT && !GTEST_OS_WINDOWS_MINGW + +// Returns the character attribute for the given color. +static WORD GetColorAttribute(GTestColor color) { + switch (color) { + case GTestColor::kRed: + return FOREGROUND_RED; + case GTestColor::kGreen: + return FOREGROUND_GREEN; + case GTestColor::kYellow: + return FOREGROUND_RED | FOREGROUND_GREEN; + default: return 0; + } +} + +static int GetBitOffset(WORD color_mask) { + if (color_mask == 0) return 0; + + int bitOffset = 0; + while ((color_mask & 1) == 0) { + color_mask >>= 1; + ++bitOffset; + } + return bitOffset; +} + +static WORD GetNewColor(GTestColor color, WORD old_color_attrs) { + // Let's reuse the BG + static const WORD background_mask = BACKGROUND_BLUE | BACKGROUND_GREEN | + BACKGROUND_RED | BACKGROUND_INTENSITY; + static const WORD foreground_mask = FOREGROUND_BLUE | FOREGROUND_GREEN | + FOREGROUND_RED | FOREGROUND_INTENSITY; + const WORD existing_bg = old_color_attrs & background_mask; + + WORD new_color = + GetColorAttribute(color) | existing_bg | FOREGROUND_INTENSITY; + static const int bg_bitOffset = GetBitOffset(background_mask); + static const int fg_bitOffset = GetBitOffset(foreground_mask); + + if (((new_color & background_mask) >> bg_bitOffset) == + ((new_color & foreground_mask) >> fg_bitOffset)) { + new_color ^= FOREGROUND_INTENSITY; // invert intensity + } + return new_color; +} + +#else + +// Returns the ANSI color code for the given color. GTestColor::kDefault is +// an invalid input. +static const char* GetAnsiColorCode(GTestColor color) { + switch (color) { + case GTestColor::kRed: + return "1"; + case GTestColor::kGreen: + return "2"; + case GTestColor::kYellow: + return "3"; + default: + return nullptr; + } +} + +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + +// Returns true if and only if Google Test should use colors in the output. +bool ShouldUseColor(bool stdout_is_tty) { + const char* const gtest_color = GTEST_FLAG(color).c_str(); + + if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) { +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW + // On Windows the TERM variable is usually not set, but the + // console there does support colors. + return stdout_is_tty; +#else + // On non-Windows platforms, we rely on the TERM variable. + const char* const term = posix::GetEnv("TERM"); + const bool term_supports_color = + String::CStringEquals(term, "xterm") || + String::CStringEquals(term, "xterm-color") || + String::CStringEquals(term, "xterm-256color") || + String::CStringEquals(term, "screen") || + String::CStringEquals(term, "screen-256color") || + String::CStringEquals(term, "tmux") || + String::CStringEquals(term, "tmux-256color") || + String::CStringEquals(term, "rxvt-unicode") || + String::CStringEquals(term, "rxvt-unicode-256color") || + String::CStringEquals(term, "linux") || + String::CStringEquals(term, "cygwin"); + return stdout_is_tty && term_supports_color; +#endif // GTEST_OS_WINDOWS + } + + return String::CaseInsensitiveCStringEquals(gtest_color, "yes") || + String::CaseInsensitiveCStringEquals(gtest_color, "true") || + String::CaseInsensitiveCStringEquals(gtest_color, "t") || + String::CStringEquals(gtest_color, "1"); + // We take "yes", "true", "t", and "1" as meaning "yes". If the + // value is neither one of these nor "auto", we treat it as "no" to + // be conservative. +} + +// Helpers for printing colored strings to stdout. Note that on Windows, we +// cannot simply emit special characters and have the terminal change colors. +// This routine must actually emit the characters rather than return a string +// that would be colored when printed, as can be done on Linux. + +GTEST_ATTRIBUTE_PRINTF_(2, 3) +static void ColoredPrintf(GTestColor color, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_ZOS || GTEST_OS_IOS || \ + GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT || defined(ESP_PLATFORM) + const bool use_color = AlwaysFalse(); +#else + static const bool in_color_mode = + ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0); + const bool use_color = in_color_mode && (color != GTestColor::kDefault); +#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_ZOS + + if (!use_color) { + vprintf(fmt, args); + va_end(args); + return; + } + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \ + !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT && !GTEST_OS_WINDOWS_MINGW + const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + // Gets the current text color. + CONSOLE_SCREEN_BUFFER_INFO buffer_info; + GetConsoleScreenBufferInfo(stdout_handle, &buffer_info); + const WORD old_color_attrs = buffer_info.wAttributes; + const WORD new_color = GetNewColor(color, old_color_attrs); + + // We need to flush the stream buffers into the console before each + // SetConsoleTextAttribute call lest it affect the text that is already + // printed but has not yet reached the console. + fflush(stdout); + SetConsoleTextAttribute(stdout_handle, new_color); + + vprintf(fmt, args); + + fflush(stdout); + // Restores the text color. + SetConsoleTextAttribute(stdout_handle, old_color_attrs); +#else + printf("\033[0;3%sm", GetAnsiColorCode(color)); + vprintf(fmt, args); + printf("\033[m"); // Resets the terminal to default. +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + va_end(args); +} + +// Text printed in Google Test's text output and --gtest_list_tests +// output to label the type parameter and value parameter for a test. +static const char kTypeParamLabel[] = "TypeParam"; +static const char kValueParamLabel[] = "GetParam()"; + +static void PrintFullTestCommentIfPresent(const TestInfo& test_info) { + const char* const type_param = test_info.type_param(); + const char* const value_param = test_info.value_param(); + + if (type_param != nullptr || value_param != nullptr) { + printf(", where "); + if (type_param != nullptr) { + printf("%s = %s", kTypeParamLabel, type_param); + if (value_param != nullptr) printf(" and "); + } + if (value_param != nullptr) { + printf("%s = %s", kValueParamLabel, value_param); + } + } +} + +// This class implements the TestEventListener interface. +// +// Class PrettyUnitTestResultPrinter is copyable. +class PrettyUnitTestResultPrinter : public TestEventListener { + public: + PrettyUnitTestResultPrinter() {} + static void PrintTestName(const char* test_suite, const char* test) { + printf("%s.%s", test_suite, test); + } + + // The following methods override what's in the TestEventListener class. + void OnTestProgramStart(const UnitTest& /*unit_test*/) override {} + void OnTestIterationStart(const UnitTest& unit_test, int iteration) override; + void OnEnvironmentsSetUpStart(const UnitTest& unit_test) override; + void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) override {} +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseStart(const TestCase& test_case) override; +#else + void OnTestSuiteStart(const TestSuite& test_suite) override; +#endif // OnTestCaseStart + + void OnTestStart(const TestInfo& test_info) override; + + void OnTestPartResult(const TestPartResult& result) override; + void OnTestEnd(const TestInfo& test_info) override; +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseEnd(const TestCase& test_case) override; +#else + void OnTestSuiteEnd(const TestSuite& test_suite) override; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnEnvironmentsTearDownStart(const UnitTest& unit_test) override; + void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) override {} + void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override; + void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {} + + private: + static void PrintFailedTests(const UnitTest& unit_test); + static void PrintFailedTestSuites(const UnitTest& unit_test); + static void PrintSkippedTests(const UnitTest& unit_test); +}; + + // Fired before each iteration of tests starts. +void PrettyUnitTestResultPrinter::OnTestIterationStart( + const UnitTest& unit_test, int iteration) { + if (GTEST_FLAG(repeat) != 1) + printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration + 1); + + const char* const filter = GTEST_FLAG(filter).c_str(); + + // Prints the filter if it's not *. This reminds the user that some + // tests may be skipped. + if (!String::CStringEquals(filter, kUniversalFilter)) { + ColoredPrintf(GTestColor::kYellow, "Note: %s filter = %s\n", GTEST_NAME_, + filter); + } + + if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) { + const int32_t shard_index = Int32FromEnvOrDie(kTestShardIndex, -1); + ColoredPrintf(GTestColor::kYellow, "Note: This is test shard %d of %s.\n", + static_cast<int>(shard_index) + 1, + internal::posix::GetEnv(kTestTotalShards)); + } + + if (GTEST_FLAG(shuffle)) { + ColoredPrintf(GTestColor::kYellow, + "Note: Randomizing tests' orders with a seed of %d .\n", + unit_test.random_seed()); + } + + ColoredPrintf(GTestColor::kGreen, "[==========] "); + printf("Running %s from %s.\n", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestSuiteCount(unit_test.test_suite_to_run_count()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(GTestColor::kGreen, "[----------] "); + printf("Global test environment set-up.\n"); + fflush(stdout); +} + +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(GTestColor::kGreen, "[----------] "); + printf("%s from %s", counts.c_str(), test_case.name()); + if (test_case.type_param() == nullptr) { + printf("\n"); + } else { + printf(", where %s = %s\n", kTypeParamLabel, test_case.type_param()); + } + fflush(stdout); +} +#else +void PrettyUnitTestResultPrinter::OnTestSuiteStart( + const TestSuite& test_suite) { + const std::string counts = + FormatCountableNoun(test_suite.test_to_run_count(), "test", "tests"); + ColoredPrintf(GTestColor::kGreen, "[----------] "); + printf("%s from %s", counts.c_str(), test_suite.name()); + if (test_suite.type_param() == nullptr) { + printf("\n"); + } else { + printf(", where %s = %s\n", kTypeParamLabel, test_suite.type_param()); + } + fflush(stdout); +} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { + ColoredPrintf(GTestColor::kGreen, "[ RUN ] "); + PrintTestName(test_info.test_suite_name(), test_info.name()); + printf("\n"); + fflush(stdout); +} + +// Called after an assertion failure. +void PrettyUnitTestResultPrinter::OnTestPartResult( + const TestPartResult& result) { + switch (result.type()) { + // If the test part succeeded, we don't need to do anything. + case TestPartResult::kSuccess: + return; + default: + // Print failure message from the assertion + // (e.g. expected this and got that). + PrintTestPartResult(result); + fflush(stdout); + } +} + +void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { + if (test_info.result()->Passed()) { + ColoredPrintf(GTestColor::kGreen, "[ OK ] "); + } else if (test_info.result()->Skipped()) { + ColoredPrintf(GTestColor::kGreen, "[ SKIPPED ] "); + } else { + ColoredPrintf(GTestColor::kRed, "[ FAILED ] "); + } + PrintTestName(test_info.test_suite_name(), test_info.name()); + if (test_info.result()->Failed()) + PrintFullTestCommentIfPresent(test_info); + + if (GTEST_FLAG(print_time)) { + printf(" (%s ms)\n", internal::StreamableToString( + test_info.result()->elapsed_time()).c_str()); + } else { + printf("\n"); + } + fflush(stdout); +} + +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) { + if (!GTEST_FLAG(print_time)) return; + + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(GTestColor::kGreen, "[----------] "); + printf("%s from %s (%s ms total)\n\n", counts.c_str(), test_case.name(), + internal::StreamableToString(test_case.elapsed_time()).c_str()); + fflush(stdout); +} +#else +void PrettyUnitTestResultPrinter::OnTestSuiteEnd(const TestSuite& test_suite) { + if (!GTEST_FLAG(print_time)) return; + + const std::string counts = + FormatCountableNoun(test_suite.test_to_run_count(), "test", "tests"); + ColoredPrintf(GTestColor::kGreen, "[----------] "); + printf("%s from %s (%s ms total)\n\n", counts.c_str(), test_suite.name(), + internal::StreamableToString(test_suite.elapsed_time()).c_str()); + fflush(stdout); +} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(GTestColor::kGreen, "[----------] "); + printf("Global test environment tear-down\n"); + fflush(stdout); +} + +// Internal helper for printing the list of failed tests. +void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) { + const int failed_test_count = unit_test.failed_test_count(); + ColoredPrintf(GTestColor::kRed, "[ FAILED ] "); + printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str()); + + for (int i = 0; i < unit_test.total_test_suite_count(); ++i) { + const TestSuite& test_suite = *unit_test.GetTestSuite(i); + if (!test_suite.should_run() || (test_suite.failed_test_count() == 0)) { + continue; + } + for (int j = 0; j < test_suite.total_test_count(); ++j) { + const TestInfo& test_info = *test_suite.GetTestInfo(j); + if (!test_info.should_run() || !test_info.result()->Failed()) { + continue; + } + ColoredPrintf(GTestColor::kRed, "[ FAILED ] "); + printf("%s.%s", test_suite.name(), test_info.name()); + PrintFullTestCommentIfPresent(test_info); + printf("\n"); + } + } + printf("\n%2d FAILED %s\n", failed_test_count, + failed_test_count == 1 ? "TEST" : "TESTS"); +} + +// Internal helper for printing the list of test suite failures not covered by +// PrintFailedTests. +void PrettyUnitTestResultPrinter::PrintFailedTestSuites( + const UnitTest& unit_test) { + int suite_failure_count = 0; + for (int i = 0; i < unit_test.total_test_suite_count(); ++i) { + const TestSuite& test_suite = *unit_test.GetTestSuite(i); + if (!test_suite.should_run()) { + continue; + } + if (test_suite.ad_hoc_test_result().Failed()) { + ColoredPrintf(GTestColor::kRed, "[ FAILED ] "); + printf("%s: SetUpTestSuite or TearDownTestSuite\n", test_suite.name()); + ++suite_failure_count; + } + } + if (suite_failure_count > 0) { + printf("\n%2d FAILED TEST %s\n", suite_failure_count, + suite_failure_count == 1 ? "SUITE" : "SUITES"); + } +} + +// Internal helper for printing the list of skipped tests. +void PrettyUnitTestResultPrinter::PrintSkippedTests(const UnitTest& unit_test) { + const int skipped_test_count = unit_test.skipped_test_count(); + if (skipped_test_count == 0) { + return; + } + + for (int i = 0; i < unit_test.total_test_suite_count(); ++i) { + const TestSuite& test_suite = *unit_test.GetTestSuite(i); + if (!test_suite.should_run() || (test_suite.skipped_test_count() == 0)) { + continue; + } + for (int j = 0; j < test_suite.total_test_count(); ++j) { + const TestInfo& test_info = *test_suite.GetTestInfo(j); + if (!test_info.should_run() || !test_info.result()->Skipped()) { + continue; + } + ColoredPrintf(GTestColor::kGreen, "[ SKIPPED ] "); + printf("%s.%s", test_suite.name(), test_info.name()); + printf("\n"); + } + } +} + +void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + ColoredPrintf(GTestColor::kGreen, "[==========] "); + printf("%s from %s ran.", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestSuiteCount(unit_test.test_suite_to_run_count()).c_str()); + if (GTEST_FLAG(print_time)) { + printf(" (%s ms total)", + internal::StreamableToString(unit_test.elapsed_time()).c_str()); + } + printf("\n"); + ColoredPrintf(GTestColor::kGreen, "[ PASSED ] "); + printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str()); + + const int skipped_test_count = unit_test.skipped_test_count(); + if (skipped_test_count > 0) { + ColoredPrintf(GTestColor::kGreen, "[ SKIPPED ] "); + printf("%s, listed below:\n", FormatTestCount(skipped_test_count).c_str()); + PrintSkippedTests(unit_test); + } + + if (!unit_test.Passed()) { + PrintFailedTests(unit_test); + PrintFailedTestSuites(unit_test); + } + + int num_disabled = unit_test.reportable_disabled_test_count(); + if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { + if (unit_test.Passed()) { + printf("\n"); // Add a spacer if no FAILURE banner is displayed. + } + ColoredPrintf(GTestColor::kYellow, " YOU HAVE %d DISABLED %s\n\n", + num_disabled, num_disabled == 1 ? "TEST" : "TESTS"); + } + // Ensure that Google Test output is printed before, e.g., heapchecker output. + fflush(stdout); +} + +// End PrettyUnitTestResultPrinter + +// This class implements the TestEventListener interface. +// +// Class BriefUnitTestResultPrinter is copyable. +class BriefUnitTestResultPrinter : public TestEventListener { + public: + BriefUnitTestResultPrinter() {} + static void PrintTestName(const char* test_suite, const char* test) { + printf("%s.%s", test_suite, test); + } + + // The following methods override what's in the TestEventListener class. + void OnTestProgramStart(const UnitTest& /*unit_test*/) override {} + void OnTestIterationStart(const UnitTest& /*unit_test*/, + int /*iteration*/) override {} + void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) override {} +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseStart(const TestCase& /*test_case*/) override {} +#else + void OnTestSuiteStart(const TestSuite& /*test_suite*/) override {} +#endif // OnTestCaseStart + + void OnTestStart(const TestInfo& /*test_info*/) override {} + + void OnTestPartResult(const TestPartResult& result) override; + void OnTestEnd(const TestInfo& test_info) override; +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseEnd(const TestCase& /*test_case*/) override {} +#else + void OnTestSuiteEnd(const TestSuite& /*test_suite*/) override {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) override {} + void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override; + void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {} +}; + +// Called after an assertion failure. +void BriefUnitTestResultPrinter::OnTestPartResult( + const TestPartResult& result) { + switch (result.type()) { + // If the test part succeeded, we don't need to do anything. + case TestPartResult::kSuccess: + return; + default: + // Print failure message from the assertion + // (e.g. expected this and got that). + PrintTestPartResult(result); + fflush(stdout); + } +} + +void BriefUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { + if (test_info.result()->Failed()) { + ColoredPrintf(GTestColor::kRed, "[ FAILED ] "); + PrintTestName(test_info.test_suite_name(), test_info.name()); + PrintFullTestCommentIfPresent(test_info); + + if (GTEST_FLAG(print_time)) { + printf(" (%s ms)\n", + internal::StreamableToString(test_info.result()->elapsed_time()) + .c_str()); + } else { + printf("\n"); + } + fflush(stdout); + } +} + +void BriefUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + ColoredPrintf(GTestColor::kGreen, "[==========] "); + printf("%s from %s ran.", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestSuiteCount(unit_test.test_suite_to_run_count()).c_str()); + if (GTEST_FLAG(print_time)) { + printf(" (%s ms total)", + internal::StreamableToString(unit_test.elapsed_time()).c_str()); + } + printf("\n"); + ColoredPrintf(GTestColor::kGreen, "[ PASSED ] "); + printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str()); + + const int skipped_test_count = unit_test.skipped_test_count(); + if (skipped_test_count > 0) { + ColoredPrintf(GTestColor::kGreen, "[ SKIPPED ] "); + printf("%s.\n", FormatTestCount(skipped_test_count).c_str()); + } + + int num_disabled = unit_test.reportable_disabled_test_count(); + if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { + if (unit_test.Passed()) { + printf("\n"); // Add a spacer if no FAILURE banner is displayed. + } + ColoredPrintf(GTestColor::kYellow, " YOU HAVE %d DISABLED %s\n\n", + num_disabled, num_disabled == 1 ? "TEST" : "TESTS"); + } + // Ensure that Google Test output is printed before, e.g., heapchecker output. + fflush(stdout); +} + +// End BriefUnitTestResultPrinter + +// class TestEventRepeater +// +// This class forwards events to other event listeners. +class TestEventRepeater : public TestEventListener { + public: + TestEventRepeater() : forwarding_enabled_(true) {} + ~TestEventRepeater() override; + void Append(TestEventListener *listener); + TestEventListener* Release(TestEventListener* listener); + + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled() const { return forwarding_enabled_; } + void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; } + + void OnTestProgramStart(const UnitTest& unit_test) override; + void OnTestIterationStart(const UnitTest& unit_test, int iteration) override; + void OnEnvironmentsSetUpStart(const UnitTest& unit_test) override; + void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) override; +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseStart(const TestSuite& parameter) override; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestSuiteStart(const TestSuite& parameter) override; + void OnTestStart(const TestInfo& test_info) override; + void OnTestPartResult(const TestPartResult& result) override; + void OnTestEnd(const TestInfo& test_info) override; +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseEnd(const TestCase& parameter) override; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestSuiteEnd(const TestSuite& parameter) override; + void OnEnvironmentsTearDownStart(const UnitTest& unit_test) override; + void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) override; + void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override; + void OnTestProgramEnd(const UnitTest& unit_test) override; + + private: + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled_; + // The list of listeners that receive events. + std::vector<TestEventListener*> listeners_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventRepeater); +}; + +TestEventRepeater::~TestEventRepeater() { + ForEach(listeners_, Delete<TestEventListener>); +} + +void TestEventRepeater::Append(TestEventListener *listener) { + listeners_.push_back(listener); +} + +TestEventListener* TestEventRepeater::Release(TestEventListener *listener) { + for (size_t i = 0; i < listeners_.size(); ++i) { + if (listeners_[i] == listener) { + listeners_.erase(listeners_.begin() + static_cast<int>(i)); + return listener; + } + } + + return nullptr; +} + +// Since most methods are very similar, use macros to reduce boilerplate. +// This defines a member that forwards the call to all listeners. +#define GTEST_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (size_t i = 0; i < listeners_.size(); i++) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} +// This defines a member that forwards the call to all listeners in reverse +// order. +#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \ + void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (size_t i = listeners_.size(); i != 0; i--) { \ + listeners_[i - 1]->Name(parameter); \ + } \ + } \ + } + +GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest) +GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest) +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +GTEST_REPEATER_METHOD_(OnTestCaseStart, TestSuite) +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +GTEST_REPEATER_METHOD_(OnTestSuiteStart, TestSuite) +GTEST_REPEATER_METHOD_(OnTestStart, TestInfo) +GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult) +GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo) +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestSuite) +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +GTEST_REVERSE_REPEATER_METHOD_(OnTestSuiteEnd, TestSuite) +GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest) + +#undef GTEST_REPEATER_METHOD_ +#undef GTEST_REVERSE_REPEATER_METHOD_ + +void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (size_t i = 0; i < listeners_.size(); i++) { + listeners_[i]->OnTestIterationStart(unit_test, iteration); + } + } +} + +void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (size_t i = listeners_.size(); i > 0; i--) { + listeners_[i - 1]->OnTestIterationEnd(unit_test, iteration); + } + } +} + +// End TestEventRepeater + +// This class generates an XML output file. +class XmlUnitTestResultPrinter : public EmptyTestEventListener { + public: + explicit XmlUnitTestResultPrinter(const char* output_file); + + void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override; + void ListTestsMatchingFilter(const std::vector<TestSuite*>& test_suites); + + // Prints an XML summary of all unit tests. + static void PrintXmlTestsList(std::ostream* stream, + const std::vector<TestSuite*>& test_suites); + + private: + // Is c a whitespace character that is normalized to a space character + // when it appears in an XML attribute value? + static bool IsNormalizableWhitespace(char c) { + return c == 0x9 || c == 0xA || c == 0xD; + } + + // May c appear in a well-formed XML document? + static bool IsValidXmlCharacter(char c) { + return IsNormalizableWhitespace(c) || c >= 0x20; + } + + // Returns an XML-escaped copy of the input string str. If + // is_attribute is true, the text is meant to appear as an attribute + // value, and normalizable whitespace is preserved by replacing it + // with character references. + static std::string EscapeXml(const std::string& str, bool is_attribute); + + // Returns the given string with all characters invalid in XML removed. + static std::string RemoveInvalidXmlCharacters(const std::string& str); + + // Convenience wrapper around EscapeXml when str is an attribute value. + static std::string EscapeXmlAttribute(const std::string& str) { + return EscapeXml(str, true); + } + + // Convenience wrapper around EscapeXml when str is not an attribute value. + static std::string EscapeXmlText(const char* str) { + return EscapeXml(str, false); + } + + // Verifies that the given attribute belongs to the given element and + // streams the attribute as XML. + static void OutputXmlAttribute(std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value); + + // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. + static void OutputXmlCDataSection(::std::ostream* stream, const char* data); + + // Streams a test suite XML stanza containing the given test result. + // + // Requires: result.Failed() + static void OutputXmlTestSuiteForTestResult(::std::ostream* stream, + const TestResult& result); + + // Streams an XML representation of a TestResult object. + static void OutputXmlTestResult(::std::ostream* stream, + const TestResult& result); + + // Streams an XML representation of a TestInfo object. + static void OutputXmlTestInfo(::std::ostream* stream, + const char* test_suite_name, + const TestInfo& test_info); + + // Prints an XML representation of a TestSuite object + static void PrintXmlTestSuite(::std::ostream* stream, + const TestSuite& test_suite); + + // Prints an XML summary of unit_test to output stream out. + static void PrintXmlUnitTest(::std::ostream* stream, + const UnitTest& unit_test); + + // Produces a string representing the test properties in a result as space + // delimited XML attributes based on the property key="value" pairs. + // When the std::string is not empty, it includes a space at the beginning, + // to delimit this attribute from prior attributes. + static std::string TestPropertiesAsXmlAttributes(const TestResult& result); + + // Streams an XML representation of the test properties of a TestResult + // object. + static void OutputXmlTestProperties(std::ostream* stream, + const TestResult& result); + + // The output file. + const std::string output_file_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter); +}; + +// Creates a new XmlUnitTestResultPrinter. +XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file) + : output_file_(output_file) { + if (output_file_.empty()) { + GTEST_LOG_(FATAL) << "XML output file may not be null"; + } +} + +// Called after the unit test ends. +void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + FILE* xmlout = OpenFileForWriting(output_file_); + std::stringstream stream; + PrintXmlUnitTest(&stream, unit_test); + fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); + fclose(xmlout); +} + +void XmlUnitTestResultPrinter::ListTestsMatchingFilter( + const std::vector<TestSuite*>& test_suites) { + FILE* xmlout = OpenFileForWriting(output_file_); + std::stringstream stream; + PrintXmlTestsList(&stream, test_suites); + fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); + fclose(xmlout); +} + +// Returns an XML-escaped copy of the input string str. If is_attribute +// is true, the text is meant to appear as an attribute value, and +// normalizable whitespace is preserved by replacing it with character +// references. +// +// Invalid XML characters in str, if any, are stripped from the output. +// It is expected that most, if not all, of the text processed by this +// module will consist of ordinary English text. +// If this module is ever modified to produce version 1.1 XML output, +// most invalid characters can be retained using character references. +std::string XmlUnitTestResultPrinter::EscapeXml( + const std::string& str, bool is_attribute) { + Message m; + + for (size_t i = 0; i < str.size(); ++i) { + const char ch = str[i]; + switch (ch) { + case '<': + m << "<"; + break; + case '>': + m << ">"; + break; + case '&': + m << "&"; + break; + case '\'': + if (is_attribute) + m << "'"; + else + m << '\''; + break; + case '"': + if (is_attribute) + m << """; + else + m << '"'; + break; + default: + if (IsValidXmlCharacter(ch)) { + if (is_attribute && IsNormalizableWhitespace(ch)) + m << "&#x" << String::FormatByte(static_cast<unsigned char>(ch)) + << ";"; + else + m << ch; + } + break; + } + } + + return m.GetString(); +} + +// Returns the given string with all characters invalid in XML removed. +// Currently invalid characters are dropped from the string. An +// alternative is to replace them with certain characters such as . or ?. +std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters( + const std::string& str) { + std::string output; + output.reserve(str.size()); + for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) + if (IsValidXmlCharacter(*it)) + output.push_back(*it); + + return output; +} + +// The following routines generate an XML representation of a UnitTest +// object. +// GOOGLETEST_CM0009 DO NOT DELETE +// +// This is how Google Test concepts map to the DTD: +// +// <testsuites name="AllTests"> <-- corresponds to a UnitTest object +// <testsuite name="testcase-name"> <-- corresponds to a TestSuite object +// <testcase name="test-name"> <-- corresponds to a TestInfo object +// <failure message="...">...</failure> +// <failure message="...">...</failure> +// <failure message="...">...</failure> +// <-- individual assertion failures +// </testcase> +// </testsuite> +// </testsuites> + +// Formats the given time in milliseconds as seconds. +std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) { + ::std::stringstream ss; + ss << (static_cast<double>(ms) * 1e-3); + return ss.str(); +} + +static bool PortableLocaltime(time_t seconds, struct tm* out) { +#if defined(_MSC_VER) + return localtime_s(out, &seconds) == 0; +#elif defined(__MINGW32__) || defined(__MINGW64__) + // MINGW <time.h> provides neither localtime_r nor localtime_s, but uses + // Windows' localtime(), which has a thread-local tm buffer. + struct tm* tm_ptr = localtime(&seconds); // NOLINT + if (tm_ptr == nullptr) return false; + *out = *tm_ptr; + return true; +#elif defined(__STDC_LIB_EXT1__) + // Uses localtime_s when available as localtime_r is only available from + // C23 standard. + return localtime_s(&seconds, out) != nullptr; +#else + return localtime_r(&seconds, out) != nullptr; +#endif +} + +// Converts the given epoch time in milliseconds to a date string in the ISO +// 8601 format, without the timezone information. +std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) { + struct tm time_struct; + if (!PortableLocaltime(static_cast<time_t>(ms / 1000), &time_struct)) + return ""; + // YYYY-MM-DDThh:mm:ss.sss + return StreamableToString(time_struct.tm_year + 1900) + "-" + + String::FormatIntWidth2(time_struct.tm_mon + 1) + "-" + + String::FormatIntWidth2(time_struct.tm_mday) + "T" + + String::FormatIntWidth2(time_struct.tm_hour) + ":" + + String::FormatIntWidth2(time_struct.tm_min) + ":" + + String::FormatIntWidth2(time_struct.tm_sec) + "." + + String::FormatIntWidthN(static_cast<int>(ms % 1000), 3); +} + +// Streams an XML CDATA section, escaping invalid CDATA sequences as needed. +void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, + const char* data) { + const char* segment = data; + *stream << "<![CDATA["; + for (;;) { + const char* const next_segment = strstr(segment, "]]>"); + if (next_segment != nullptr) { + stream->write( + segment, static_cast<std::streamsize>(next_segment - segment)); + *stream << "]]>]]><![CDATA["; + segment = next_segment + strlen("]]>"); + } else { + *stream << segment; + break; + } + } + *stream << "]]>"; +} + +void XmlUnitTestResultPrinter::OutputXmlAttribute( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value) { + const std::vector<std::string>& allowed_names = + GetReservedOutputAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Attribute " << name << " is not allowed for element <" << element_name + << ">."; + + *stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\""; +} + +// Streams a test suite XML stanza containing the given test result. +void XmlUnitTestResultPrinter::OutputXmlTestSuiteForTestResult( + ::std::ostream* stream, const TestResult& result) { + // Output the boilerplate for a minimal test suite with one test. + *stream << " <testsuite"; + OutputXmlAttribute(stream, "testsuite", "name", "NonTestSuiteFailure"); + OutputXmlAttribute(stream, "testsuite", "tests", "1"); + OutputXmlAttribute(stream, "testsuite", "failures", "1"); + OutputXmlAttribute(stream, "testsuite", "disabled", "0"); + OutputXmlAttribute(stream, "testsuite", "skipped", "0"); + OutputXmlAttribute(stream, "testsuite", "errors", "0"); + OutputXmlAttribute(stream, "testsuite", "time", + FormatTimeInMillisAsSeconds(result.elapsed_time())); + OutputXmlAttribute( + stream, "testsuite", "timestamp", + FormatEpochTimeInMillisAsIso8601(result.start_timestamp())); + *stream << ">"; + + // Output the boilerplate for a minimal test case with a single test. + *stream << " <testcase"; + OutputXmlAttribute(stream, "testcase", "name", ""); + OutputXmlAttribute(stream, "testcase", "status", "run"); + OutputXmlAttribute(stream, "testcase", "result", "completed"); + OutputXmlAttribute(stream, "testcase", "classname", ""); + OutputXmlAttribute(stream, "testcase", "time", + FormatTimeInMillisAsSeconds(result.elapsed_time())); + OutputXmlAttribute( + stream, "testcase", "timestamp", + FormatEpochTimeInMillisAsIso8601(result.start_timestamp())); + + // Output the actual test result. + OutputXmlTestResult(stream, result); + + // Complete the test suite. + *stream << " </testsuite>\n"; +} + +// Prints an XML representation of a TestInfo object. +void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, + const char* test_suite_name, + const TestInfo& test_info) { + const TestResult& result = *test_info.result(); + const std::string kTestsuite = "testcase"; + + if (test_info.is_in_another_shard()) { + return; + } + + *stream << " <testcase"; + OutputXmlAttribute(stream, kTestsuite, "name", test_info.name()); + + if (test_info.value_param() != nullptr) { + OutputXmlAttribute(stream, kTestsuite, "value_param", + test_info.value_param()); + } + if (test_info.type_param() != nullptr) { + OutputXmlAttribute(stream, kTestsuite, "type_param", + test_info.type_param()); + } + if (GTEST_FLAG(list_tests)) { + OutputXmlAttribute(stream, kTestsuite, "file", test_info.file()); + OutputXmlAttribute(stream, kTestsuite, "line", + StreamableToString(test_info.line())); + *stream << " />\n"; + return; + } + + OutputXmlAttribute(stream, kTestsuite, "status", + test_info.should_run() ? "run" : "notrun"); + OutputXmlAttribute(stream, kTestsuite, "result", + test_info.should_run() + ? (result.Skipped() ? "skipped" : "completed") + : "suppressed"); + OutputXmlAttribute(stream, kTestsuite, "time", + FormatTimeInMillisAsSeconds(result.elapsed_time())); + OutputXmlAttribute( + stream, kTestsuite, "timestamp", + FormatEpochTimeInMillisAsIso8601(result.start_timestamp())); + OutputXmlAttribute(stream, kTestsuite, "classname", test_suite_name); + + OutputXmlTestResult(stream, result); +} + +void XmlUnitTestResultPrinter::OutputXmlTestResult(::std::ostream* stream, + const TestResult& result) { + int failures = 0; + int skips = 0; + for (int i = 0; i < result.total_part_count(); ++i) { + const TestPartResult& part = result.GetTestPartResult(i); + if (part.failed()) { + if (++failures == 1 && skips == 0) { + *stream << ">\n"; + } + const std::string location = + internal::FormatCompilerIndependentFileLocation(part.file_name(), + part.line_number()); + const std::string summary = location + "\n" + part.summary(); + *stream << " <failure message=\"" + << EscapeXmlAttribute(summary) + << "\" type=\"\">"; + const std::string detail = location + "\n" + part.message(); + OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str()); + *stream << "</failure>\n"; + } else if (part.skipped()) { + if (++skips == 1 && failures == 0) { + *stream << ">\n"; + } + const std::string location = + internal::FormatCompilerIndependentFileLocation(part.file_name(), + part.line_number()); + const std::string summary = location + "\n" + part.summary(); + *stream << " <skipped message=\"" + << EscapeXmlAttribute(summary.c_str()) << "\">"; + const std::string detail = location + "\n" + part.message(); + OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str()); + *stream << "</skipped>\n"; + } + } + + if (failures == 0 && skips == 0 && result.test_property_count() == 0) { + *stream << " />\n"; + } else { + if (failures == 0 && skips == 0) { + *stream << ">\n"; + } + OutputXmlTestProperties(stream, result); + *stream << " </testcase>\n"; + } +} + +// Prints an XML representation of a TestSuite object +void XmlUnitTestResultPrinter::PrintXmlTestSuite(std::ostream* stream, + const TestSuite& test_suite) { + const std::string kTestsuite = "testsuite"; + *stream << " <" << kTestsuite; + OutputXmlAttribute(stream, kTestsuite, "name", test_suite.name()); + OutputXmlAttribute(stream, kTestsuite, "tests", + StreamableToString(test_suite.reportable_test_count())); + if (!GTEST_FLAG(list_tests)) { + OutputXmlAttribute(stream, kTestsuite, "failures", + StreamableToString(test_suite.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuite, "disabled", + StreamableToString(test_suite.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuite, "skipped", + StreamableToString(test_suite.skipped_test_count())); + + OutputXmlAttribute(stream, kTestsuite, "errors", "0"); + + OutputXmlAttribute(stream, kTestsuite, "time", + FormatTimeInMillisAsSeconds(test_suite.elapsed_time())); + OutputXmlAttribute( + stream, kTestsuite, "timestamp", + FormatEpochTimeInMillisAsIso8601(test_suite.start_timestamp())); + *stream << TestPropertiesAsXmlAttributes(test_suite.ad_hoc_test_result()); + } + *stream << ">\n"; + for (int i = 0; i < test_suite.total_test_count(); ++i) { + if (test_suite.GetTestInfo(i)->is_reportable()) + OutputXmlTestInfo(stream, test_suite.name(), *test_suite.GetTestInfo(i)); + } + *stream << " </" << kTestsuite << ">\n"; +} + +// Prints an XML summary of unit_test to output stream out. +void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, + const UnitTest& unit_test) { + const std::string kTestsuites = "testsuites"; + + *stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + *stream << "<" << kTestsuites; + + OutputXmlAttribute(stream, kTestsuites, "tests", + StreamableToString(unit_test.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuites, "failures", + StreamableToString(unit_test.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuites, "disabled", + StreamableToString(unit_test.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuites, "errors", "0"); + OutputXmlAttribute(stream, kTestsuites, "time", + FormatTimeInMillisAsSeconds(unit_test.elapsed_time())); + OutputXmlAttribute( + stream, kTestsuites, "timestamp", + FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp())); + + if (GTEST_FLAG(shuffle)) { + OutputXmlAttribute(stream, kTestsuites, "random_seed", + StreamableToString(unit_test.random_seed())); + } + *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result()); + + OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); + *stream << ">\n"; + + for (int i = 0; i < unit_test.total_test_suite_count(); ++i) { + if (unit_test.GetTestSuite(i)->reportable_test_count() > 0) + PrintXmlTestSuite(stream, *unit_test.GetTestSuite(i)); + } + + // If there was a test failure outside of one of the test suites (like in a + // test environment) include that in the output. + if (unit_test.ad_hoc_test_result().Failed()) { + OutputXmlTestSuiteForTestResult(stream, unit_test.ad_hoc_test_result()); + } + + *stream << "</" << kTestsuites << ">\n"; +} + +void XmlUnitTestResultPrinter::PrintXmlTestsList( + std::ostream* stream, const std::vector<TestSuite*>& test_suites) { + const std::string kTestsuites = "testsuites"; + + *stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + *stream << "<" << kTestsuites; + + int total_tests = 0; + for (auto test_suite : test_suites) { + total_tests += test_suite->total_test_count(); + } + OutputXmlAttribute(stream, kTestsuites, "tests", + StreamableToString(total_tests)); + OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); + *stream << ">\n"; + + for (auto test_suite : test_suites) { + PrintXmlTestSuite(stream, *test_suite); + } + *stream << "</" << kTestsuites << ">\n"; +} + +// Produces a string representing the test properties in a result as space +// delimited XML attributes based on the property key="value" pairs. +std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( + const TestResult& result) { + Message attributes; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + attributes << " " << property.key() << "=" + << "\"" << EscapeXmlAttribute(property.value()) << "\""; + } + return attributes.GetString(); +} + +void XmlUnitTestResultPrinter::OutputXmlTestProperties( + std::ostream* stream, const TestResult& result) { + const std::string kProperties = "properties"; + const std::string kProperty = "property"; + + if (result.test_property_count() <= 0) { + return; + } + + *stream << "<" << kProperties << ">\n"; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + *stream << "<" << kProperty; + *stream << " name=\"" << EscapeXmlAttribute(property.key()) << "\""; + *stream << " value=\"" << EscapeXmlAttribute(property.value()) << "\""; + *stream << "/>\n"; + } + *stream << "</" << kProperties << ">\n"; +} + +// End XmlUnitTestResultPrinter + +// This class generates an JSON output file. +class JsonUnitTestResultPrinter : public EmptyTestEventListener { + public: + explicit JsonUnitTestResultPrinter(const char* output_file); + + void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override; + + // Prints an JSON summary of all unit tests. + static void PrintJsonTestList(::std::ostream* stream, + const std::vector<TestSuite*>& test_suites); + + private: + // Returns an JSON-escaped copy of the input string str. + static std::string EscapeJson(const std::string& str); + + //// Verifies that the given attribute belongs to the given element and + //// streams the attribute as JSON. + static void OutputJsonKey(std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value, + const std::string& indent, + bool comma = true); + static void OutputJsonKey(std::ostream* stream, + const std::string& element_name, + const std::string& name, + int value, + const std::string& indent, + bool comma = true); + + // Streams a test suite JSON stanza containing the given test result. + // + // Requires: result.Failed() + static void OutputJsonTestSuiteForTestResult(::std::ostream* stream, + const TestResult& result); + + // Streams a JSON representation of a TestResult object. + static void OutputJsonTestResult(::std::ostream* stream, + const TestResult& result); + + // Streams a JSON representation of a TestInfo object. + static void OutputJsonTestInfo(::std::ostream* stream, + const char* test_suite_name, + const TestInfo& test_info); + + // Prints a JSON representation of a TestSuite object + static void PrintJsonTestSuite(::std::ostream* stream, + const TestSuite& test_suite); + + // Prints a JSON summary of unit_test to output stream out. + static void PrintJsonUnitTest(::std::ostream* stream, + const UnitTest& unit_test); + + // Produces a string representing the test properties in a result as + // a JSON dictionary. + static std::string TestPropertiesAsJson(const TestResult& result, + const std::string& indent); + + // The output file. + const std::string output_file_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(JsonUnitTestResultPrinter); +}; + +// Creates a new JsonUnitTestResultPrinter. +JsonUnitTestResultPrinter::JsonUnitTestResultPrinter(const char* output_file) + : output_file_(output_file) { + if (output_file_.empty()) { + GTEST_LOG_(FATAL) << "JSON output file may not be null"; + } +} + +void JsonUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + FILE* jsonout = OpenFileForWriting(output_file_); + std::stringstream stream; + PrintJsonUnitTest(&stream, unit_test); + fprintf(jsonout, "%s", StringStreamToString(&stream).c_str()); + fclose(jsonout); +} + +// Returns an JSON-escaped copy of the input string str. +std::string JsonUnitTestResultPrinter::EscapeJson(const std::string& str) { + Message m; + + for (size_t i = 0; i < str.size(); ++i) { + const char ch = str[i]; + switch (ch) { + case '\\': + case '"': + case '/': + m << '\\' << ch; + break; + case '\b': + m << "\\b"; + break; + case '\t': + m << "\\t"; + break; + case '\n': + m << "\\n"; + break; + case '\f': + m << "\\f"; + break; + case '\r': + m << "\\r"; + break; + default: + if (ch < ' ') { + m << "\\u00" << String::FormatByte(static_cast<unsigned char>(ch)); + } else { + m << ch; + } + break; + } + } + + return m.GetString(); +} + +// The following routines generate an JSON representation of a UnitTest +// object. + +// Formats the given time in milliseconds as seconds. +static std::string FormatTimeInMillisAsDuration(TimeInMillis ms) { + ::std::stringstream ss; + ss << (static_cast<double>(ms) * 1e-3) << "s"; + return ss.str(); +} + +// Converts the given epoch time in milliseconds to a date string in the +// RFC3339 format, without the timezone information. +static std::string FormatEpochTimeInMillisAsRFC3339(TimeInMillis ms) { + struct tm time_struct; + if (!PortableLocaltime(static_cast<time_t>(ms / 1000), &time_struct)) + return ""; + // YYYY-MM-DDThh:mm:ss + return StreamableToString(time_struct.tm_year + 1900) + "-" + + String::FormatIntWidth2(time_struct.tm_mon + 1) + "-" + + String::FormatIntWidth2(time_struct.tm_mday) + "T" + + String::FormatIntWidth2(time_struct.tm_hour) + ":" + + String::FormatIntWidth2(time_struct.tm_min) + ":" + + String::FormatIntWidth2(time_struct.tm_sec) + "Z"; +} + +static inline std::string Indent(size_t width) { + return std::string(width, ' '); +} + +void JsonUnitTestResultPrinter::OutputJsonKey( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value, + const std::string& indent, + bool comma) { + const std::vector<std::string>& allowed_names = + GetReservedOutputAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Key \"" << name << "\" is not allowed for value \"" << element_name + << "\"."; + + *stream << indent << "\"" << name << "\": \"" << EscapeJson(value) << "\""; + if (comma) + *stream << ",\n"; +} + +void JsonUnitTestResultPrinter::OutputJsonKey( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + int value, + const std::string& indent, + bool comma) { + const std::vector<std::string>& allowed_names = + GetReservedOutputAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Key \"" << name << "\" is not allowed for value \"" << element_name + << "\"."; + + *stream << indent << "\"" << name << "\": " << StreamableToString(value); + if (comma) + *stream << ",\n"; +} + +// Streams a test suite JSON stanza containing the given test result. +void JsonUnitTestResultPrinter::OutputJsonTestSuiteForTestResult( + ::std::ostream* stream, const TestResult& result) { + // Output the boilerplate for a new test suite. + *stream << Indent(4) << "{\n"; + OutputJsonKey(stream, "testsuite", "name", "NonTestSuiteFailure", Indent(6)); + OutputJsonKey(stream, "testsuite", "tests", 1, Indent(6)); + if (!GTEST_FLAG(list_tests)) { + OutputJsonKey(stream, "testsuite", "failures", 1, Indent(6)); + OutputJsonKey(stream, "testsuite", "disabled", 0, Indent(6)); + OutputJsonKey(stream, "testsuite", "skipped", 0, Indent(6)); + OutputJsonKey(stream, "testsuite", "errors", 0, Indent(6)); + OutputJsonKey(stream, "testsuite", "time", + FormatTimeInMillisAsDuration(result.elapsed_time()), + Indent(6)); + OutputJsonKey(stream, "testsuite", "timestamp", + FormatEpochTimeInMillisAsRFC3339(result.start_timestamp()), + Indent(6)); + } + *stream << Indent(6) << "\"testsuite\": [\n"; + + // Output the boilerplate for a new test case. + *stream << Indent(8) << "{\n"; + OutputJsonKey(stream, "testcase", "name", "", Indent(10)); + OutputJsonKey(stream, "testcase", "status", "RUN", Indent(10)); + OutputJsonKey(stream, "testcase", "result", "COMPLETED", Indent(10)); + OutputJsonKey(stream, "testcase", "timestamp", + FormatEpochTimeInMillisAsRFC3339(result.start_timestamp()), + Indent(10)); + OutputJsonKey(stream, "testcase", "time", + FormatTimeInMillisAsDuration(result.elapsed_time()), + Indent(10)); + OutputJsonKey(stream, "testcase", "classname", "", Indent(10), false); + *stream << TestPropertiesAsJson(result, Indent(10)); + + // Output the actual test result. + OutputJsonTestResult(stream, result); + + // Finish the test suite. + *stream << "\n" << Indent(6) << "]\n" << Indent(4) << "}"; +} + +// Prints a JSON representation of a TestInfo object. +void JsonUnitTestResultPrinter::OutputJsonTestInfo(::std::ostream* stream, + const char* test_suite_name, + const TestInfo& test_info) { + const TestResult& result = *test_info.result(); + const std::string kTestsuite = "testcase"; + const std::string kIndent = Indent(10); + + *stream << Indent(8) << "{\n"; + OutputJsonKey(stream, kTestsuite, "name", test_info.name(), kIndent); + + if (test_info.value_param() != nullptr) { + OutputJsonKey(stream, kTestsuite, "value_param", test_info.value_param(), + kIndent); + } + if (test_info.type_param() != nullptr) { + OutputJsonKey(stream, kTestsuite, "type_param", test_info.type_param(), + kIndent); + } + if (GTEST_FLAG(list_tests)) { + OutputJsonKey(stream, kTestsuite, "file", test_info.file(), kIndent); + OutputJsonKey(stream, kTestsuite, "line", test_info.line(), kIndent, false); + *stream << "\n" << Indent(8) << "}"; + return; + } + + OutputJsonKey(stream, kTestsuite, "status", + test_info.should_run() ? "RUN" : "NOTRUN", kIndent); + OutputJsonKey(stream, kTestsuite, "result", + test_info.should_run() + ? (result.Skipped() ? "SKIPPED" : "COMPLETED") + : "SUPPRESSED", + kIndent); + OutputJsonKey(stream, kTestsuite, "timestamp", + FormatEpochTimeInMillisAsRFC3339(result.start_timestamp()), + kIndent); + OutputJsonKey(stream, kTestsuite, "time", + FormatTimeInMillisAsDuration(result.elapsed_time()), kIndent); + OutputJsonKey(stream, kTestsuite, "classname", test_suite_name, kIndent, + false); + *stream << TestPropertiesAsJson(result, kIndent); + + OutputJsonTestResult(stream, result); +} + +void JsonUnitTestResultPrinter::OutputJsonTestResult(::std::ostream* stream, + const TestResult& result) { + const std::string kIndent = Indent(10); + + int failures = 0; + for (int i = 0; i < result.total_part_count(); ++i) { + const TestPartResult& part = result.GetTestPartResult(i); + if (part.failed()) { + *stream << ",\n"; + if (++failures == 1) { + *stream << kIndent << "\"" << "failures" << "\": [\n"; + } + const std::string location = + internal::FormatCompilerIndependentFileLocation(part.file_name(), + part.line_number()); + const std::string message = EscapeJson(location + "\n" + part.message()); + *stream << kIndent << " {\n" + << kIndent << " \"failure\": \"" << message << "\",\n" + << kIndent << " \"type\": \"\"\n" + << kIndent << " }"; + } + } + + if (failures > 0) + *stream << "\n" << kIndent << "]"; + *stream << "\n" << Indent(8) << "}"; +} + +// Prints an JSON representation of a TestSuite object +void JsonUnitTestResultPrinter::PrintJsonTestSuite( + std::ostream* stream, const TestSuite& test_suite) { + const std::string kTestsuite = "testsuite"; + const std::string kIndent = Indent(6); + + *stream << Indent(4) << "{\n"; + OutputJsonKey(stream, kTestsuite, "name", test_suite.name(), kIndent); + OutputJsonKey(stream, kTestsuite, "tests", test_suite.reportable_test_count(), + kIndent); + if (!GTEST_FLAG(list_tests)) { + OutputJsonKey(stream, kTestsuite, "failures", + test_suite.failed_test_count(), kIndent); + OutputJsonKey(stream, kTestsuite, "disabled", + test_suite.reportable_disabled_test_count(), kIndent); + OutputJsonKey(stream, kTestsuite, "errors", 0, kIndent); + OutputJsonKey( + stream, kTestsuite, "timestamp", + FormatEpochTimeInMillisAsRFC3339(test_suite.start_timestamp()), + kIndent); + OutputJsonKey(stream, kTestsuite, "time", + FormatTimeInMillisAsDuration(test_suite.elapsed_time()), + kIndent, false); + *stream << TestPropertiesAsJson(test_suite.ad_hoc_test_result(), kIndent) + << ",\n"; + } + + *stream << kIndent << "\"" << kTestsuite << "\": [\n"; + + bool comma = false; + for (int i = 0; i < test_suite.total_test_count(); ++i) { + if (test_suite.GetTestInfo(i)->is_reportable()) { + if (comma) { + *stream << ",\n"; + } else { + comma = true; + } + OutputJsonTestInfo(stream, test_suite.name(), *test_suite.GetTestInfo(i)); + } + } + *stream << "\n" << kIndent << "]\n" << Indent(4) << "}"; +} + +// Prints a JSON summary of unit_test to output stream out. +void JsonUnitTestResultPrinter::PrintJsonUnitTest(std::ostream* stream, + const UnitTest& unit_test) { + const std::string kTestsuites = "testsuites"; + const std::string kIndent = Indent(2); + *stream << "{\n"; + + OutputJsonKey(stream, kTestsuites, "tests", unit_test.reportable_test_count(), + kIndent); + OutputJsonKey(stream, kTestsuites, "failures", unit_test.failed_test_count(), + kIndent); + OutputJsonKey(stream, kTestsuites, "disabled", + unit_test.reportable_disabled_test_count(), kIndent); + OutputJsonKey(stream, kTestsuites, "errors", 0, kIndent); + if (GTEST_FLAG(shuffle)) { + OutputJsonKey(stream, kTestsuites, "random_seed", unit_test.random_seed(), + kIndent); + } + OutputJsonKey(stream, kTestsuites, "timestamp", + FormatEpochTimeInMillisAsRFC3339(unit_test.start_timestamp()), + kIndent); + OutputJsonKey(stream, kTestsuites, "time", + FormatTimeInMillisAsDuration(unit_test.elapsed_time()), kIndent, + false); + + *stream << TestPropertiesAsJson(unit_test.ad_hoc_test_result(), kIndent) + << ",\n"; + + OutputJsonKey(stream, kTestsuites, "name", "AllTests", kIndent); + *stream << kIndent << "\"" << kTestsuites << "\": [\n"; + + bool comma = false; + for (int i = 0; i < unit_test.total_test_suite_count(); ++i) { + if (unit_test.GetTestSuite(i)->reportable_test_count() > 0) { + if (comma) { + *stream << ",\n"; + } else { + comma = true; + } + PrintJsonTestSuite(stream, *unit_test.GetTestSuite(i)); + } + } + + // If there was a test failure outside of one of the test suites (like in a + // test environment) include that in the output. + if (unit_test.ad_hoc_test_result().Failed()) { + OutputJsonTestSuiteForTestResult(stream, unit_test.ad_hoc_test_result()); + } + + *stream << "\n" << kIndent << "]\n" << "}\n"; +} + +void JsonUnitTestResultPrinter::PrintJsonTestList( + std::ostream* stream, const std::vector<TestSuite*>& test_suites) { + const std::string kTestsuites = "testsuites"; + const std::string kIndent = Indent(2); + *stream << "{\n"; + int total_tests = 0; + for (auto test_suite : test_suites) { + total_tests += test_suite->total_test_count(); + } + OutputJsonKey(stream, kTestsuites, "tests", total_tests, kIndent); + + OutputJsonKey(stream, kTestsuites, "name", "AllTests", kIndent); + *stream << kIndent << "\"" << kTestsuites << "\": [\n"; + + for (size_t i = 0; i < test_suites.size(); ++i) { + if (i != 0) { + *stream << ",\n"; + } + PrintJsonTestSuite(stream, *test_suites[i]); + } + + *stream << "\n" + << kIndent << "]\n" + << "}\n"; +} +// Produces a string representing the test properties in a result as +// a JSON dictionary. +std::string JsonUnitTestResultPrinter::TestPropertiesAsJson( + const TestResult& result, const std::string& indent) { + Message attributes; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + attributes << ",\n" << indent << "\"" << property.key() << "\": " + << "\"" << EscapeJson(property.value()) << "\""; + } + return attributes.GetString(); +} + +// End JsonUnitTestResultPrinter + +#if GTEST_CAN_STREAM_RESULTS_ + +// Checks if str contains '=', '&', '%' or '\n' characters. If yes, +// replaces them by "%xx" where xx is their hexadecimal value. For +// example, replaces "=" with "%3D". This algorithm is O(strlen(str)) +// in both time and space -- important as the input str may contain an +// arbitrarily long test failure message and stack trace. +std::string StreamingListener::UrlEncode(const char* str) { + std::string result; + result.reserve(strlen(str) + 1); + for (char ch = *str; ch != '\0'; ch = *++str) { + switch (ch) { + case '%': + case '=': + case '&': + case '\n': + result.append("%" + String::FormatByte(static_cast<unsigned char>(ch))); + break; + default: + result.push_back(ch); + break; + } + } + return result; +} + +void StreamingListener::SocketWriter::MakeConnection() { + GTEST_CHECK_(sockfd_ == -1) + << "MakeConnection() can't be called when there is already a connection."; + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses. + hints.ai_socktype = SOCK_STREAM; + addrinfo* servinfo = nullptr; + + // Use the getaddrinfo() to get a linked list of IP addresses for + // the given host name. + const int error_num = getaddrinfo( + host_name_.c_str(), port_num_.c_str(), &hints, &servinfo); + if (error_num != 0) { + GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: " + << gai_strerror(error_num); + } + + // Loop through all the results and connect to the first we can. + for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != nullptr; + cur_addr = cur_addr->ai_next) { + sockfd_ = socket( + cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol); + if (sockfd_ != -1) { + // Connect the client socket to the server socket. + if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) { + close(sockfd_); + sockfd_ = -1; + } + } + } + + freeaddrinfo(servinfo); // all done with this structure + + if (sockfd_ == -1) { + GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to " + << host_name_ << ":" << port_num_; + } +} + +// End of class Streaming Listener +#endif // GTEST_CAN_STREAM_RESULTS__ + +// class OsStackTraceGetter + +const char* const OsStackTraceGetterInterface::kElidedFramesMarker = + "... " GTEST_NAME_ " internal frames ..."; + +std::string OsStackTraceGetter::CurrentStackTrace(int max_depth, int skip_count) + GTEST_LOCK_EXCLUDED_(mutex_) { +#if GTEST_HAS_ABSL + std::string result; + + if (max_depth <= 0) { + return result; + } + + max_depth = std::min(max_depth, kMaxStackTraceDepth); + + std::vector<void*> raw_stack(max_depth); + // Skips the frames requested by the caller, plus this function. + const int raw_stack_size = + absl::GetStackTrace(&raw_stack[0], max_depth, skip_count + 1); + + void* caller_frame = nullptr; + { + MutexLock lock(&mutex_); + caller_frame = caller_frame_; + } + + for (int i = 0; i < raw_stack_size; ++i) { + if (raw_stack[i] == caller_frame && + !GTEST_FLAG(show_internal_stack_frames)) { + // Add a marker to the trace and stop adding frames. + absl::StrAppend(&result, kElidedFramesMarker, "\n"); + break; + } + + char tmp[1024]; + const char* symbol = "(unknown)"; + if (absl::Symbolize(raw_stack[i], tmp, sizeof(tmp))) { + symbol = tmp; + } + + char line[1024]; + snprintf(line, sizeof(line), " %p: %s\n", raw_stack[i], symbol); + result += line; + } + + return result; + +#else // !GTEST_HAS_ABSL + static_cast<void>(max_depth); + static_cast<void>(skip_count); + return ""; +#endif // GTEST_HAS_ABSL +} + +void OsStackTraceGetter::UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_) { +#if GTEST_HAS_ABSL + void* caller_frame = nullptr; + if (absl::GetStackTrace(&caller_frame, 1, 3) <= 0) { + caller_frame = nullptr; + } + + MutexLock lock(&mutex_); + caller_frame_ = caller_frame; +#endif // GTEST_HAS_ABSL +} + +// A helper class that creates the premature-exit file in its +// constructor and deletes the file in its destructor. +class ScopedPrematureExitFile { + public: + explicit ScopedPrematureExitFile(const char* premature_exit_filepath) + : premature_exit_filepath_(premature_exit_filepath ? + premature_exit_filepath : "") { + // If a path to the premature-exit file is specified... + if (!premature_exit_filepath_.empty()) { + // create the file with a single "0" character in it. I/O + // errors are ignored as there's nothing better we can do and we + // don't want to fail the test because of this. + FILE* pfile = posix::FOpen(premature_exit_filepath, "w"); + fwrite("0", 1, 1, pfile); + fclose(pfile); + } + } + + ~ScopedPrematureExitFile() { +#if !defined GTEST_OS_ESP8266 + if (!premature_exit_filepath_.empty()) { + int retval = remove(premature_exit_filepath_.c_str()); + if (retval) { + GTEST_LOG_(ERROR) << "Failed to remove premature exit filepath \"" + << premature_exit_filepath_ << "\" with error " + << retval; + } + } +#endif + } + + private: + const std::string premature_exit_filepath_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile); +}; + +} // namespace internal + +// class TestEventListeners + +TestEventListeners::TestEventListeners() + : repeater_(new internal::TestEventRepeater()), + default_result_printer_(nullptr), + default_xml_generator_(nullptr) {} + +TestEventListeners::~TestEventListeners() { delete repeater_; } + +// Returns the standard listener responsible for the default console +// output. Can be removed from the listeners list to shut down default +// console output. Note that removing this object from the listener list +// with Release transfers its ownership to the user. +void TestEventListeners::Append(TestEventListener* listener) { + repeater_->Append(listener); +} + +// Removes the given event listener from the list and returns it. It then +// becomes the caller's responsibility to delete the listener. Returns +// NULL if the listener is not found in the list. +TestEventListener* TestEventListeners::Release(TestEventListener* listener) { + if (listener == default_result_printer_) + default_result_printer_ = nullptr; + else if (listener == default_xml_generator_) + default_xml_generator_ = nullptr; + return repeater_->Release(listener); +} + +// Returns repeater that broadcasts the TestEventListener events to all +// subscribers. +TestEventListener* TestEventListeners::repeater() { return repeater_; } + +// Sets the default_result_printer attribute to the provided listener. +// The listener is also added to the listener list and previous +// default_result_printer is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) { + if (default_result_printer_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_result_printer_); + default_result_printer_ = listener; + if (listener != nullptr) Append(listener); + } +} + +// Sets the default_xml_generator attribute to the provided listener. The +// listener is also added to the listener list and previous +// default_xml_generator is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultXmlGenerator(TestEventListener* listener) { + if (default_xml_generator_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_xml_generator_); + default_xml_generator_ = listener; + if (listener != nullptr) Append(listener); + } +} + +// Controls whether events will be forwarded by the repeater to the +// listeners in the list. +bool TestEventListeners::EventForwardingEnabled() const { + return repeater_->forwarding_enabled(); +} + +void TestEventListeners::SuppressEventForwarding() { + repeater_->set_forwarding_enabled(false); +} + +// class UnitTest + +// Gets the singleton UnitTest object. The first time this method is +// called, a UnitTest object is constructed and returned. Consecutive +// calls will return the same object. +// +// We don't protect this under mutex_ as a user is not supposed to +// call this before main() starts, from which point on the return +// value will never change. +UnitTest* UnitTest::GetInstance() { + // CodeGear C++Builder insists on a public destructor for the + // default implementation. Use this implementation to keep good OO + // design with private destructor. + +#if defined(__BORLANDC__) + static UnitTest* const instance = new UnitTest; + return instance; +#else + static UnitTest instance; + return &instance; +#endif // defined(__BORLANDC__) +} + +// Gets the number of successful test suites. +int UnitTest::successful_test_suite_count() const { + return impl()->successful_test_suite_count(); +} + +// Gets the number of failed test suites. +int UnitTest::failed_test_suite_count() const { + return impl()->failed_test_suite_count(); +} + +// Gets the number of all test suites. +int UnitTest::total_test_suite_count() const { + return impl()->total_test_suite_count(); +} + +// Gets the number of all test suites that contain at least one test +// that should run. +int UnitTest::test_suite_to_run_count() const { + return impl()->test_suite_to_run_count(); +} + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +int UnitTest::successful_test_case_count() const { + return impl()->successful_test_suite_count(); +} +int UnitTest::failed_test_case_count() const { + return impl()->failed_test_suite_count(); +} +int UnitTest::total_test_case_count() const { + return impl()->total_test_suite_count(); +} +int UnitTest::test_case_to_run_count() const { + return impl()->test_suite_to_run_count(); +} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// Gets the number of successful tests. +int UnitTest::successful_test_count() const { + return impl()->successful_test_count(); +} + +// Gets the number of skipped tests. +int UnitTest::skipped_test_count() const { + return impl()->skipped_test_count(); +} + +// Gets the number of failed tests. +int UnitTest::failed_test_count() const { return impl()->failed_test_count(); } + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTest::reportable_disabled_test_count() const { + return impl()->reportable_disabled_test_count(); +} + +// Gets the number of disabled tests. +int UnitTest::disabled_test_count() const { + return impl()->disabled_test_count(); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTest::reportable_test_count() const { + return impl()->reportable_test_count(); +} + +// Gets the number of all tests. +int UnitTest::total_test_count() const { return impl()->total_test_count(); } + +// Gets the number of tests that should run. +int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); } + +// Gets the time of the test program start, in ms from the start of the +// UNIX epoch. +internal::TimeInMillis UnitTest::start_timestamp() const { + return impl()->start_timestamp(); +} + +// Gets the elapsed time, in milliseconds. +internal::TimeInMillis UnitTest::elapsed_time() const { + return impl()->elapsed_time(); +} + +// Returns true if and only if the unit test passed (i.e. all test suites +// passed). +bool UnitTest::Passed() const { return impl()->Passed(); } + +// Returns true if and only if the unit test failed (i.e. some test suite +// failed or something outside of all tests failed). +bool UnitTest::Failed() const { return impl()->Failed(); } + +// Gets the i-th test suite among all the test suites. i can range from 0 to +// total_test_suite_count() - 1. If i is not in that range, returns NULL. +const TestSuite* UnitTest::GetTestSuite(int i) const { + return impl()->GetTestSuite(i); +} + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +const TestCase* UnitTest::GetTestCase(int i) const { + return impl()->GetTestCase(i); +} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// Returns the TestResult containing information on test failures and +// properties logged outside of individual test suites. +const TestResult& UnitTest::ad_hoc_test_result() const { + return *impl()->ad_hoc_test_result(); +} + +// Gets the i-th test suite among all the test suites. i can range from 0 to +// total_test_suite_count() - 1. If i is not in that range, returns NULL. +TestSuite* UnitTest::GetMutableTestSuite(int i) { + return impl()->GetMutableSuiteCase(i); +} + +// Returns the list of event listeners that can be used to track events +// inside Google Test. +TestEventListeners& UnitTest::listeners() { + return *impl()->listeners(); +} + +// Registers and returns a global test environment. When a test +// program is run, all global test environments will be set-up in the +// order they were registered. After all tests in the program have +// finished, all global test environments will be torn-down in the +// *reverse* order they were registered. +// +// The UnitTest object takes ownership of the given environment. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +Environment* UnitTest::AddEnvironment(Environment* env) { + if (env == nullptr) { + return nullptr; + } + + impl_->environments().push_back(env); + return env; +} + +// Adds a TestPartResult to the current TestResult object. All Google Test +// assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call +// this to report their results. The user code should use the +// assertion macros instead of calling this directly. +void UnitTest::AddTestPartResult( + TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) GTEST_LOCK_EXCLUDED_(mutex_) { + Message msg; + msg << message; + + internal::MutexLock lock(&mutex_); + if (impl_->gtest_trace_stack().size() > 0) { + msg << "\n" << GTEST_NAME_ << " trace:"; + + for (size_t i = impl_->gtest_trace_stack().size(); i > 0; --i) { + const internal::TraceInfo& trace = impl_->gtest_trace_stack()[i - 1]; + msg << "\n" << internal::FormatFileLocation(trace.file, trace.line) + << " " << trace.message; + } + } + + if (os_stack_trace.c_str() != nullptr && !os_stack_trace.empty()) { + msg << internal::kStackTraceMarker << os_stack_trace; + } + + const TestPartResult result = TestPartResult( + result_type, file_name, line_number, msg.GetString().c_str()); + impl_->GetTestPartResultReporterForCurrentThread()-> + ReportTestPartResult(result); + + if (result_type != TestPartResult::kSuccess && + result_type != TestPartResult::kSkip) { + // gtest_break_on_failure takes precedence over + // gtest_throw_on_failure. This allows a user to set the latter + // in the code (perhaps in order to use Google Test assertions + // with another testing framework) and specify the former on the + // command line for debugging. + if (GTEST_FLAG(break_on_failure)) { +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + // Using DebugBreak on Windows allows gtest to still break into a debugger + // when a failure happens and both the --gtest_break_on_failure and + // the --gtest_catch_exceptions flags are specified. + DebugBreak(); +#elif (!defined(__native_client__)) && \ + ((defined(__clang__) || defined(__GNUC__)) && \ + (defined(__x86_64__) || defined(__i386__))) + // with clang/gcc we can achieve the same effect on x86 by invoking int3 + asm("int3"); +#else + // Dereference nullptr through a volatile pointer to prevent the compiler + // from removing. We use this rather than abort() or __builtin_trap() for + // portability: some debuggers don't correctly trap abort(). + *static_cast<volatile int*>(nullptr) = 1; +#endif // GTEST_OS_WINDOWS + } else if (GTEST_FLAG(throw_on_failure)) { +#if GTEST_HAS_EXCEPTIONS + throw internal::GoogleTestFailureException(result); +#else + // We cannot call abort() as it generates a pop-up in debug mode + // that cannot be suppressed in VC 7.1 or below. + exit(1); +#endif + } + } +} + +// Adds a TestProperty to the current TestResult object when invoked from +// inside a test, to current TestSuite's ad_hoc_test_result_ when invoked +// from SetUpTestSuite or TearDownTestSuite, or to the global property set +// when invoked elsewhere. If the result already contains a property with +// the same key, the value will be updated. +void UnitTest::RecordProperty(const std::string& key, + const std::string& value) { + impl_->RecordProperty(TestProperty(key, value)); +} + +// Runs all tests in this UnitTest object and prints the result. +// Returns 0 if successful, or 1 otherwise. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +int UnitTest::Run() { + const bool in_death_test_child_process = + internal::GTEST_FLAG(internal_run_death_test).length() > 0; + + // Google Test implements this protocol for catching that a test + // program exits before returning control to Google Test: + // + // 1. Upon start, Google Test creates a file whose absolute path + // is specified by the environment variable + // TEST_PREMATURE_EXIT_FILE. + // 2. When Google Test has finished its work, it deletes the file. + // + // This allows a test runner to set TEST_PREMATURE_EXIT_FILE before + // running a Google-Test-based test program and check the existence + // of the file at the end of the test execution to see if it has + // exited prematurely. + + // If we are in the child process of a death test, don't + // create/delete the premature exit file, as doing so is unnecessary + // and will confuse the parent process. Otherwise, create/delete + // the file upon entering/leaving this function. If the program + // somehow exits before this function has a chance to return, the + // premature-exit file will be left undeleted, causing a test runner + // that understands the premature-exit-file protocol to report the + // test as having failed. + const internal::ScopedPrematureExitFile premature_exit_file( + in_death_test_child_process + ? nullptr + : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE")); + + // Captures the value of GTEST_FLAG(catch_exceptions). This value will be + // used for the duration of the program. + impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions)); + +#if GTEST_OS_WINDOWS + // Either the user wants Google Test to catch exceptions thrown by the + // tests or this is executing in the context of death test child + // process. In either case the user does not want to see pop-up dialogs + // about crashes - they are expected. + if (impl()->catch_exceptions() || in_death_test_child_process) { +# if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + // SetErrorMode doesn't exist on CE. + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | + SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); +# endif // !GTEST_OS_WINDOWS_MOBILE + +# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE + // Death test children can be terminated with _abort(). On Windows, + // _abort() can show a dialog with a warning message. This forces the + // abort message to go to stderr instead. + _set_error_mode(_OUT_TO_STDERR); +# endif + +# if defined(_MSC_VER) && !GTEST_OS_WINDOWS_MOBILE + // In the debug version, Visual Studio pops up a separate dialog + // offering a choice to debug the aborted program. We need to suppress + // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement + // executed. Google Test will notify the user of any unexpected + // failure via stderr. + if (!GTEST_FLAG(break_on_failure)) + _set_abort_behavior( + 0x0, // Clear the following flags: + _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. + + // In debug mode, the Windows CRT can crash with an assertion over invalid + // input (e.g. passing an invalid file descriptor). The default handling + // for these assertions is to pop up a dialog and wait for user input. + // Instead ask the CRT to dump such assertions to stderr non-interactively. + if (!IsDebuggerPresent()) { + (void)_CrtSetReportMode(_CRT_ASSERT, + _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + (void)_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); + } +# endif + } +#endif // GTEST_OS_WINDOWS + + return internal::HandleExceptionsInMethodIfSupported( + impl(), + &internal::UnitTestImpl::RunAllTests, + "auxiliary test code (environments or event listeners)") ? 0 : 1; +} + +// Returns the working directory when the first TEST() or TEST_F() was +// executed. +const char* UnitTest::original_working_dir() const { + return impl_->original_working_dir_.c_str(); +} + +// Returns the TestSuite object for the test that's currently running, +// or NULL if no test is running. +const TestSuite* UnitTest::current_test_suite() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_suite(); +} + +// Legacy API is still available but deprecated +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +const TestCase* UnitTest::current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_suite(); +} +#endif + +// Returns the TestInfo object for the test that's currently running, +// or NULL if no test is running. +const TestInfo* UnitTest::current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_info(); +} + +// Returns the random seed used at the start of the current test run. +int UnitTest::random_seed() const { return impl_->random_seed(); } + +// Returns ParameterizedTestSuiteRegistry object used to keep track of +// value-parameterized tests and instantiate and register them. +internal::ParameterizedTestSuiteRegistry& +UnitTest::parameterized_test_registry() GTEST_LOCK_EXCLUDED_(mutex_) { + return impl_->parameterized_test_registry(); +} + +// Creates an empty UnitTest. +UnitTest::UnitTest() { + impl_ = new internal::UnitTestImpl(this); +} + +// Destructor of UnitTest. +UnitTest::~UnitTest() { + delete impl_; +} + +// Pushes a trace defined by SCOPED_TRACE() on to the per-thread +// Google Test trace stack. +void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().push_back(trace); +} + +// Pops a trace from the per-thread Google Test trace stack. +void UnitTest::PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().pop_back(); +} + +namespace internal { + +UnitTestImpl::UnitTestImpl(UnitTest* parent) + : parent_(parent), + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4355 /* using this in initializer */) + default_global_test_part_result_reporter_(this), + default_per_thread_test_part_result_reporter_(this), + GTEST_DISABLE_MSC_WARNINGS_POP_() global_test_part_result_repoter_( + &default_global_test_part_result_reporter_), + per_thread_test_part_result_reporter_( + &default_per_thread_test_part_result_reporter_), + parameterized_test_registry_(), + parameterized_tests_registered_(false), + last_death_test_suite_(-1), + current_test_suite_(nullptr), + current_test_info_(nullptr), + ad_hoc_test_result_(), + os_stack_trace_getter_(nullptr), + post_flag_parse_init_performed_(false), + random_seed_(0), // Will be overridden by the flag before first use. + random_(0), // Will be reseeded before first use. + start_timestamp_(0), + elapsed_time_(0), +#if GTEST_HAS_DEATH_TEST + death_test_factory_(new DefaultDeathTestFactory), +#endif + // Will be overridden by the flag before first use. + catch_exceptions_(false) { + listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter); +} + +UnitTestImpl::~UnitTestImpl() { + // Deletes every TestSuite. + ForEach(test_suites_, internal::Delete<TestSuite>); + + // Deletes every Environment. + ForEach(environments_, internal::Delete<Environment>); + + delete os_stack_trace_getter_; +} + +// Adds a TestProperty to the current TestResult object when invoked in a +// context of a test, to current test suite's ad_hoc_test_result when invoke +// from SetUpTestSuite/TearDownTestSuite, or to the global property set +// otherwise. If the result already contains a property with the same key, +// the value will be updated. +void UnitTestImpl::RecordProperty(const TestProperty& test_property) { + std::string xml_element; + TestResult* test_result; // TestResult appropriate for property recording. + + if (current_test_info_ != nullptr) { + xml_element = "testcase"; + test_result = &(current_test_info_->result_); + } else if (current_test_suite_ != nullptr) { + xml_element = "testsuite"; + test_result = &(current_test_suite_->ad_hoc_test_result_); + } else { + xml_element = "testsuites"; + test_result = &ad_hoc_test_result_; + } + test_result->RecordProperty(xml_element, test_property); +} + +#if GTEST_HAS_DEATH_TEST +// Disables event forwarding if the control is currently in a death test +// subprocess. Must not be called before InitGoogleTest. +void UnitTestImpl::SuppressTestEventsIfInSubprocess() { + if (internal_run_death_test_flag_.get() != nullptr) + listeners()->SuppressEventForwarding(); +} +#endif // GTEST_HAS_DEATH_TEST + +// Initializes event listeners performing XML output as specified by +// UnitTestOptions. Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureXmlOutput() { + const std::string& output_format = UnitTestOptions::GetOutputFormat(); + if (output_format == "xml") { + listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); + } else if (output_format == "json") { + listeners()->SetDefaultXmlGenerator(new JsonUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); + } else if (output_format != "") { + GTEST_LOG_(WARNING) << "WARNING: unrecognized output format \"" + << output_format << "\" ignored."; + } +} + +#if GTEST_CAN_STREAM_RESULTS_ +// Initializes event listeners for streaming test results in string form. +// Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureStreamingOutput() { + const std::string& target = GTEST_FLAG(stream_result_to); + if (!target.empty()) { + const size_t pos = target.find(':'); + if (pos != std::string::npos) { + listeners()->Append(new StreamingListener(target.substr(0, pos), + target.substr(pos+1))); + } else { + GTEST_LOG_(WARNING) << "unrecognized streaming target \"" << target + << "\" ignored."; + } + } +} +#endif // GTEST_CAN_STREAM_RESULTS_ + +// Performs initialization dependent upon flag values obtained in +// ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to +// ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest +// this function is also called from RunAllTests. Since this function can be +// called more than once, it has to be idempotent. +void UnitTestImpl::PostFlagParsingInit() { + // Ensures that this function does not execute more than once. + if (!post_flag_parse_init_performed_) { + post_flag_parse_init_performed_ = true; + +#if defined(GTEST_CUSTOM_TEST_EVENT_LISTENER_) + // Register to send notifications about key process state changes. + listeners()->Append(new GTEST_CUSTOM_TEST_EVENT_LISTENER_()); +#endif // defined(GTEST_CUSTOM_TEST_EVENT_LISTENER_) + +#if GTEST_HAS_DEATH_TEST + InitDeathTestSubprocessControlInfo(); + SuppressTestEventsIfInSubprocess(); +#endif // GTEST_HAS_DEATH_TEST + + // Registers parameterized tests. This makes parameterized tests + // available to the UnitTest reflection API without running + // RUN_ALL_TESTS. + RegisterParameterizedTests(); + + // Configures listeners for XML output. This makes it possible for users + // to shut down the default XML output before invoking RUN_ALL_TESTS. + ConfigureXmlOutput(); + + if (GTEST_FLAG(brief)) { + listeners()->SetDefaultResultPrinter(new BriefUnitTestResultPrinter); + } + +#if GTEST_CAN_STREAM_RESULTS_ + // Configures listeners for streaming test results to the specified server. + ConfigureStreamingOutput(); +#endif // GTEST_CAN_STREAM_RESULTS_ + +#if GTEST_HAS_ABSL + if (GTEST_FLAG(install_failure_signal_handler)) { + absl::FailureSignalHandlerOptions options; + absl::InstallFailureSignalHandler(options); + } +#endif // GTEST_HAS_ABSL + } +} + +// A predicate that checks the name of a TestSuite against a known +// value. +// +// This is used for implementation of the UnitTest class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestSuiteNameIs is copyable. +class TestSuiteNameIs { + public: + // Constructor. + explicit TestSuiteNameIs(const std::string& name) : name_(name) {} + + // Returns true if and only if the name of test_suite matches name_. + bool operator()(const TestSuite* test_suite) const { + return test_suite != nullptr && + strcmp(test_suite->name(), name_.c_str()) == 0; + } + + private: + std::string name_; +}; + +// Finds and returns a TestSuite with the given name. If one doesn't +// exist, creates one and returns it. It's the CALLER'S +// RESPONSIBILITY to ensure that this function is only called WHEN THE +// TESTS ARE NOT SHUFFLED. +// +// Arguments: +// +// test_suite_name: name of the test suite +// type_param: the name of the test suite's type parameter, or NULL if +// this is not a typed or a type-parameterized test suite. +// set_up_tc: pointer to the function that sets up the test suite +// tear_down_tc: pointer to the function that tears down the test suite +TestSuite* UnitTestImpl::GetTestSuite( + const char* test_suite_name, const char* type_param, + internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc) { + // Can we find a TestSuite with the given name? + const auto test_suite = + std::find_if(test_suites_.rbegin(), test_suites_.rend(), + TestSuiteNameIs(test_suite_name)); + + if (test_suite != test_suites_.rend()) return *test_suite; + + // No. Let's create one. + auto* const new_test_suite = + new TestSuite(test_suite_name, type_param, set_up_tc, tear_down_tc); + + // Is this a death test suite? + if (internal::UnitTestOptions::MatchesFilter(test_suite_name, + kDeathTestSuiteFilter)) { + // Yes. Inserts the test suite after the last death test suite + // defined so far. This only works when the test suites haven't + // been shuffled. Otherwise we may end up running a death test + // after a non-death test. + ++last_death_test_suite_; + test_suites_.insert(test_suites_.begin() + last_death_test_suite_, + new_test_suite); + } else { + // No. Appends to the end of the list. + test_suites_.push_back(new_test_suite); + } + + test_suite_indices_.push_back(static_cast<int>(test_suite_indices_.size())); + return new_test_suite; +} + +// Helpers for setting up / tearing down the given environment. They +// are for use in the ForEach() function. +static void SetUpEnvironment(Environment* env) { env->SetUp(); } +static void TearDownEnvironment(Environment* env) { env->TearDown(); } + +// Runs all tests in this UnitTest object, prints the result, and +// returns true if all tests are successful. If any exception is +// thrown during a test, the test is considered to be failed, but the +// rest of the tests will still be run. +// +// When parameterized tests are enabled, it expands and registers +// parameterized tests first in RegisterParameterizedTests(). +// All other functions called from RunAllTests() may safely assume that +// parameterized tests are ready to be counted and run. +bool UnitTestImpl::RunAllTests() { + // True if and only if Google Test is initialized before RUN_ALL_TESTS() is + // called. + const bool gtest_is_initialized_before_run_all_tests = GTestIsInitialized(); + + // Do not run any test if the --help flag was specified. + if (g_help_flag) + return true; + + // Repeats the call to the post-flag parsing initialization in case the + // user didn't call InitGoogleTest. + PostFlagParsingInit(); + + // Even if sharding is not on, test runners may want to use the + // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding + // protocol. + internal::WriteToShardStatusFileIfNeeded(); + + // True if and only if we are in a subprocess for running a thread-safe-style + // death test. + bool in_subprocess_for_death_test = false; + +#if GTEST_HAS_DEATH_TEST + in_subprocess_for_death_test = + (internal_run_death_test_flag_.get() != nullptr); +# if defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_) + if (in_subprocess_for_death_test) { + GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_(); + } +# endif // defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_) +#endif // GTEST_HAS_DEATH_TEST + + const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex, + in_subprocess_for_death_test); + + // Compares the full test names with the filter to decide which + // tests to run. + const bool has_tests_to_run = FilterTests(should_shard + ? HONOR_SHARDING_PROTOCOL + : IGNORE_SHARDING_PROTOCOL) > 0; + + // Lists the tests and exits if the --gtest_list_tests flag was specified. + if (GTEST_FLAG(list_tests)) { + // This must be called *after* FilterTests() has been called. + ListTestsMatchingFilter(); + return true; + } + + random_seed_ = GTEST_FLAG(shuffle) ? + GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0; + + // True if and only if at least one test has failed. + bool failed = false; + + TestEventListener* repeater = listeners()->repeater(); + + start_timestamp_ = GetTimeInMillis(); + repeater->OnTestProgramStart(*parent_); + + // How many times to repeat the tests? We don't want to repeat them + // when we are inside the subprocess of a death test. + const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat); + // Repeats forever if the repeat count is negative. + const bool gtest_repeat_forever = repeat < 0; + for (int i = 0; gtest_repeat_forever || i != repeat; i++) { + // We want to preserve failures generated by ad-hoc test + // assertions executed before RUN_ALL_TESTS(). + ClearNonAdHocTestResult(); + + Timer timer; + + // Shuffles test suites and tests if requested. + if (has_tests_to_run && GTEST_FLAG(shuffle)) { + random()->Reseed(static_cast<uint32_t>(random_seed_)); + // This should be done before calling OnTestIterationStart(), + // such that a test event listener can see the actual test order + // in the event. + ShuffleTests(); + } + + // Tells the unit test event listeners that the tests are about to start. + repeater->OnTestIterationStart(*parent_, i); + + // Runs each test suite if there is at least one test to run. + if (has_tests_to_run) { + // Sets up all environments beforehand. + repeater->OnEnvironmentsSetUpStart(*parent_); + ForEach(environments_, SetUpEnvironment); + repeater->OnEnvironmentsSetUpEnd(*parent_); + + // Runs the tests only if there was no fatal failure or skip triggered + // during global set-up. + if (Test::IsSkipped()) { + // Emit diagnostics when global set-up calls skip, as it will not be + // emitted by default. + TestResult& test_result = + *internal::GetUnitTestImpl()->current_test_result(); + for (int j = 0; j < test_result.total_part_count(); ++j) { + const TestPartResult& test_part_result = + test_result.GetTestPartResult(j); + if (test_part_result.type() == TestPartResult::kSkip) { + const std::string& result = test_part_result.message(); + printf("%s\n", result.c_str()); + } + } + fflush(stdout); + } else if (!Test::HasFatalFailure()) { + for (int test_index = 0; test_index < total_test_suite_count(); + test_index++) { + GetMutableSuiteCase(test_index)->Run(); + if (GTEST_FLAG(fail_fast) && + GetMutableSuiteCase(test_index)->Failed()) { + for (int j = test_index + 1; j < total_test_suite_count(); j++) { + GetMutableSuiteCase(j)->Skip(); + } + break; + } + } + } else if (Test::HasFatalFailure()) { + // If there was a fatal failure during the global setup then we know we + // aren't going to run any tests. Explicitly mark all of the tests as + // skipped to make this obvious in the output. + for (int test_index = 0; test_index < total_test_suite_count(); + test_index++) { + GetMutableSuiteCase(test_index)->Skip(); + } + } + + // Tears down all environments in reverse order afterwards. + repeater->OnEnvironmentsTearDownStart(*parent_); + std::for_each(environments_.rbegin(), environments_.rend(), + TearDownEnvironment); + repeater->OnEnvironmentsTearDownEnd(*parent_); + } + + elapsed_time_ = timer.Elapsed(); + + // Tells the unit test event listener that the tests have just finished. + repeater->OnTestIterationEnd(*parent_, i); + + // Gets the result and clears it. + if (!Passed()) { + failed = true; + } + + // Restores the original test order after the iteration. This + // allows the user to quickly repro a failure that happens in the + // N-th iteration without repeating the first (N - 1) iterations. + // This is not enclosed in "if (GTEST_FLAG(shuffle)) { ... }", in + // case the user somehow changes the value of the flag somewhere + // (it's always safe to unshuffle the tests). + UnshuffleTests(); + + if (GTEST_FLAG(shuffle)) { + // Picks a new random seed for each iteration. + random_seed_ = GetNextRandomSeed(random_seed_); + } + } + + repeater->OnTestProgramEnd(*parent_); + + if (!gtest_is_initialized_before_run_all_tests) { + ColoredPrintf( + GTestColor::kRed, + "\nIMPORTANT NOTICE - DO NOT IGNORE:\n" + "This test program did NOT call " GTEST_INIT_GOOGLE_TEST_NAME_ + "() before calling RUN_ALL_TESTS(). This is INVALID. Soon " GTEST_NAME_ + " will start to enforce the valid usage. " + "Please fix it ASAP, or IT WILL START TO FAIL.\n"); // NOLINT +#if GTEST_FOR_GOOGLE_ + ColoredPrintf(GTestColor::kRed, + "For more details, see http://wiki/Main/ValidGUnitMain.\n"); +#endif // GTEST_FOR_GOOGLE_ + } + + return !failed; +} + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded() { + const char* const test_shard_file = posix::GetEnv(kTestShardStatusFile); + if (test_shard_file != nullptr) { + FILE* const file = posix::FOpen(test_shard_file, "w"); + if (file == nullptr) { + ColoredPrintf(GTestColor::kRed, + "Could not write to the test shard status file \"%s\" " + "specified by the %s environment variable.\n", + test_shard_file, kTestShardStatusFile); + fflush(stdout); + exit(EXIT_FAILURE); + } + fclose(file); + } +} + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (i.e., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +bool ShouldShard(const char* total_shards_env, + const char* shard_index_env, + bool in_subprocess_for_death_test) { + if (in_subprocess_for_death_test) { + return false; + } + + const int32_t total_shards = Int32FromEnvOrDie(total_shards_env, -1); + const int32_t shard_index = Int32FromEnvOrDie(shard_index_env, -1); + + if (total_shards == -1 && shard_index == -1) { + return false; + } else if (total_shards == -1 && shard_index != -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestShardIndex << " = " << shard_index + << ", but have left " << kTestTotalShards << " unset.\n"; + ColoredPrintf(GTestColor::kRed, "%s", msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (total_shards != -1 && shard_index == -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestTotalShards << " = " << total_shards + << ", but have left " << kTestShardIndex << " unset.\n"; + ColoredPrintf(GTestColor::kRed, "%s", msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (shard_index < 0 || shard_index >= total_shards) { + const Message msg = Message() + << "Invalid environment variables: we require 0 <= " + << kTestShardIndex << " < " << kTestTotalShards + << ", but you have " << kTestShardIndex << "=" << shard_index + << ", " << kTestTotalShards << "=" << total_shards << ".\n"; + ColoredPrintf(GTestColor::kRed, "%s", msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } + + return total_shards > 1; +} + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error +// and aborts. +int32_t Int32FromEnvOrDie(const char* var, int32_t default_val) { + const char* str_val = posix::GetEnv(var); + if (str_val == nullptr) { + return default_val; + } + + int32_t result; + if (!ParseInt32(Message() << "The value of environment variable " << var, + str_val, &result)) { + exit(EXIT_FAILURE); + } + return result; +} + +// Given the total number of shards, the shard index, and the test id, +// returns true if and only if the test should be run on this shard. The test id +// is some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) { + return (test_id % total_shards) == shard_index; +} + +// Compares the name of each test with the user-specified filter to +// decide whether the test should be run, then records the result in +// each TestSuite and TestInfo object. +// If shard_tests == true, further filters tests based on sharding +// variables in the environment - see +// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md +// . Returns the number of tests that should run. +int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { + const int32_t total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestTotalShards, -1) : -1; + const int32_t shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestShardIndex, -1) : -1; + + // num_runnable_tests are the number of tests that will + // run across all shards (i.e., match filter and are not disabled). + // num_selected_tests are the number of tests to be run on + // this shard. + int num_runnable_tests = 0; + int num_selected_tests = 0; + for (auto* test_suite : test_suites_) { + const std::string& test_suite_name = test_suite->name(); + test_suite->set_should_run(false); + + for (size_t j = 0; j < test_suite->test_info_list().size(); j++) { + TestInfo* const test_info = test_suite->test_info_list()[j]; + const std::string test_name(test_info->name()); + // A test is disabled if test suite name or test name matches + // kDisableTestFilter. + const bool is_disabled = internal::UnitTestOptions::MatchesFilter( + test_suite_name, kDisableTestFilter) || + internal::UnitTestOptions::MatchesFilter( + test_name, kDisableTestFilter); + test_info->is_disabled_ = is_disabled; + + const bool matches_filter = internal::UnitTestOptions::FilterMatchesTest( + test_suite_name, test_name); + test_info->matches_filter_ = matches_filter; + + const bool is_runnable = + (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && + matches_filter; + + const bool is_in_another_shard = + shard_tests != IGNORE_SHARDING_PROTOCOL && + !ShouldRunTestOnShard(total_shards, shard_index, num_runnable_tests); + test_info->is_in_another_shard_ = is_in_another_shard; + const bool is_selected = is_runnable && !is_in_another_shard; + + num_runnable_tests += is_runnable; + num_selected_tests += is_selected; + + test_info->should_run_ = is_selected; + test_suite->set_should_run(test_suite->should_run() || is_selected); + } + } + return num_selected_tests; +} + +// Prints the given C-string on a single line by replacing all '\n' +// characters with string "\\n". If the output takes more than +// max_length characters, only prints the first max_length characters +// and "...". +static void PrintOnOneLine(const char* str, int max_length) { + if (str != nullptr) { + for (int i = 0; *str != '\0'; ++str) { + if (i >= max_length) { + printf("..."); + break; + } + if (*str == '\n') { + printf("\\n"); + i += 2; + } else { + printf("%c", *str); + ++i; + } + } + } +} + +// Prints the names of the tests matching the user-specified filter flag. +void UnitTestImpl::ListTestsMatchingFilter() { + // Print at most this many characters for each type/value parameter. + const int kMaxParamLength = 250; + + for (auto* test_suite : test_suites_) { + bool printed_test_suite_name = false; + + for (size_t j = 0; j < test_suite->test_info_list().size(); j++) { + const TestInfo* const test_info = test_suite->test_info_list()[j]; + if (test_info->matches_filter_) { + if (!printed_test_suite_name) { + printed_test_suite_name = true; + printf("%s.", test_suite->name()); + if (test_suite->type_param() != nullptr) { + printf(" # %s = ", kTypeParamLabel); + // We print the type parameter on a single line to make + // the output easy to parse by a program. + PrintOnOneLine(test_suite->type_param(), kMaxParamLength); + } + printf("\n"); + } + printf(" %s", test_info->name()); + if (test_info->value_param() != nullptr) { + printf(" # %s = ", kValueParamLabel); + // We print the value parameter on a single line to make the + // output easy to parse by a program. + PrintOnOneLine(test_info->value_param(), kMaxParamLength); + } + printf("\n"); + } + } + } + fflush(stdout); + const std::string& output_format = UnitTestOptions::GetOutputFormat(); + if (output_format == "xml" || output_format == "json") { + FILE* fileout = OpenFileForWriting( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str()); + std::stringstream stream; + if (output_format == "xml") { + XmlUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str()) + .PrintXmlTestsList(&stream, test_suites_); + } else if (output_format == "json") { + JsonUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str()) + .PrintJsonTestList(&stream, test_suites_); + } + fprintf(fileout, "%s", StringStreamToString(&stream).c_str()); + fclose(fileout); + } +} + +// Sets the OS stack trace getter. +// +// Does nothing if the input and the current OS stack trace getter are +// the same; otherwise, deletes the old getter and makes the input the +// current getter. +void UnitTestImpl::set_os_stack_trace_getter( + OsStackTraceGetterInterface* getter) { + if (os_stack_trace_getter_ != getter) { + delete os_stack_trace_getter_; + os_stack_trace_getter_ = getter; + } +} + +// Returns the current OS stack trace getter if it is not NULL; +// otherwise, creates an OsStackTraceGetter, makes it the current +// getter, and returns it. +OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { + if (os_stack_trace_getter_ == nullptr) { +#ifdef GTEST_OS_STACK_TRACE_GETTER_ + os_stack_trace_getter_ = new GTEST_OS_STACK_TRACE_GETTER_; +#else + os_stack_trace_getter_ = new OsStackTraceGetter; +#endif // GTEST_OS_STACK_TRACE_GETTER_ + } + + return os_stack_trace_getter_; +} + +// Returns the most specific TestResult currently running. +TestResult* UnitTestImpl::current_test_result() { + if (current_test_info_ != nullptr) { + return ¤t_test_info_->result_; + } + if (current_test_suite_ != nullptr) { + return ¤t_test_suite_->ad_hoc_test_result_; + } + return &ad_hoc_test_result_; +} + +// Shuffles all test suites, and the tests within each test suite, +// making sure that death tests are still run first. +void UnitTestImpl::ShuffleTests() { + // Shuffles the death test suites. + ShuffleRange(random(), 0, last_death_test_suite_ + 1, &test_suite_indices_); + + // Shuffles the non-death test suites. + ShuffleRange(random(), last_death_test_suite_ + 1, + static_cast<int>(test_suites_.size()), &test_suite_indices_); + + // Shuffles the tests inside each test suite. + for (auto& test_suite : test_suites_) { + test_suite->ShuffleTests(random()); + } +} + +// Restores the test suites and tests to their order before the first shuffle. +void UnitTestImpl::UnshuffleTests() { + for (size_t i = 0; i < test_suites_.size(); i++) { + // Unshuffles the tests in each test suite. + test_suites_[i]->UnshuffleTests(); + // Resets the index of each test suite. + test_suite_indices_[i] = static_cast<int>(i); + } +} + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +std::string GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, + int skip_count) { + // We pass skip_count + 1 to skip this wrapper function in addition + // to what the user really wants to skip. + return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); +} + +// Used by the GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_ macro to +// suppress unreachable code warnings. +namespace { +class ClassUniqueToAlwaysTrue {}; +} + +bool IsTrue(bool condition) { return condition; } + +bool AlwaysTrue() { +#if GTEST_HAS_EXCEPTIONS + // This condition is always false so AlwaysTrue() never actually throws, + // but it makes the compiler think that it may throw. + if (IsTrue(false)) + throw ClassUniqueToAlwaysTrue(); +#endif // GTEST_HAS_EXCEPTIONS + return true; +} + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +bool SkipPrefix(const char* prefix, const char** pstr) { + const size_t prefix_len = strlen(prefix); + if (strncmp(*pstr, prefix, prefix_len) == 0) { + *pstr += prefix_len; + return true; + } + return false; +} + +// Parses a string as a command line flag. The string should have +// the format "--flag=value". When def_optional is true, the "=value" +// part can be omitted. +// +// Returns the value of the flag, or NULL if the parsing failed. +static const char* ParseFlagValue(const char* str, const char* flag, + bool def_optional) { + // str and flag must not be NULL. + if (str == nullptr || flag == nullptr) return nullptr; + + // The flag must start with "--" followed by GTEST_FLAG_PREFIX_. + const std::string flag_str = std::string("--") + GTEST_FLAG_PREFIX_ + flag; + const size_t flag_len = flag_str.length(); + if (strncmp(str, flag_str.c_str(), flag_len) != 0) return nullptr; + + // Skips the flag name. + const char* flag_end = str + flag_len; + + // When def_optional is true, it's OK to not have a "=value" part. + if (def_optional && (flag_end[0] == '\0')) { + return flag_end; + } + + // If def_optional is true and there are more characters after the + // flag name, or if def_optional is false, there must be a '=' after + // the flag name. + if (flag_end[0] != '=') return nullptr; + + // Returns the string after "=". + return flag_end + 1; +} + +// Parses a string for a bool flag, in the form of either +// "--flag=value" or "--flag". +// +// In the former case, the value is taken as true as long as it does +// not start with '0', 'f', or 'F'. +// +// In the latter case, the value is taken as true. +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +static bool ParseBoolFlag(const char* str, const char* flag, bool* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == nullptr) return false; + + // Converts the string value to a bool. + *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + return true; +} + +// Parses a string for an int32_t flag, in the form of "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseInt32Flag(const char* str, const char* flag, int32_t* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == nullptr) return false; + + // Sets *value to the value of the flag. + return ParseInt32(Message() << "The value of flag --" << flag, + value_str, value); +} + +// Parses a string for a string flag, in the form of "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +template <typename String> +static bool ParseStringFlag(const char* str, const char* flag, String* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == nullptr) return false; + + // Sets *value to the value of the flag. + *value = value_str; + return true; +} + +// Determines whether a string has a prefix that Google Test uses for its +// flags, i.e., starts with GTEST_FLAG_PREFIX_ or GTEST_FLAG_PREFIX_DASH_. +// If Google Test detects that a command line flag has its prefix but is not +// recognized, it will print its help message. Flags starting with +// GTEST_INTERNAL_PREFIX_ followed by "internal_" are considered Google Test +// internal flags and do not trigger the help message. +static bool HasGoogleTestFlagPrefix(const char* str) { + return (SkipPrefix("--", &str) || + SkipPrefix("-", &str) || + SkipPrefix("/", &str)) && + !SkipPrefix(GTEST_FLAG_PREFIX_ "internal_", &str) && + (SkipPrefix(GTEST_FLAG_PREFIX_, &str) || + SkipPrefix(GTEST_FLAG_PREFIX_DASH_, &str)); +} + +// Prints a string containing code-encoded text. The following escape +// sequences can be used in the string to control the text color: +// +// @@ prints a single '@' character. +// @R changes the color to red. +// @G changes the color to green. +// @Y changes the color to yellow. +// @D changes to the default terminal text color. +// +static void PrintColorEncoded(const char* str) { + GTestColor color = GTestColor::kDefault; // The current color. + + // Conceptually, we split the string into segments divided by escape + // sequences. Then we print one segment at a time. At the end of + // each iteration, the str pointer advances to the beginning of the + // next segment. + for (;;) { + const char* p = strchr(str, '@'); + if (p == nullptr) { + ColoredPrintf(color, "%s", str); + return; + } + + ColoredPrintf(color, "%s", std::string(str, p).c_str()); + + const char ch = p[1]; + str = p + 2; + if (ch == '@') { + ColoredPrintf(color, "@"); + } else if (ch == 'D') { + color = GTestColor::kDefault; + } else if (ch == 'R') { + color = GTestColor::kRed; + } else if (ch == 'G') { + color = GTestColor::kGreen; + } else if (ch == 'Y') { + color = GTestColor::kYellow; + } else { + --str; + } + } +} + +static const char kColorEncodedHelpMessage[] = + "This program contains tests written using " GTEST_NAME_ + ". You can use the\n" + "following command line flags to control its behavior:\n" + "\n" + "Test Selection:\n" + " @G--" GTEST_FLAG_PREFIX_ + "list_tests@D\n" + " List the names of all tests instead of running them. The name of\n" + " TEST(Foo, Bar) is \"Foo.Bar\".\n" + " @G--" GTEST_FLAG_PREFIX_ + "filter=@YPOSITIVE_PATTERNS" + "[@G-@YNEGATIVE_PATTERNS]@D\n" + " Run only the tests whose name matches one of the positive patterns " + "but\n" + " none of the negative patterns. '?' matches any single character; " + "'*'\n" + " matches any substring; ':' separates two patterns.\n" + " @G--" GTEST_FLAG_PREFIX_ + "also_run_disabled_tests@D\n" + " Run all disabled tests too.\n" + "\n" + "Test Execution:\n" + " @G--" GTEST_FLAG_PREFIX_ + "repeat=@Y[COUNT]@D\n" + " Run the tests repeatedly; use a negative count to repeat forever.\n" + " @G--" GTEST_FLAG_PREFIX_ + "shuffle@D\n" + " Randomize tests' orders on every iteration.\n" + " @G--" GTEST_FLAG_PREFIX_ + "random_seed=@Y[NUMBER]@D\n" + " Random number seed to use for shuffling test orders (between 1 and\n" + " 99999, or 0 to use a seed based on the current time).\n" + "\n" + "Test Output:\n" + " @G--" GTEST_FLAG_PREFIX_ + "color=@Y(@Gyes@Y|@Gno@Y|@Gauto@Y)@D\n" + " Enable/disable colored output. The default is @Gauto@D.\n" + " @G--" GTEST_FLAG_PREFIX_ + "brief=1@D\n" + " Only print test failures.\n" + " @G--" GTEST_FLAG_PREFIX_ + "print_time=0@D\n" + " Don't print the elapsed time of each test.\n" + " @G--" GTEST_FLAG_PREFIX_ + "output=@Y(@Gjson@Y|@Gxml@Y)[@G:@YDIRECTORY_PATH@G" GTEST_PATH_SEP_ + "@Y|@G:@YFILE_PATH]@D\n" + " Generate a JSON or XML report in the given directory or with the " + "given\n" + " file name. @YFILE_PATH@D defaults to @Gtest_detail.xml@D.\n" +# if GTEST_CAN_STREAM_RESULTS_ + " @G--" GTEST_FLAG_PREFIX_ + "stream_result_to=@YHOST@G:@YPORT@D\n" + " Stream test results to the given server.\n" +# endif // GTEST_CAN_STREAM_RESULTS_ + "\n" + "Assertion Behavior:\n" +# if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS + " @G--" GTEST_FLAG_PREFIX_ + "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n" + " Set the default death test style.\n" +# endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS + " @G--" GTEST_FLAG_PREFIX_ + "break_on_failure@D\n" + " Turn assertion failures into debugger break-points.\n" + " @G--" GTEST_FLAG_PREFIX_ + "throw_on_failure@D\n" + " Turn assertion failures into C++ exceptions for use by an external\n" + " test framework.\n" + " @G--" GTEST_FLAG_PREFIX_ + "catch_exceptions=0@D\n" + " Do not report exceptions as test failures. Instead, allow them\n" + " to crash the program or throw a pop-up (on Windows).\n" + "\n" + "Except for @G--" GTEST_FLAG_PREFIX_ + "list_tests@D, you can alternatively set " + "the corresponding\n" + "environment variable of a flag (all letters in upper-case). For example, " + "to\n" + "disable colored text output, you can either specify " + "@G--" GTEST_FLAG_PREFIX_ + "color=no@D or set\n" + "the @G" GTEST_FLAG_PREFIX_UPPER_ + "COLOR@D environment variable to @Gno@D.\n" + "\n" + "For more information, please read the " GTEST_NAME_ + " documentation at\n" + "@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_ + "\n" + "(not one in your own code or tests), please report it to\n" + "@G<" GTEST_DEV_EMAIL_ ">@D.\n"; + +static bool ParseGoogleTestFlag(const char* const arg) { + return ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag, + >EST_FLAG(also_run_disabled_tests)) || + ParseBoolFlag(arg, kBreakOnFailureFlag, + >EST_FLAG(break_on_failure)) || + ParseBoolFlag(arg, kCatchExceptionsFlag, + >EST_FLAG(catch_exceptions)) || + ParseStringFlag(arg, kColorFlag, >EST_FLAG(color)) || + ParseStringFlag(arg, kDeathTestStyleFlag, + >EST_FLAG(death_test_style)) || + ParseBoolFlag(arg, kDeathTestUseFork, + >EST_FLAG(death_test_use_fork)) || + ParseBoolFlag(arg, kFailFast, >EST_FLAG(fail_fast)) || + ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) || + ParseStringFlag(arg, kInternalRunDeathTestFlag, + >EST_FLAG(internal_run_death_test)) || + ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || + ParseStringFlag(arg, kOutputFlag, >EST_FLAG(output)) || + ParseBoolFlag(arg, kBriefFlag, >EST_FLAG(brief)) || + ParseBoolFlag(arg, kPrintTimeFlag, >EST_FLAG(print_time)) || + ParseBoolFlag(arg, kPrintUTF8Flag, >EST_FLAG(print_utf8)) || + ParseInt32Flag(arg, kRandomSeedFlag, >EST_FLAG(random_seed)) || + ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) || + ParseBoolFlag(arg, kShuffleFlag, >EST_FLAG(shuffle)) || + ParseInt32Flag(arg, kStackTraceDepthFlag, + >EST_FLAG(stack_trace_depth)) || + ParseStringFlag(arg, kStreamResultToFlag, + >EST_FLAG(stream_result_to)) || + ParseBoolFlag(arg, kThrowOnFailureFlag, >EST_FLAG(throw_on_failure)); +} + +#if GTEST_USE_OWN_FLAGFILE_FLAG_ +static void LoadFlagsFromFile(const std::string& path) { + FILE* flagfile = posix::FOpen(path.c_str(), "r"); + if (!flagfile) { + GTEST_LOG_(FATAL) << "Unable to open file \"" << GTEST_FLAG(flagfile) + << "\""; + } + std::string contents(ReadEntireFile(flagfile)); + posix::FClose(flagfile); + std::vector<std::string> lines; + SplitString(contents, '\n', &lines); + for (size_t i = 0; i < lines.size(); ++i) { + if (lines[i].empty()) + continue; + if (!ParseGoogleTestFlag(lines[i].c_str())) + g_help_flag = true; + } +} +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. The type parameter CharType can be +// instantiated to either char or wchar_t. +template <typename CharType> +void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { + for (int i = 1; i < *argc; i++) { + const std::string arg_string = StreamableToString(argv[i]); + const char* const arg = arg_string.c_str(); + + using internal::ParseBoolFlag; + using internal::ParseInt32Flag; + using internal::ParseStringFlag; + + bool remove_flag = false; + if (ParseGoogleTestFlag(arg)) { + remove_flag = true; +#if GTEST_USE_OWN_FLAGFILE_FLAG_ + } else if (ParseStringFlag(arg, kFlagfileFlag, >EST_FLAG(flagfile))) { + LoadFlagsFromFile(GTEST_FLAG(flagfile)); + remove_flag = true; +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + } else if (arg_string == "--help" || arg_string == "-h" || + arg_string == "-?" || arg_string == "/?" || + HasGoogleTestFlagPrefix(arg)) { + // Both help flag and unrecognized Google Test flags (excluding + // internal ones) trigger help display. + g_help_flag = true; + } + + if (remove_flag) { + // Shift the remainder of the argv list left by one. Note + // that argv has (*argc + 1) elements, the last one always being + // NULL. The following loop moves the trailing NULL element as + // well. + for (int j = i; j != *argc; j++) { + argv[j] = argv[j + 1]; + } + + // Decrements the argument count. + (*argc)--; + + // We also need to decrement the iterator as we just removed + // an element. + i--; + } + } + + if (g_help_flag) { + // We print the help here instead of in RUN_ALL_TESTS(), as the + // latter may not be called at all if the user is using Google + // Test with another testing framework. + PrintColorEncoded(kColorEncodedHelpMessage); + } +} + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +void ParseGoogleTestFlagsOnly(int* argc, char** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); + + // Fix the value of *_NSGetArgc() on macOS, but if and only if + // *_NSGetArgv() == argv + // Only applicable to char** version of argv +#if GTEST_OS_MAC +#ifndef GTEST_OS_IOS + if (*_NSGetArgv() == argv) { + *_NSGetArgc() = *argc; + } +#endif +#endif +} +void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} + +// The internal implementation of InitGoogleTest(). +// +// The type parameter CharType can be instantiated to either char or +// wchar_t. +template <typename CharType> +void InitGoogleTestImpl(int* argc, CharType** argv) { + // We don't want to run the initialization code twice. + if (GTestIsInitialized()) return; + + if (*argc <= 0) return; + + g_argvs.clear(); + for (int i = 0; i != *argc; i++) { + g_argvs.push_back(StreamableToString(argv[i])); + } + +#if GTEST_HAS_ABSL + absl::InitializeSymbolizer(g_argvs[0].c_str()); +#endif // GTEST_HAS_ABSL + + ParseGoogleTestFlagsOnly(argc, argv); + GetUnitTestImpl()->PostFlagParsingInit(); +} + +} // namespace internal + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +void InitGoogleTest(int* argc, char** argv) { +#if defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_(argc, argv); +#else // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + internal::InitGoogleTestImpl(argc, argv); +#endif // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) +} + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +void InitGoogleTest(int* argc, wchar_t** argv) { +#if defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_(argc, argv); +#else // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + internal::InitGoogleTestImpl(argc, argv); +#endif // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) +} + +// This overloaded version can be used on Arduino/embedded platforms where +// there is no argc/argv. +void InitGoogleTest() { + // Since Arduino doesn't have a command line, fake out the argc/argv arguments + int argc = 1; + const auto arg0 = "dummy"; + char* argv0 = const_cast<char*>(arg0); + char** argv = &argv0; + +#if defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_(&argc, argv); +#else // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) + internal::InitGoogleTestImpl(&argc, argv); +#endif // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) +} + +std::string TempDir() { +#if defined(GTEST_CUSTOM_TEMPDIR_FUNCTION_) + return GTEST_CUSTOM_TEMPDIR_FUNCTION_(); +#elif GTEST_OS_WINDOWS_MOBILE + return "\\temp\\"; +#elif GTEST_OS_WINDOWS + const char* temp_dir = internal::posix::GetEnv("TEMP"); + if (temp_dir == nullptr || temp_dir[0] == '\0') { + return "\\temp\\"; + } else if (temp_dir[strlen(temp_dir) - 1] == '\\') { + return temp_dir; + } else { + return std::string(temp_dir) + "\\"; + } +#elif GTEST_OS_LINUX_ANDROID + const char* temp_dir = internal::posix::GetEnv("TEST_TMPDIR"); + if (temp_dir == nullptr || temp_dir[0] == '\0') { + return "/data/local/tmp/"; + } else { + return temp_dir; + } +#elif GTEST_OS_LINUX + const char* temp_dir = internal::posix::GetEnv("TEST_TMPDIR"); + if (temp_dir == nullptr || temp_dir[0] == '\0') { + return "/tmp/"; + } else { + return temp_dir; + } +#else + return "/tmp/"; +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Class ScopedTrace + +// Pushes the given source file location and message onto a per-thread +// trace stack maintained by Google Test. +void ScopedTrace::PushTrace(const char* file, int line, std::string message) { + internal::TraceInfo trace; + trace.file = file; + trace.line = line; + trace.message.swap(message); + + UnitTest::GetInstance()->PushGTestTrace(trace); +} + +// Pops the info pushed by the c'tor. +ScopedTrace::~ScopedTrace() + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { + UnitTest::GetInstance()->PopGTestTrace(); +} + +} // namespace testing +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// +// This file implements death tests. + + +#include <functional> +#include <utility> + + +#if GTEST_HAS_DEATH_TEST + +# if GTEST_OS_MAC +# include <crt_externs.h> +# endif // GTEST_OS_MAC + +# include <errno.h> +# include <fcntl.h> +# include <limits.h> + +# if GTEST_OS_LINUX +# include <signal.h> +# endif // GTEST_OS_LINUX + +# include <stdarg.h> + +# if GTEST_OS_WINDOWS +# include <windows.h> +# else +# include <sys/mman.h> +# include <sys/wait.h> +# endif // GTEST_OS_WINDOWS + +# if GTEST_OS_QNX +# include <spawn.h> +# endif // GTEST_OS_QNX + +# if GTEST_OS_FUCHSIA +# include <lib/fdio/fd.h> +# include <lib/fdio/io.h> +# include <lib/fdio/spawn.h> +# include <lib/zx/channel.h> +# include <lib/zx/port.h> +# include <lib/zx/process.h> +# include <lib/zx/socket.h> +# include <zircon/processargs.h> +# include <zircon/syscalls.h> +# include <zircon/syscalls/policy.h> +# include <zircon/syscalls/port.h> +# endif // GTEST_OS_FUCHSIA + +#endif // GTEST_HAS_DEATH_TEST + + +namespace testing { + +// Constants. + +// The default death test style. +// +// This is defined in internal/gtest-port.h as "fast", but can be overridden by +// a definition in internal/custom/gtest-port.h. The recommended value, which is +// used internally at Google, is "threadsafe". +static const char kDefaultDeathTestStyle[] = GTEST_DEFAULT_DEATH_TEST_STYLE; + +GTEST_DEFINE_string_( + death_test_style, + internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle), + "Indicates how to run a death test in a forked child process: " + "\"threadsafe\" (child process re-executes the test binary " + "from the beginning, running only the specific death test) or " + "\"fast\" (child process runs the death test immediately " + "after forking)."); + +GTEST_DEFINE_bool_( + death_test_use_fork, + internal::BoolFromGTestEnv("death_test_use_fork", false), + "Instructs to use fork()/_exit() instead of clone() in death tests. " + "Ignored and always uses fork() on POSIX systems where clone() is not " + "implemented. Useful when running under valgrind or similar tools if " + "those do not support clone(). Valgrind 3.3.1 will just fail if " + "it sees an unsupported combination of clone() flags. " + "It is not recommended to use this flag w/o valgrind though it will " + "work in 99% of the cases. Once valgrind is fixed, this flag will " + "most likely be removed."); + +namespace internal { +GTEST_DEFINE_string_( + internal_run_death_test, "", + "Indicates the file, line number, temporal index of " + "the single death test to run, and a file descriptor to " + "which a success code may be sent, all separated by " + "the '|' characters. This flag is specified if and only if the " + "current process is a sub-process launched for running a thread-safe " + "death test. FOR INTERNAL USE ONLY."); +} // namespace internal + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Valid only for fast death tests. Indicates the code is running in the +// child process of a fast style death test. +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +static bool g_in_fast_death_test_child = false; +# endif + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +bool InDeathTestChild() { +# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA + + // On Windows and Fuchsia, death tests are thread-safe regardless of the value + // of the death_test_style flag. + return !GTEST_FLAG(internal_run_death_test).empty(); + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") + return !GTEST_FLAG(internal_run_death_test).empty(); + else + return g_in_fast_death_test_child; +#endif +} + +} // namespace internal + +// ExitedWithCode constructor. +ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { +} + +// ExitedWithCode function-call operator. +bool ExitedWithCode::operator()(int exit_status) const { +# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA + + return exit_status == exit_code_; + +# else + + return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_; + +# endif // GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA +} + +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +// KilledBySignal constructor. +KilledBySignal::KilledBySignal(int signum) : signum_(signum) { +} + +// KilledBySignal function-call operator. +bool KilledBySignal::operator()(int exit_status) const { +# if defined(GTEST_KILLED_BY_SIGNAL_OVERRIDE_) + { + bool result; + if (GTEST_KILLED_BY_SIGNAL_OVERRIDE_(signum_, exit_status, &result)) { + return result; + } + } +# endif // defined(GTEST_KILLED_BY_SIGNAL_OVERRIDE_) + return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_; +} +# endif // !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA + +namespace internal { + +// Utilities needed for death tests. + +// Generates a textual description of a given exit code, in the format +// specified by wait(2). +static std::string ExitSummary(int exit_code) { + Message m; + +# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA + + m << "Exited with exit status " << exit_code; + +# else + + if (WIFEXITED(exit_code)) { + m << "Exited with exit status " << WEXITSTATUS(exit_code); + } else if (WIFSIGNALED(exit_code)) { + m << "Terminated by signal " << WTERMSIG(exit_code); + } +# ifdef WCOREDUMP + if (WCOREDUMP(exit_code)) { + m << " (core dumped)"; + } +# endif +# endif // GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA + + return m.GetString(); +} + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +bool ExitedUnsuccessfully(int exit_status) { + return !ExitedWithCode(0)(exit_status); +} + +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +// Generates a textual failure message when a death test finds more than +// one thread running, or cannot determine the number of threads, prior +// to executing the given statement. It is the responsibility of the +// caller not to pass a thread_count of 1. +static std::string DeathTestThreadWarning(size_t thread_count) { + Message msg; + msg << "Death tests use fork(), which is unsafe particularly" + << " in a threaded context. For this test, " << GTEST_NAME_ << " "; + if (thread_count == 0) { + msg << "couldn't detect the number of threads."; + } else { + msg << "detected " << thread_count << " threads."; + } + msg << " See " + "https://github.com/google/googletest/blob/master/docs/" + "advanced.md#death-tests-and-threads" + << " for more explanation and suggested solutions, especially if" + << " this is the last message you see before your test times out."; + return msg.GetString(); +} +# endif // !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA + +// Flag characters for reporting a death test that did not die. +static const char kDeathTestLived = 'L'; +static const char kDeathTestReturned = 'R'; +static const char kDeathTestThrew = 'T'; +static const char kDeathTestInternalError = 'I'; + +#if GTEST_OS_FUCHSIA + +// File descriptor used for the pipe in the child process. +static const int kFuchsiaReadPipeFd = 3; + +#endif + +// An enumeration describing all of the possible ways that a death test can +// conclude. DIED means that the process died while executing the test +// code; LIVED means that process lived beyond the end of the test code; +// RETURNED means that the test statement attempted to execute a return +// statement, which is not allowed; THREW means that the test statement +// returned control by throwing an exception. IN_PROGRESS means the test +// has not yet concluded. +enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; + +// Routine for aborting the program which is safe to call from an +// exec-style death test child process, in which case the error +// message is propagated back to the parent process. Otherwise, the +// message is simply printed to stderr. In either case, the program +// then exits with status 1. +static void DeathTestAbort(const std::string& message) { + // On a POSIX system, this function may be called from a threadsafe-style + // death test child process, which operates on a very small stack. Use + // the heap for any additional non-minuscule memory requirements. + const InternalRunDeathTestFlag* const flag = + GetUnitTestImpl()->internal_run_death_test_flag(); + if (flag != nullptr) { + FILE* parent = posix::FDOpen(flag->write_fd(), "w"); + fputc(kDeathTestInternalError, parent); + fprintf(parent, "%s", message.c_str()); + fflush(parent); + _exit(1); + } else { + fprintf(stderr, "%s", message.c_str()); + fflush(stderr); + posix::Abort(); + } +} + +// A replacement for CHECK that calls DeathTestAbort if the assertion +// fails. +# define GTEST_DEATH_TEST_CHECK_(expression) \ + do { \ + if (!::testing::internal::IsTrue(expression)) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// This macro is similar to GTEST_DEATH_TEST_CHECK_, but it is meant for +// evaluating any system call that fulfills two conditions: it must return +// -1 on failure, and set errno to EINTR when it is interrupted and +// should be tried again. The macro expands to a loop that repeatedly +// evaluates the expression as long as it evaluates to -1 and sets +// errno to EINTR. If the expression evaluates to -1 but errno is +// something other than EINTR, DeathTestAbort is called. +# define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \ + do { \ + int gtest_retval; \ + do { \ + gtest_retval = (expression); \ + } while (gtest_retval == -1 && errno == EINTR); \ + if (gtest_retval == -1) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression + " != -1"); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// Returns the message describing the last system error in errno. +std::string GetLastErrnoDescription() { + return errno == 0 ? "" : posix::StrError(errno); +} + +// This is called from a death test parent process to read a failure +// message from the death test child process and log it with the FATAL +// severity. On Windows, the message is read from a pipe handle. On other +// platforms, it is read from a file descriptor. +static void FailFromInternalError(int fd) { + Message error; + char buffer[256]; + int num_read; + + do { + while ((num_read = posix::Read(fd, buffer, 255)) > 0) { + buffer[num_read] = '\0'; + error << buffer; + } + } while (num_read == -1 && errno == EINTR); + + if (num_read == 0) { + GTEST_LOG_(FATAL) << error.GetString(); + } else { + const int last_error = errno; + GTEST_LOG_(FATAL) << "Error while reading death test internal: " + << GetLastErrnoDescription() << " [" << last_error << "]"; + } +} + +// Death test constructor. Increments the running death test count +// for the current test. +DeathTest::DeathTest() { + TestInfo* const info = GetUnitTestImpl()->current_test_info(); + if (info == nullptr) { + DeathTestAbort("Cannot run a death test outside of a TEST or " + "TEST_F construct"); + } +} + +// Creates and returns a death test by dispatching to the current +// death test factory. +bool DeathTest::Create(const char* statement, + Matcher<const std::string&> matcher, const char* file, + int line, DeathTest** test) { + return GetUnitTestImpl()->death_test_factory()->Create( + statement, std::move(matcher), file, line, test); +} + +const char* DeathTest::LastMessage() { + return last_death_test_message_.c_str(); +} + +void DeathTest::set_last_death_test_message(const std::string& message) { + last_death_test_message_ = message; +} + +std::string DeathTest::last_death_test_message_; + +// Provides cross platform implementation for some death functionality. +class DeathTestImpl : public DeathTest { + protected: + DeathTestImpl(const char* a_statement, Matcher<const std::string&> matcher) + : statement_(a_statement), + matcher_(std::move(matcher)), + spawned_(false), + status_(-1), + outcome_(IN_PROGRESS), + read_fd_(-1), + write_fd_(-1) {} + + // read_fd_ is expected to be closed and cleared by a derived class. + ~DeathTestImpl() override { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); } + + void Abort(AbortReason reason) override; + bool Passed(bool status_ok) override; + + const char* statement() const { return statement_; } + bool spawned() const { return spawned_; } + void set_spawned(bool is_spawned) { spawned_ = is_spawned; } + int status() const { return status_; } + void set_status(int a_status) { status_ = a_status; } + DeathTestOutcome outcome() const { return outcome_; } + void set_outcome(DeathTestOutcome an_outcome) { outcome_ = an_outcome; } + int read_fd() const { return read_fd_; } + void set_read_fd(int fd) { read_fd_ = fd; } + int write_fd() const { return write_fd_; } + void set_write_fd(int fd) { write_fd_ = fd; } + + // Called in the parent process only. Reads the result code of the death + // test child process via a pipe, interprets it to set the outcome_ + // member, and closes read_fd_. Outputs diagnostics and terminates in + // case of unexpected codes. + void ReadAndInterpretStatusByte(); + + // Returns stderr output from the child process. + virtual std::string GetErrorLogs(); + + private: + // The textual content of the code this object is testing. This class + // doesn't own this string and should not attempt to delete it. + const char* const statement_; + // A matcher that's expected to match the stderr output by the child process. + Matcher<const std::string&> matcher_; + // True if the death test child process has been successfully spawned. + bool spawned_; + // The exit status of the child process. + int status_; + // How the death test concluded. + DeathTestOutcome outcome_; + // Descriptor to the read end of the pipe to the child process. It is + // always -1 in the child process. The child keeps its write end of the + // pipe in write_fd_. + int read_fd_; + // Descriptor to the child's write end of the pipe to the parent process. + // It is always -1 in the parent process. The parent keeps its end of the + // pipe in read_fd_. + int write_fd_; +}; + +// Called in the parent process only. Reads the result code of the death +// test child process via a pipe, interprets it to set the outcome_ +// member, and closes read_fd_. Outputs diagnostics and terminates in +// case of unexpected codes. +void DeathTestImpl::ReadAndInterpretStatusByte() { + char flag; + int bytes_read; + + // The read() here blocks until data is available (signifying the + // failure of the death test) or until the pipe is closed (signifying + // its success), so it's okay to call this in the parent before + // the child process has exited. + do { + bytes_read = posix::Read(read_fd(), &flag, 1); + } while (bytes_read == -1 && errno == EINTR); + + if (bytes_read == 0) { + set_outcome(DIED); + } else if (bytes_read == 1) { + switch (flag) { + case kDeathTestReturned: + set_outcome(RETURNED); + break; + case kDeathTestThrew: + set_outcome(THREW); + break; + case kDeathTestLived: + set_outcome(LIVED); + break; + case kDeathTestInternalError: + FailFromInternalError(read_fd()); // Does not return. + break; + default: + GTEST_LOG_(FATAL) << "Death test child process reported " + << "unexpected status byte (" + << static_cast<unsigned int>(flag) << ")"; + } + } else { + GTEST_LOG_(FATAL) << "Read from death test child process failed: " + << GetLastErrnoDescription(); + } + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(read_fd())); + set_read_fd(-1); +} + +std::string DeathTestImpl::GetErrorLogs() { + return GetCapturedStderr(); +} + +// Signals that the death test code which should have exited, didn't. +// Should be called only in a death test child process. +// Writes a status byte to the child's status file descriptor, then +// calls _exit(1). +void DeathTestImpl::Abort(AbortReason reason) { + // The parent process considers the death test to be a failure if + // it finds any data in our pipe. So, here we write a single flag byte + // to the pipe, then exit. + const char status_ch = + reason == TEST_DID_NOT_DIE ? kDeathTestLived : + reason == TEST_THREW_EXCEPTION ? kDeathTestThrew : kDeathTestReturned; + + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1)); + // We are leaking the descriptor here because on some platforms (i.e., + // when built as Windows DLL), destructors of global objects will still + // run after calling _exit(). On such systems, write_fd_ will be + // indirectly closed from the destructor of UnitTestImpl, causing double + // close if it is also closed here. On debug configurations, double close + // may assert. As there are no in-process buffers to flush here, we are + // relying on the OS to close the descriptor after the process terminates + // when the destructors are not run. + _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) +} + +// Returns an indented copy of stderr output for a death test. +// This makes distinguishing death test output lines from regular log lines +// much easier. +static ::std::string FormatDeathTestOutput(const ::std::string& output) { + ::std::string ret; + for (size_t at = 0; ; ) { + const size_t line_end = output.find('\n', at); + ret += "[ DEATH ] "; + if (line_end == ::std::string::npos) { + ret += output.substr(at); + break; + } + ret += output.substr(at, line_end + 1 - at); + at = line_end + 1; + } + return ret; +} + +// Assesses the success or failure of a death test, using both private +// members which have previously been set, and one argument: +// +// Private data members: +// outcome: An enumeration describing how the death test +// concluded: DIED, LIVED, THREW, or RETURNED. The death test +// fails in the latter three cases. +// status: The exit status of the child process. On *nix, it is in the +// in the format specified by wait(2). On Windows, this is the +// value supplied to the ExitProcess() API or a numeric code +// of the exception that terminated the program. +// matcher_: A matcher that's expected to match the stderr output by the child +// process. +// +// Argument: +// status_ok: true if exit_status is acceptable in the context of +// this particular death test, which fails if it is false +// +// Returns true if and only if all of the above conditions are met. Otherwise, +// the first failing condition, in the order given above, is the one that is +// reported. Also sets the last death test message string. +bool DeathTestImpl::Passed(bool status_ok) { + if (!spawned()) + return false; + + const std::string error_message = GetErrorLogs(); + + bool success = false; + Message buffer; + + buffer << "Death test: " << statement() << "\n"; + switch (outcome()) { + case LIVED: + buffer << " Result: failed to die.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case THREW: + buffer << " Result: threw an exception.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case RETURNED: + buffer << " Result: illegal return in test statement.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case DIED: + if (status_ok) { + if (matcher_.Matches(error_message)) { + success = true; + } else { + std::ostringstream stream; + matcher_.DescribeTo(&stream); + buffer << " Result: died but not with expected error.\n" + << " Expected: " << stream.str() << "\n" + << "Actual msg:\n" + << FormatDeathTestOutput(error_message); + } + } else { + buffer << " Result: died but not with expected exit code:\n" + << " " << ExitSummary(status()) << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + break; + case IN_PROGRESS: + default: + GTEST_LOG_(FATAL) + << "DeathTest::Passed somehow called before conclusion of test"; + } + + DeathTest::set_last_death_test_message(buffer.GetString()); + return success; +} + +# if GTEST_OS_WINDOWS +// WindowsDeathTest implements death tests on Windows. Due to the +// specifics of starting new processes on Windows, death tests there are +// always threadsafe, and Google Test considers the +// --gtest_death_test_style=fast setting to be equivalent to +// --gtest_death_test_style=threadsafe there. +// +// A few implementation notes: Like the Linux version, the Windows +// implementation uses pipes for child-to-parent communication. But due to +// the specifics of pipes on Windows, some extra steps are required: +// +// 1. The parent creates a communication pipe and stores handles to both +// ends of it. +// 2. The parent starts the child and provides it with the information +// necessary to acquire the handle to the write end of the pipe. +// 3. The child acquires the write end of the pipe and signals the parent +// using a Windows event. +// 4. Now the parent can release the write end of the pipe on its side. If +// this is done before step 3, the object's reference count goes down to +// 0 and it is destroyed, preventing the child from acquiring it. The +// parent now has to release it, or read operations on the read end of +// the pipe will not return when the child terminates. +// 5. The parent reads child's output through the pipe (outcome code and +// any possible error messages) from the pipe, and its stderr and then +// determines whether to fail the test. +// +// Note: to distinguish Win32 API calls from the local method and function +// calls, the former are explicitly resolved in the global namespace. +// +class WindowsDeathTest : public DeathTestImpl { + public: + WindowsDeathTest(const char* a_statement, Matcher<const std::string&> matcher, + const char* file, int line) + : DeathTestImpl(a_statement, std::move(matcher)), + file_(file), + line_(line) {} + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + virtual TestRole AssumeRole(); + + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; + // Handle to the write end of the pipe to the child process. + AutoHandle write_handle_; + // Child process handle. + AutoHandle child_handle_; + // Event the child process uses to signal the parent that it has + // acquired the handle to the write end of the pipe. After seeing this + // event the parent can release its own handles to make sure its + // ReadFile() calls return when the child terminates. + AutoHandle event_handle_; +}; + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int WindowsDeathTest::Wait() { + if (!spawned()) + return 0; + + // Wait until the child either signals that it has acquired the write end + // of the pipe or it dies. + const HANDLE wait_handles[2] = { child_handle_.Get(), event_handle_.Get() }; + switch (::WaitForMultipleObjects(2, + wait_handles, + FALSE, // Waits for any of the handles. + INFINITE)) { + case WAIT_OBJECT_0: + case WAIT_OBJECT_0 + 1: + break; + default: + GTEST_DEATH_TEST_CHECK_(false); // Should not get here. + } + + // The child has acquired the write end of the pipe or exited. + // We release the handle on our side and continue. + write_handle_.Reset(); + event_handle_.Reset(); + + ReadAndInterpretStatusByte(); + + // Waits for the child process to exit if it haven't already. This + // returns immediately if the child has already exited, regardless of + // whether previous calls to WaitForMultipleObjects synchronized on this + // handle or not. + GTEST_DEATH_TEST_CHECK_( + WAIT_OBJECT_0 == ::WaitForSingleObject(child_handle_.Get(), + INFINITE)); + DWORD status_code; + GTEST_DEATH_TEST_CHECK_( + ::GetExitCodeProcess(child_handle_.Get(), &status_code) != FALSE); + child_handle_.Reset(); + set_status(static_cast<int>(status_code)); + return status(); +} + +// The AssumeRole process for a Windows death test. It creates a child +// process with the same executable as the current process to run the +// death test. The child process is given the --gtest_filter and +// --gtest_internal_run_death_test flags such that it knows to run the +// current death test only. +DeathTest::TestRole WindowsDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != nullptr) { + // ParseInternalRunDeathTestFlag() has performed all the necessary + // processing. + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + // WindowsDeathTest uses an anonymous pipe to communicate results of + // a death test. + SECURITY_ATTRIBUTES handles_are_inheritable = {sizeof(SECURITY_ATTRIBUTES), + nullptr, TRUE}; + HANDLE read_handle, write_handle; + GTEST_DEATH_TEST_CHECK_( + ::CreatePipe(&read_handle, &write_handle, &handles_are_inheritable, + 0) // Default buffer size. + != FALSE); + set_read_fd(::_open_osfhandle(reinterpret_cast<intptr_t>(read_handle), + O_RDONLY)); + write_handle_.Reset(write_handle); + event_handle_.Reset(::CreateEvent( + &handles_are_inheritable, + TRUE, // The event will automatically reset to non-signaled state. + FALSE, // The initial state is non-signalled. + nullptr)); // The even is unnamed. + GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != nullptr); + const std::string filter_flag = std::string("--") + GTEST_FLAG_PREFIX_ + + kFilterFlag + "=" + info->test_suite_name() + + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + + "=" + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(static_cast<unsigned int>(::GetCurrentProcessId())) + + // size_t has the same width as pointers on both 32-bit and 64-bit + // Windows platforms. + // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx. + "|" + StreamableToString(reinterpret_cast<size_t>(write_handle)) + + "|" + StreamableToString(reinterpret_cast<size_t>(event_handle_.Get())); + + char executable_path[_MAX_PATH + 1]; // NOLINT + GTEST_DEATH_TEST_CHECK_(_MAX_PATH + 1 != ::GetModuleFileNameA(nullptr, + executable_path, + _MAX_PATH)); + + std::string command_line = + std::string(::GetCommandLineA()) + " " + filter_flag + " \"" + + internal_flag + "\""; + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // Flush the log buffers since the log streams are shared with the child. + FlushInfoLog(); + + // The child process will share the standard handles with the parent. + STARTUPINFOA startup_info; + memset(&startup_info, 0, sizeof(STARTUPINFO)); + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE); + startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); + + PROCESS_INFORMATION process_info; + GTEST_DEATH_TEST_CHECK_( + ::CreateProcessA( + executable_path, const_cast<char*>(command_line.c_str()), + nullptr, // Retuned process handle is not inheritable. + nullptr, // Retuned thread handle is not inheritable. + TRUE, // Child inherits all inheritable handles (for write_handle_). + 0x0, // Default creation flags. + nullptr, // Inherit the parent's environment. + UnitTest::GetInstance()->original_working_dir(), &startup_info, + &process_info) != FALSE); + child_handle_.Reset(process_info.hProcess); + ::CloseHandle(process_info.hThread); + set_spawned(true); + return OVERSEE_TEST; +} + +# elif GTEST_OS_FUCHSIA + +class FuchsiaDeathTest : public DeathTestImpl { + public: + FuchsiaDeathTest(const char* a_statement, Matcher<const std::string&> matcher, + const char* file, int line) + : DeathTestImpl(a_statement, std::move(matcher)), + file_(file), + line_(line) {} + + // All of these virtual functions are inherited from DeathTest. + int Wait() override; + TestRole AssumeRole() override; + std::string GetErrorLogs() override; + + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; + // The stderr data captured by the child process. + std::string captured_stderr_; + + zx::process child_process_; + zx::channel exception_channel_; + zx::socket stderr_socket_; +}; + +// Utility class for accumulating command-line arguments. +class Arguments { + public: + Arguments() { args_.push_back(nullptr); } + + ~Arguments() { + for (std::vector<char*>::iterator i = args_.begin(); i != args_.end(); + ++i) { + free(*i); + } + } + void AddArgument(const char* argument) { + args_.insert(args_.end() - 1, posix::StrDup(argument)); + } + + template <typename Str> + void AddArguments(const ::std::vector<Str>& arguments) { + for (typename ::std::vector<Str>::const_iterator i = arguments.begin(); + i != arguments.end(); + ++i) { + args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); + } + } + char* const* Argv() { + return &args_[0]; + } + + int size() { + return static_cast<int>(args_.size()) - 1; + } + + private: + std::vector<char*> args_; +}; + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int FuchsiaDeathTest::Wait() { + const int kProcessKey = 0; + const int kSocketKey = 1; + const int kExceptionKey = 2; + + if (!spawned()) + return 0; + + // Create a port to wait for socket/task/exception events. + zx_status_t status_zx; + zx::port port; + status_zx = zx::port::create(0, &port); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + // Register to wait for the child process to terminate. + status_zx = child_process_.wait_async( + port, kProcessKey, ZX_PROCESS_TERMINATED, 0); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + // Register to wait for the socket to be readable or closed. + status_zx = stderr_socket_.wait_async( + port, kSocketKey, ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED, 0); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + // Register to wait for an exception. + status_zx = exception_channel_.wait_async( + port, kExceptionKey, ZX_CHANNEL_READABLE, 0); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + bool process_terminated = false; + bool socket_closed = false; + do { + zx_port_packet_t packet = {}; + status_zx = port.wait(zx::time::infinite(), &packet); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + if (packet.key == kExceptionKey) { + // Process encountered an exception. Kill it directly rather than + // letting other handlers process the event. We will get a kProcessKey + // event when the process actually terminates. + status_zx = child_process_.kill(); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + } else if (packet.key == kProcessKey) { + // Process terminated. + GTEST_DEATH_TEST_CHECK_(ZX_PKT_IS_SIGNAL_ONE(packet.type)); + GTEST_DEATH_TEST_CHECK_(packet.signal.observed & ZX_PROCESS_TERMINATED); + process_terminated = true; + } else if (packet.key == kSocketKey) { + GTEST_DEATH_TEST_CHECK_(ZX_PKT_IS_SIGNAL_ONE(packet.type)); + if (packet.signal.observed & ZX_SOCKET_READABLE) { + // Read data from the socket. + constexpr size_t kBufferSize = 1024; + do { + size_t old_length = captured_stderr_.length(); + size_t bytes_read = 0; + captured_stderr_.resize(old_length + kBufferSize); + status_zx = stderr_socket_.read( + 0, &captured_stderr_.front() + old_length, kBufferSize, + &bytes_read); + captured_stderr_.resize(old_length + bytes_read); + } while (status_zx == ZX_OK); + if (status_zx == ZX_ERR_PEER_CLOSED) { + socket_closed = true; + } else { + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_ERR_SHOULD_WAIT); + status_zx = stderr_socket_.wait_async( + port, kSocketKey, ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED, 0); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + } + } else { + GTEST_DEATH_TEST_CHECK_(packet.signal.observed & ZX_SOCKET_PEER_CLOSED); + socket_closed = true; + } + } + } while (!process_terminated && !socket_closed); + + ReadAndInterpretStatusByte(); + + zx_info_process_v2_t buffer; + status_zx = child_process_.get_info( + ZX_INFO_PROCESS_V2, &buffer, sizeof(buffer), nullptr, nullptr); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + GTEST_DEATH_TEST_CHECK_(buffer.flags & ZX_INFO_PROCESS_FLAG_EXITED); + set_status(static_cast<int>(buffer.return_code)); + return status(); +} + +// The AssumeRole process for a Fuchsia death test. It creates a child +// process with the same executable as the current process to run the +// death test. The child process is given the --gtest_filter and +// --gtest_internal_run_death_test flags such that it knows to run the +// current death test only. +DeathTest::TestRole FuchsiaDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != nullptr) { + // ParseInternalRunDeathTestFlag() has performed all the necessary + // processing. + set_write_fd(kFuchsiaReadPipeFd); + return EXECUTE_TEST; + } + + // Flush the log buffers since the log streams are shared with the child. + FlushInfoLog(); + + // Build the child process command line. + const std::string filter_flag = std::string("--") + GTEST_FLAG_PREFIX_ + + kFilterFlag + "=" + info->test_suite_name() + + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "=" + + file_ + "|" + + StreamableToString(line_) + "|" + + StreamableToString(death_test_index); + Arguments args; + args.AddArguments(GetInjectableArgvs()); + args.AddArgument(filter_flag.c_str()); + args.AddArgument(internal_flag.c_str()); + + // Build the pipe for communication with the child. + zx_status_t status; + zx_handle_t child_pipe_handle; + int child_pipe_fd; + status = fdio_pipe_half(&child_pipe_fd, &child_pipe_handle); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + set_read_fd(child_pipe_fd); + + // Set the pipe handle for the child. + fdio_spawn_action_t spawn_actions[2] = {}; + fdio_spawn_action_t* add_handle_action = &spawn_actions[0]; + add_handle_action->action = FDIO_SPAWN_ACTION_ADD_HANDLE; + add_handle_action->h.id = PA_HND(PA_FD, kFuchsiaReadPipeFd); + add_handle_action->h.handle = child_pipe_handle; + + // Create a socket pair will be used to receive the child process' stderr. + zx::socket stderr_producer_socket; + status = + zx::socket::create(0, &stderr_producer_socket, &stderr_socket_); + GTEST_DEATH_TEST_CHECK_(status >= 0); + int stderr_producer_fd = -1; + status = + fdio_fd_create(stderr_producer_socket.release(), &stderr_producer_fd); + GTEST_DEATH_TEST_CHECK_(status >= 0); + + // Make the stderr socket nonblocking. + GTEST_DEATH_TEST_CHECK_(fcntl(stderr_producer_fd, F_SETFL, 0) == 0); + + fdio_spawn_action_t* add_stderr_action = &spawn_actions[1]; + add_stderr_action->action = FDIO_SPAWN_ACTION_CLONE_FD; + add_stderr_action->fd.local_fd = stderr_producer_fd; + add_stderr_action->fd.target_fd = STDERR_FILENO; + + // Create a child job. + zx_handle_t child_job = ZX_HANDLE_INVALID; + status = zx_job_create(zx_job_default(), 0, & child_job); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + zx_policy_basic_t policy; + policy.condition = ZX_POL_NEW_ANY; + policy.policy = ZX_POL_ACTION_ALLOW; + status = zx_job_set_policy( + child_job, ZX_JOB_POL_RELATIVE, ZX_JOB_POL_BASIC, &policy, 1); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + + // Create an exception channel attached to the |child_job|, to allow + // us to suppress the system default exception handler from firing. + status = + zx_task_create_exception_channel( + child_job, 0, exception_channel_.reset_and_get_address()); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + + // Spawn the child process. + status = fdio_spawn_etc( + child_job, FDIO_SPAWN_CLONE_ALL, args.Argv()[0], args.Argv(), nullptr, + 2, spawn_actions, child_process_.reset_and_get_address(), nullptr); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + + set_spawned(true); + return OVERSEE_TEST; +} + +std::string FuchsiaDeathTest::GetErrorLogs() { + return captured_stderr_; +} + +#else // We are neither on Windows, nor on Fuchsia. + +// ForkingDeathTest provides implementations for most of the abstract +// methods of the DeathTest interface. Only the AssumeRole method is +// left undefined. +class ForkingDeathTest : public DeathTestImpl { + public: + ForkingDeathTest(const char* statement, Matcher<const std::string&> matcher); + + // All of these virtual functions are inherited from DeathTest. + int Wait() override; + + protected: + void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; } + + private: + // PID of child process during death test; 0 in the child process itself. + pid_t child_pid_; +}; + +// Constructs a ForkingDeathTest. +ForkingDeathTest::ForkingDeathTest(const char* a_statement, + Matcher<const std::string&> matcher) + : DeathTestImpl(a_statement, std::move(matcher)), child_pid_(-1) {} + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int ForkingDeathTest::Wait() { + if (!spawned()) + return 0; + + ReadAndInterpretStatusByte(); + + int status_value; + GTEST_DEATH_TEST_CHECK_SYSCALL_(waitpid(child_pid_, &status_value, 0)); + set_status(status_value); + return status_value; +} + +// A concrete death test class that forks, then immediately runs the test +// in the child process. +class NoExecDeathTest : public ForkingDeathTest { + public: + NoExecDeathTest(const char* a_statement, Matcher<const std::string&> matcher) + : ForkingDeathTest(a_statement, std::move(matcher)) {} + TestRole AssumeRole() override; +}; + +// The AssumeRole process for a fork-and-run death test. It implements a +// straightforward fork, with a simple pipe to transmit the status byte. +DeathTest::TestRole NoExecDeathTest::AssumeRole() { + const size_t thread_count = GetThreadCount(); + if (thread_count != 1) { + GTEST_LOG_(WARNING) << DeathTestThreadWarning(thread_count); + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + + DeathTest::set_last_death_test_message(""); + CaptureStderr(); + // When we fork the process below, the log file buffers are copied, but the + // file descriptors are shared. We flush all log files here so that closing + // the file descriptors in the child process doesn't throw off the + // synchronization between descriptors and buffers in the parent process. + // This is as close to the fork as possible to avoid a race condition in case + // there are multiple threads running before the death test, and another + // thread writes to the log file. + FlushInfoLog(); + + const pid_t child_pid = fork(); + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + set_child_pid(child_pid); + if (child_pid == 0) { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[0])); + set_write_fd(pipe_fd[1]); + // Redirects all logging to stderr in the child process to prevent + // concurrent writes to the log files. We capture stderr in the parent + // process and append the child process' output to a log. + LogToStderr(); + // Event forwarding to the listeners of event listener API mush be shut + // down in death test subprocesses. + GetUnitTestImpl()->listeners()->SuppressEventForwarding(); + g_in_fast_death_test_child = true; + return EXECUTE_TEST; + } else { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; + } +} + +// A concrete death test class that forks and re-executes the main +// program from the beginning, with command-line flags set that cause +// only this specific death test to be run. +class ExecDeathTest : public ForkingDeathTest { + public: + ExecDeathTest(const char* a_statement, Matcher<const std::string&> matcher, + const char* file, int line) + : ForkingDeathTest(a_statement, std::move(matcher)), + file_(file), + line_(line) {} + TestRole AssumeRole() override; + + private: + static ::std::vector<std::string> GetArgvsForDeathTestChildProcess() { + ::std::vector<std::string> args = GetInjectableArgvs(); +# if defined(GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_) + ::std::vector<std::string> extra_args = + GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_(); + args.insert(args.end(), extra_args.begin(), extra_args.end()); +# endif // defined(GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_) + return args; + } + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; +}; + +// Utility class for accumulating command-line arguments. +class Arguments { + public: + Arguments() { args_.push_back(nullptr); } + + ~Arguments() { + for (std::vector<char*>::iterator i = args_.begin(); i != args_.end(); + ++i) { + free(*i); + } + } + void AddArgument(const char* argument) { + args_.insert(args_.end() - 1, posix::StrDup(argument)); + } + + template <typename Str> + void AddArguments(const ::std::vector<Str>& arguments) { + for (typename ::std::vector<Str>::const_iterator i = arguments.begin(); + i != arguments.end(); + ++i) { + args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); + } + } + char* const* Argv() { + return &args_[0]; + } + + private: + std::vector<char*> args_; +}; + +// A struct that encompasses the arguments to the child process of a +// threadsafe-style death test process. +struct ExecDeathTestArgs { + char* const* argv; // Command-line arguments for the child's call to exec + int close_fd; // File descriptor to close; the read end of a pipe +}; + +# if GTEST_OS_QNX +extern "C" char** environ; +# else // GTEST_OS_QNX +// The main function for a threadsafe-style death test child process. +// This function is called in a clone()-ed process and thus must avoid +// any potentially unsafe operations like malloc or libc functions. +static int ExecDeathTestChildMain(void* child_arg) { + ExecDeathTestArgs* const args = static_cast<ExecDeathTestArgs*>(child_arg); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd)); + + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + // We can safely call execv() as it's almost a direct system call. We + // cannot use execvp() as it's a libc function and thus potentially + // unsafe. Since execv() doesn't search the PATH, the user must + // invoke the test program via a valid path that contains at least + // one path separator. + execv(args->argv[0], args->argv); + DeathTestAbort(std::string("execv(") + args->argv[0] + ", ...) in " + + original_dir + " failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; +} +# endif // GTEST_OS_QNX + +# if GTEST_HAS_CLONE +// Two utility routines that together determine the direction the stack +// grows. +// This could be accomplished more elegantly by a single recursive +// function, but we want to guard against the unlikely possibility of +// a smart compiler optimizing the recursion away. +// +// GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining +// StackLowerThanAddress into StackGrowsDown, which then doesn't give +// correct answer. +static void StackLowerThanAddress(const void* ptr, + bool* result) GTEST_NO_INLINE_; +// Make sure sanitizers do not tamper with the stack here. +// Ideally, we want to use `__builtin_frame_address` instead of a local variable +// address with sanitizer disabled, but it does not work when the +// compiler optimizes the stack frame out, which happens on PowerPC targets. +// HWAddressSanitizer add a random tag to the MSB of the local variable address, +// making comparison result unpredictable. +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +static void StackLowerThanAddress(const void* ptr, bool* result) { + int dummy = 0; + *result = std::less<const void*>()(&dummy, ptr); +} + +// Make sure AddressSanitizer does not tamper with the stack here. +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +static bool StackGrowsDown() { + int dummy = 0; + bool result; + StackLowerThanAddress(&dummy, &result); + return result; +} +# endif // GTEST_HAS_CLONE + +// Spawns a child process with the same executable as the current process in +// a thread-safe manner and instructs it to run the death test. The +// implementation uses fork(2) + exec. On systems where clone(2) is +// available, it is used instead, being slightly more thread-safe. On QNX, +// fork supports only single-threaded environments, so this function uses +// spawn(2) there instead. The function dies with an error message if +// anything goes wrong. +static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) { + ExecDeathTestArgs args = { argv, close_fd }; + pid_t child_pid = -1; + +# if GTEST_OS_QNX + // Obtains the current directory and sets it to be closed in the child + // process. + const int cwd_fd = open(".", O_RDONLY); + GTEST_DEATH_TEST_CHECK_(cwd_fd != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(cwd_fd, F_SETFD, FD_CLOEXEC)); + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + int fd_flags; + // Set close_fd to be closed after spawn. + GTEST_DEATH_TEST_CHECK_SYSCALL_(fd_flags = fcntl(close_fd, F_GETFD)); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(close_fd, F_SETFD, + fd_flags | FD_CLOEXEC)); + struct inheritance inherit = {0}; + // spawn is a system call. + child_pid = spawn(args.argv[0], 0, nullptr, &inherit, args.argv, environ); + // Restores the current working directory. + GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd)); + +# else // GTEST_OS_QNX +# if GTEST_OS_LINUX + // When a SIGPROF signal is received while fork() or clone() are executing, + // the process may hang. To avoid this, we ignore SIGPROF here and re-enable + // it after the call to fork()/clone() is complete. + struct sigaction saved_sigprof_action; + struct sigaction ignore_sigprof_action; + memset(&ignore_sigprof_action, 0, sizeof(ignore_sigprof_action)); + sigemptyset(&ignore_sigprof_action.sa_mask); + ignore_sigprof_action.sa_handler = SIG_IGN; + GTEST_DEATH_TEST_CHECK_SYSCALL_(sigaction( + SIGPROF, &ignore_sigprof_action, &saved_sigprof_action)); +# endif // GTEST_OS_LINUX + +# if GTEST_HAS_CLONE + const bool use_fork = GTEST_FLAG(death_test_use_fork); + + if (!use_fork) { + static const bool stack_grows_down = StackGrowsDown(); + const auto stack_size = static_cast<size_t>(getpagesize() * 2); + // MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead. + void* const stack = mmap(nullptr, stack_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED); + + // Maximum stack alignment in bytes: For a downward-growing stack, this + // amount is subtracted from size of the stack space to get an address + // that is within the stack space and is aligned on all systems we care + // about. As far as I know there is no ABI with stack alignment greater + // than 64. We assume stack and stack_size already have alignment of + // kMaxStackAlignment. + const size_t kMaxStackAlignment = 64; + void* const stack_top = + static_cast<char*>(stack) + + (stack_grows_down ? stack_size - kMaxStackAlignment : 0); + GTEST_DEATH_TEST_CHECK_( + static_cast<size_t>(stack_size) > kMaxStackAlignment && + reinterpret_cast<uintptr_t>(stack_top) % kMaxStackAlignment == 0); + + child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args); + + GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1); + } +# else + const bool use_fork = true; +# endif // GTEST_HAS_CLONE + + if (use_fork && (child_pid = fork()) == 0) { + ExecDeathTestChildMain(&args); + _exit(0); + } +# endif // GTEST_OS_QNX +# if GTEST_OS_LINUX + GTEST_DEATH_TEST_CHECK_SYSCALL_( + sigaction(SIGPROF, &saved_sigprof_action, nullptr)); +# endif // GTEST_OS_LINUX + + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + return child_pid; +} + +// The AssumeRole process for a fork-and-exec death test. It re-executes the +// main program from the beginning, setting the --gtest_filter +// and --gtest_internal_run_death_test flags to cause only the current +// death test to be re-run. +DeathTest::TestRole ExecDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != nullptr) { + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + // Clear the close-on-exec flag on the write end of the pipe, lest + // it be closed when the child process does an exec: + GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1); + + const std::string filter_flag = std::string("--") + GTEST_FLAG_PREFIX_ + + kFilterFlag + "=" + info->test_suite_name() + + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "=" + + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(pipe_fd[1]); + Arguments args; + args.AddArguments(GetArgvsForDeathTestChildProcess()); + args.AddArgument(filter_flag.c_str()); + args.AddArgument(internal_flag.c_str()); + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // See the comment in NoExecDeathTest::AssumeRole for why the next line + // is necessary. + FlushInfoLog(); + + const pid_t child_pid = ExecDeathTestSpawnChild(args.Argv(), pipe_fd[0]); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_child_pid(child_pid); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; +} + +# endif // !GTEST_OS_WINDOWS + +// Creates a concrete DeathTest-derived class that depends on the +// --gtest_death_test_style flag, and sets the pointer pointed to +// by the "test" argument to its address. If the test should be +// skipped, sets that pointer to NULL. Returns true, unless the +// flag is set to an invalid value. +bool DefaultDeathTestFactory::Create(const char* statement, + Matcher<const std::string&> matcher, + const char* file, int line, + DeathTest** test) { + UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const int death_test_index = impl->current_test_info() + ->increment_death_test_count(); + + if (flag != nullptr) { + if (death_test_index > flag->index()) { + DeathTest::set_last_death_test_message( + "Death test count (" + StreamableToString(death_test_index) + + ") somehow exceeded expected maximum (" + + StreamableToString(flag->index()) + ")"); + return false; + } + + if (!(flag->file() == file && flag->line() == line && + flag->index() == death_test_index)) { + *test = nullptr; + return true; + } + } + +# if GTEST_OS_WINDOWS + + if (GTEST_FLAG(death_test_style) == "threadsafe" || + GTEST_FLAG(death_test_style) == "fast") { + *test = new WindowsDeathTest(statement, std::move(matcher), file, line); + } + +# elif GTEST_OS_FUCHSIA + + if (GTEST_FLAG(death_test_style) == "threadsafe" || + GTEST_FLAG(death_test_style) == "fast") { + *test = new FuchsiaDeathTest(statement, std::move(matcher), file, line); + } + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") { + *test = new ExecDeathTest(statement, std::move(matcher), file, line); + } else if (GTEST_FLAG(death_test_style) == "fast") { + *test = new NoExecDeathTest(statement, std::move(matcher)); + } + +# endif // GTEST_OS_WINDOWS + + else { // NOLINT - this is more readable than unbalanced brackets inside #if. + DeathTest::set_last_death_test_message( + "Unknown death test style \"" + GTEST_FLAG(death_test_style) + + "\" encountered"); + return false; + } + + return true; +} + +# if GTEST_OS_WINDOWS +// Recreates the pipe and event handles from the provided parameters, +// signals the event, and returns a file descriptor wrapped around the pipe +// handle. This function is called in the child process only. +static int GetStatusFileDescriptor(unsigned int parent_process_id, + size_t write_handle_as_size_t, + size_t event_handle_as_size_t) { + AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE, + FALSE, // Non-inheritable. + parent_process_id)); + if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) { + DeathTestAbort("Unable to open parent process " + + StreamableToString(parent_process_id)); + } + + GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t)); + + const HANDLE write_handle = + reinterpret_cast<HANDLE>(write_handle_as_size_t); + HANDLE dup_write_handle; + + // The newly initialized handle is accessible only in the parent + // process. To obtain one accessible within the child, we need to use + // DuplicateHandle. + if (!::DuplicateHandle(parent_process_handle.Get(), write_handle, + ::GetCurrentProcess(), &dup_write_handle, + 0x0, // Requested privileges ignored since + // DUPLICATE_SAME_ACCESS is used. + FALSE, // Request non-inheritable handler. + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the pipe handle " + + StreamableToString(write_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const HANDLE event_handle = reinterpret_cast<HANDLE>(event_handle_as_size_t); + HANDLE dup_event_handle; + + if (!::DuplicateHandle(parent_process_handle.Get(), event_handle, + ::GetCurrentProcess(), &dup_event_handle, + 0x0, + FALSE, + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the event handle " + + StreamableToString(event_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const int write_fd = + ::_open_osfhandle(reinterpret_cast<intptr_t>(dup_write_handle), O_APPEND); + if (write_fd == -1) { + DeathTestAbort("Unable to convert pipe handle " + + StreamableToString(write_handle_as_size_t) + + " to a file descriptor"); + } + + // Signals the parent that the write end of the pipe has been acquired + // so the parent can release its own write end. + ::SetEvent(dup_event_handle); + + return write_fd; +} +# endif // GTEST_OS_WINDOWS + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { + if (GTEST_FLAG(internal_run_death_test) == "") return nullptr; + + // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we + // can use it here. + int line = -1; + int index = -1; + ::std::vector< ::std::string> fields; + SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields); + int write_fd = -1; + +# if GTEST_OS_WINDOWS + + unsigned int parent_process_id = 0; + size_t write_handle_as_size_t = 0; + size_t event_handle_as_size_t = 0; + + if (fields.size() != 6 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &parent_process_id) + || !ParseNaturalNumber(fields[4], &write_handle_as_size_t) + || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + write_fd = GetStatusFileDescriptor(parent_process_id, + write_handle_as_size_t, + event_handle_as_size_t); + +# elif GTEST_OS_FUCHSIA + + if (fields.size() != 3 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + +# else + + if (fields.size() != 4 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &write_fd)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + +# endif // GTEST_OS_WINDOWS + + return new InternalRunDeathTestFlag(fields[0], line, index, write_fd); +} + +} // namespace internal + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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 <stdlib.h> + +#if GTEST_OS_WINDOWS_MOBILE +# include <windows.h> +#elif GTEST_OS_WINDOWS +# include <direct.h> +# include <io.h> +#else +# include <limits.h> +# include <climits> // Some Linux distributions define PATH_MAX here. +#endif // GTEST_OS_WINDOWS_MOBILE + + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_MAX_ _MAX_PATH +#elif defined(PATH_MAX) +# define GTEST_PATH_MAX_ PATH_MAX +#elif defined(_XOPEN_PATH_MAX) +# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX +#else +# define GTEST_PATH_MAX_ _POSIX_PATH_MAX +#endif // GTEST_OS_WINDOWS + +namespace testing { +namespace internal { + +#if GTEST_OS_WINDOWS +// On Windows, '\\' is the standard path separator, but many tools and the +// Windows API also accept '/' as an alternate path separator. Unless otherwise +// noted, a file path can contain either kind of path separators, or a mixture +// of them. +const char kPathSeparator = '\\'; +const char kAlternatePathSeparator = '/'; +const char kAlternatePathSeparatorString[] = "/"; +# if GTEST_OS_WINDOWS_MOBILE +// Windows CE doesn't have a current directory. You should not use +// the current directory in tests on Windows CE, but this at least +// provides a reasonable fallback. +const char kCurrentDirectoryString[] = "\\"; +// Windows CE doesn't define INVALID_FILE_ATTRIBUTES +const DWORD kInvalidFileAttributes = 0xffffffff; +# else +const char kCurrentDirectoryString[] = ".\\"; +# endif // GTEST_OS_WINDOWS_MOBILE +#else +const char kPathSeparator = '/'; +const char kCurrentDirectoryString[] = "./"; +#endif // GTEST_OS_WINDOWS + +// Returns whether the given character is a valid path separator. +static bool IsPathSeparator(char c) { +#if GTEST_HAS_ALT_PATH_SEP_ + return (c == kPathSeparator) || (c == kAlternatePathSeparator); +#else + return c == kPathSeparator; +#endif +} + +// Returns the current working directory, or "" if unsuccessful. +FilePath FilePath::GetCurrentDir() { +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \ + GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_ESP32 || \ + GTEST_OS_XTENSA + // These platforms do not have a current directory, so we just return + // something reasonable. + return FilePath(kCurrentDirectoryString); +#elif GTEST_OS_WINDOWS + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + return FilePath(_getcwd(cwd, sizeof(cwd)) == nullptr ? "" : cwd); +#else + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + char* result = getcwd(cwd, sizeof(cwd)); +# if GTEST_OS_NACL + // getcwd will likely fail in NaCl due to the sandbox, so return something + // reasonable. The user may have provided a shim implementation for getcwd, + // however, so fallback only when failure is detected. + return FilePath(result == nullptr ? kCurrentDirectoryString : cwd); +# endif // GTEST_OS_NACL + return FilePath(result == nullptr ? "" : cwd); +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns a copy of the FilePath with the case-insensitive extension removed. +// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns +// FilePath("dir/file"). If a case-insensitive extension is not +// found, returns a copy of the original FilePath. +FilePath FilePath::RemoveExtension(const char* extension) const { + const std::string dot_extension = std::string(".") + extension; + if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) { + return FilePath(pathname_.substr( + 0, pathname_.length() - dot_extension.length())); + } + return *this; +} + +// Returns a pointer to the last occurrence of a valid path separator in +// the FilePath. On Windows, for example, both '/' and '\' are valid path +// separators. Returns NULL if no path separator was found. +const char* FilePath::FindLastPathSeparator() const { + const char* const last_sep = strrchr(c_str(), kPathSeparator); +#if GTEST_HAS_ALT_PATH_SEP_ + const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator); + // Comparing two pointers of which only one is NULL is undefined. + if (last_alt_sep != nullptr && + (last_sep == nullptr || last_alt_sep > last_sep)) { + return last_alt_sep; + } +#endif + return last_sep; +} + +// Returns a copy of the FilePath with the directory part removed. +// Example: FilePath("path/to/file").RemoveDirectoryName() returns +// FilePath("file"). If there is no directory part ("just_a_file"), it returns +// the FilePath unmodified. If there is no file part ("just_a_dir/") it +// returns an empty FilePath (""). +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveDirectoryName() const { + const char* const last_sep = FindLastPathSeparator(); + return last_sep ? FilePath(last_sep + 1) : *this; +} + +// RemoveFileName returns the directory path with the filename removed. +// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". +// If the FilePath is "a_file" or "/a_file", RemoveFileName returns +// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does +// not have a file, like "just/a/dir/", it returns the FilePath unmodified. +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveFileName() const { + const char* const last_sep = FindLastPathSeparator(); + std::string dir; + if (last_sep) { + dir = std::string(c_str(), static_cast<size_t>(last_sep + 1 - c_str())); + } else { + dir = kCurrentDirectoryString; + } + return FilePath(dir); +} + +// Helper functions for naming files in a directory for xml output. + +// Given directory = "dir", base_name = "test", number = 0, +// extension = "xml", returns "dir/test.xml". If number is greater +// than zero (e.g., 12), returns "dir/test_12.xml". +// On Windows platform, uses \ as the separator rather than /. +FilePath FilePath::MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension) { + std::string file; + if (number == 0) { + file = base_name.string() + "." + extension; + } else { + file = base_name.string() + "_" + StreamableToString(number) + + "." + extension; + } + return ConcatPaths(directory, FilePath(file)); +} + +// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml". +// On Windows, uses \ as the separator rather than /. +FilePath FilePath::ConcatPaths(const FilePath& directory, + const FilePath& relative_path) { + if (directory.IsEmpty()) + return relative_path; + const FilePath dir(directory.RemoveTrailingPathSeparator()); + return FilePath(dir.string() + kPathSeparator + relative_path.string()); +} + +// Returns true if pathname describes something findable in the file-system, +// either a file, directory, or whatever. +bool FilePath::FileOrDirectoryExists() const { +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + return attributes != kInvalidFileAttributes; +#else + posix::StatStruct file_stat; + return posix::Stat(pathname_.c_str(), &file_stat) == 0; +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns true if pathname describes a directory in the file-system +// that exists. +bool FilePath::DirectoryExists() const { + bool result = false; +#if GTEST_OS_WINDOWS + // Don't strip off trailing separator if path is a root directory on + // Windows (like "C:\\"). + const FilePath& path(IsRootDirectory() ? *this : + RemoveTrailingPathSeparator()); +#else + const FilePath& path(*this); +#endif + +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(path.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + if ((attributes != kInvalidFileAttributes) && + (attributes & FILE_ATTRIBUTE_DIRECTORY)) { + result = true; + } +#else + posix::StatStruct file_stat; + result = posix::Stat(path.c_str(), &file_stat) == 0 && + posix::IsDir(file_stat); +#endif // GTEST_OS_WINDOWS_MOBILE + + return result; +} + +// Returns true if pathname describes a root directory. (Windows has one +// root directory per disk drive.) +bool FilePath::IsRootDirectory() const { +#if GTEST_OS_WINDOWS + return pathname_.length() == 3 && IsAbsolutePath(); +#else + return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]); +#endif +} + +// Returns true if pathname describes an absolute path. +bool FilePath::IsAbsolutePath() const { + const char* const name = pathname_.c_str(); +#if GTEST_OS_WINDOWS + return pathname_.length() >= 3 && + ((name[0] >= 'a' && name[0] <= 'z') || + (name[0] >= 'A' && name[0] <= 'Z')) && + name[1] == ':' && + IsPathSeparator(name[2]); +#else + return IsPathSeparator(name[0]); +#endif +} + +// Returns a pathname for a file that does not currently exist. The pathname +// will be directory/base_name.extension or +// directory/base_name_<number>.extension if directory/base_name.extension +// already exists. The number will be incremented until a pathname is found +// that does not already exist. +// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. +// There could be a race condition if two or more processes are calling this +// function at the same time -- they could both pick the same filename. +FilePath FilePath::GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension) { + FilePath full_pathname; + int number = 0; + do { + full_pathname.Set(MakeFileName(directory, base_name, number++, extension)); + } while (full_pathname.FileOrDirectoryExists()); + return full_pathname; +} + +// Returns true if FilePath ends with a path separator, which indicates that +// it is intended to represent a directory. Returns false otherwise. +// This does NOT check that a directory (or file) actually exists. +bool FilePath::IsDirectory() const { + return !pathname_.empty() && + IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]); +} + +// Create directories so that path exists. Returns true if successful or if +// the directories already exist; returns false if unable to create directories +// for any reason. +bool FilePath::CreateDirectoriesRecursively() const { + if (!this->IsDirectory()) { + return false; + } + + if (pathname_.length() == 0 || this->DirectoryExists()) { + return true; + } + + const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName()); + return parent.CreateDirectoriesRecursively() && this->CreateFolder(); +} + +// Create the directory so that path exists. Returns true if successful or +// if the directory already exists; returns false if unable to create the +// directory for any reason, including if the parent directory does not +// exist. Not named "CreateDirectory" because that's a macro on Windows. +bool FilePath::CreateFolder() const { +#if GTEST_OS_WINDOWS_MOBILE + FilePath removed_sep(this->RemoveTrailingPathSeparator()); + LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str()); + int result = CreateDirectory(unicode, nullptr) ? 0 : -1; + delete [] unicode; +#elif GTEST_OS_WINDOWS + int result = _mkdir(pathname_.c_str()); +#elif GTEST_OS_ESP8266 || GTEST_OS_XTENSA + // do nothing + int result = 0; +#else + int result = mkdir(pathname_.c_str(), 0777); +#endif // GTEST_OS_WINDOWS_MOBILE + + if (result == -1) { + return this->DirectoryExists(); // An error is OK if the directory exists. + } + return true; // No error. +} + +// If input name has a trailing separator character, remove it and return the +// name, otherwise return the name string unmodified. +// On Windows platform, uses \ as the separator, other platforms use /. +FilePath FilePath::RemoveTrailingPathSeparator() const { + return IsDirectory() + ? FilePath(pathname_.substr(0, pathname_.length() - 1)) + : *this; +} + +// Removes any redundant separators that might be in the pathname. +// For example, "bar///foo" becomes "bar/foo". Does not eliminate other +// redundancies that might be in a pathname involving "." or "..". +void FilePath::Normalize() { + auto out = pathname_.begin(); + + for (const char character : pathname_) { + if (!IsPathSeparator(character)) { + *(out++) = character; + } else if (out == pathname_.begin() || *std::prev(out) != kPathSeparator) { + *(out++) = kPathSeparator; + } else { + continue; + } + } + + pathname_.erase(out, pathname_.end()); +} + +} // namespace internal +} // namespace testing +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This file implements just enough of the matcher interface to allow +// EXPECT_DEATH and friends to accept a matcher argument. + + +#include <string> + +namespace testing { + +// Constructs a matcher that matches a const std::string& whose value is +// equal to s. +Matcher<const std::string&>::Matcher(const std::string& s) { *this = Eq(s); } + +// Constructs a matcher that matches a const std::string& whose value is +// equal to s. +Matcher<const std::string&>::Matcher(const char* s) { + *this = Eq(std::string(s)); +} + +// Constructs a matcher that matches a std::string whose value is equal to +// s. +Matcher<std::string>::Matcher(const std::string& s) { *this = Eq(s); } + +// Constructs a matcher that matches a std::string whose value is equal to +// s. +Matcher<std::string>::Matcher(const char* s) { *this = Eq(std::string(s)); } + +#if GTEST_INTERNAL_HAS_STRING_VIEW +// Constructs a matcher that matches a const StringView& whose value is +// equal to s. +Matcher<const internal::StringView&>::Matcher(const std::string& s) { + *this = Eq(s); +} + +// Constructs a matcher that matches a const StringView& whose value is +// equal to s. +Matcher<const internal::StringView&>::Matcher(const char* s) { + *this = Eq(std::string(s)); +} + +// Constructs a matcher that matches a const StringView& whose value is +// equal to s. +Matcher<const internal::StringView&>::Matcher(internal::StringView s) { + *this = Eq(std::string(s)); +} + +// Constructs a matcher that matches a StringView whose value is equal to +// s. +Matcher<internal::StringView>::Matcher(const std::string& s) { *this = Eq(s); } + +// Constructs a matcher that matches a StringView whose value is equal to +// s. +Matcher<internal::StringView>::Matcher(const char* s) { + *this = Eq(std::string(s)); +} + +// Constructs a matcher that matches a StringView whose value is equal to +// s. +Matcher<internal::StringView>::Matcher(internal::StringView s) { + *this = Eq(std::string(s)); +} +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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 <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <cstdint> +#include <fstream> +#include <memory> + +#if GTEST_OS_WINDOWS +# include <windows.h> +# include <io.h> +# include <sys/stat.h> +# include <map> // Used in ThreadLocal. +# ifdef _MSC_VER +# include <crtdbg.h> +# endif // _MSC_VER +#else +# include <unistd.h> +#endif // GTEST_OS_WINDOWS + +#if GTEST_OS_MAC +# include <mach/mach_init.h> +# include <mach/task.h> +# include <mach/vm_map.h> +#endif // GTEST_OS_MAC + +#if GTEST_OS_DRAGONFLY || GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD || \ + GTEST_OS_NETBSD || GTEST_OS_OPENBSD +# include <sys/sysctl.h> +# if GTEST_OS_DRAGONFLY || GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD +# include <sys/user.h> +# endif +#endif + +#if GTEST_OS_QNX +# include <devctl.h> +# include <fcntl.h> +# include <sys/procfs.h> +#endif // GTEST_OS_QNX + +#if GTEST_OS_AIX +# include <procinfo.h> +# include <sys/types.h> +#endif // GTEST_OS_AIX + +#if GTEST_OS_FUCHSIA +# include <zircon/process.h> +# include <zircon/syscalls.h> +#endif // GTEST_OS_FUCHSIA + + +namespace testing { +namespace internal { + +#if defined(_MSC_VER) || defined(__BORLANDC__) +// MSVC and C++Builder do not provide a definition of STDERR_FILENO. +const int kStdOutFileno = 1; +const int kStdErrFileno = 2; +#else +const int kStdOutFileno = STDOUT_FILENO; +const int kStdErrFileno = STDERR_FILENO; +#endif // _MSC_VER + +#if GTEST_OS_LINUX + +namespace { +template <typename T> +T ReadProcFileField(const std::string& filename, int field) { + std::string dummy; + std::ifstream file(filename.c_str()); + while (field-- > 0) { + file >> dummy; + } + T output = 0; + file >> output; + return output; +} +} // namespace + +// Returns the number of active threads, or 0 when there is an error. +size_t GetThreadCount() { + const std::string filename = + (Message() << "/proc/" << getpid() << "/stat").GetString(); + return ReadProcFileField<size_t>(filename, 19); +} + +#elif GTEST_OS_MAC + +size_t GetThreadCount() { + const task_t task = mach_task_self(); + mach_msg_type_number_t thread_count; + thread_act_array_t thread_list; + const kern_return_t status = task_threads(task, &thread_list, &thread_count); + if (status == KERN_SUCCESS) { + // task_threads allocates resources in thread_list and we need to free them + // to avoid leaks. + vm_deallocate(task, + reinterpret_cast<vm_address_t>(thread_list), + sizeof(thread_t) * thread_count); + return static_cast<size_t>(thread_count); + } else { + return 0; + } +} + +#elif GTEST_OS_DRAGONFLY || GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD || \ + GTEST_OS_NETBSD + +#if GTEST_OS_NETBSD +#undef KERN_PROC +#define KERN_PROC KERN_PROC2 +#define kinfo_proc kinfo_proc2 +#endif + +#if GTEST_OS_DRAGONFLY +#define KP_NLWP(kp) (kp.kp_nthreads) +#elif GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD +#define KP_NLWP(kp) (kp.ki_numthreads) +#elif GTEST_OS_NETBSD +#define KP_NLWP(kp) (kp.p_nlwps) +#endif + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + int mib[] = { + CTL_KERN, + KERN_PROC, + KERN_PROC_PID, + getpid(), +#if GTEST_OS_NETBSD + sizeof(struct kinfo_proc), + 1, +#endif + }; + u_int miblen = sizeof(mib) / sizeof(mib[0]); + struct kinfo_proc info; + size_t size = sizeof(info); + if (sysctl(mib, miblen, &info, &size, NULL, 0)) { + return 0; + } + return static_cast<size_t>(KP_NLWP(info)); +} +#elif GTEST_OS_OPENBSD + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + int mib[] = { + CTL_KERN, + KERN_PROC, + KERN_PROC_PID | KERN_PROC_SHOW_THREADS, + getpid(), + sizeof(struct kinfo_proc), + 0, + }; + u_int miblen = sizeof(mib) / sizeof(mib[0]); + + // get number of structs + size_t size; + if (sysctl(mib, miblen, NULL, &size, NULL, 0)) { + return 0; + } + + mib[5] = static_cast<int>(size / static_cast<size_t>(mib[4])); + + // populate array of structs + struct kinfo_proc info[mib[5]]; + if (sysctl(mib, miblen, &info, &size, NULL, 0)) { + return 0; + } + + // exclude empty members + size_t nthreads = 0; + for (size_t i = 0; i < size / static_cast<size_t>(mib[4]); i++) { + if (info[i].p_tid != -1) + nthreads++; + } + return nthreads; +} + +#elif GTEST_OS_QNX + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const int fd = open("/proc/self/as", O_RDONLY); + if (fd < 0) { + return 0; + } + procfs_info process_info; + const int status = + devctl(fd, DCMD_PROC_INFO, &process_info, sizeof(process_info), nullptr); + close(fd); + if (status == EOK) { + return static_cast<size_t>(process_info.num_threads); + } else { + return 0; + } +} + +#elif GTEST_OS_AIX + +size_t GetThreadCount() { + struct procentry64 entry; + pid_t pid = getpid(); + int status = getprocs64(&entry, sizeof(entry), nullptr, 0, &pid, 1); + if (status == 1) { + return entry.pi_thcount; + } else { + return 0; + } +} + +#elif GTEST_OS_FUCHSIA + +size_t GetThreadCount() { + int dummy_buffer; + size_t avail; + zx_status_t status = zx_object_get_info( + zx_process_self(), + ZX_INFO_PROCESS_THREADS, + &dummy_buffer, + 0, + nullptr, + &avail); + if (status == ZX_OK) { + return avail; + } else { + return 0; + } +} + +#else + +size_t GetThreadCount() { + // There's no portable way to detect the number of threads, so we just + // return 0 to indicate that we cannot detect it. + return 0; +} + +#endif // GTEST_OS_LINUX + +#if GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS + +void SleepMilliseconds(int n) { + ::Sleep(static_cast<DWORD>(n)); +} + +AutoHandle::AutoHandle() + : handle_(INVALID_HANDLE_VALUE) {} + +AutoHandle::AutoHandle(Handle handle) + : handle_(handle) {} + +AutoHandle::~AutoHandle() { + Reset(); +} + +AutoHandle::Handle AutoHandle::Get() const { + return handle_; +} + +void AutoHandle::Reset() { + Reset(INVALID_HANDLE_VALUE); +} + +void AutoHandle::Reset(HANDLE handle) { + // Resetting with the same handle we already own is invalid. + if (handle_ != handle) { + if (IsCloseable()) { + ::CloseHandle(handle_); + } + handle_ = handle; + } else { + GTEST_CHECK_(!IsCloseable()) + << "Resetting a valid handle to itself is likely a programmer error " + "and thus not allowed."; + } +} + +bool AutoHandle::IsCloseable() const { + // Different Windows APIs may use either of these values to represent an + // invalid handle. + return handle_ != nullptr && handle_ != INVALID_HANDLE_VALUE; +} + +Notification::Notification() + : event_(::CreateEvent(nullptr, // Default security attributes. + TRUE, // Do not reset automatically. + FALSE, // Initially unset. + nullptr)) { // Anonymous event. + GTEST_CHECK_(event_.Get() != nullptr); +} + +void Notification::Notify() { + GTEST_CHECK_(::SetEvent(event_.Get()) != FALSE); +} + +void Notification::WaitForNotification() { + GTEST_CHECK_( + ::WaitForSingleObject(event_.Get(), INFINITE) == WAIT_OBJECT_0); +} + +Mutex::Mutex() + : owner_thread_id_(0), + type_(kDynamic), + critical_section_init_phase_(0), + critical_section_(new CRITICAL_SECTION) { + ::InitializeCriticalSection(critical_section_); +} + +Mutex::~Mutex() { + // Static mutexes are leaked intentionally. It is not thread-safe to try + // to clean them up. + if (type_ == kDynamic) { + ::DeleteCriticalSection(critical_section_); + delete critical_section_; + critical_section_ = nullptr; + } +} + +void Mutex::Lock() { + ThreadSafeLazyInit(); + ::EnterCriticalSection(critical_section_); + owner_thread_id_ = ::GetCurrentThreadId(); +} + +void Mutex::Unlock() { + ThreadSafeLazyInit(); + // We don't protect writing to owner_thread_id_ here, as it's the + // caller's responsibility to ensure that the current thread holds the + // mutex when this is called. + owner_thread_id_ = 0; + ::LeaveCriticalSection(critical_section_); +} + +// Does nothing if the current thread holds the mutex. Otherwise, crashes +// with high probability. +void Mutex::AssertHeld() { + ThreadSafeLazyInit(); + GTEST_CHECK_(owner_thread_id_ == ::GetCurrentThreadId()) + << "The current thread is not holding the mutex @" << this; +} + +namespace { + +#ifdef _MSC_VER +// Use the RAII idiom to flag mem allocs that are intentionally never +// deallocated. The motivation is to silence the false positive mem leaks +// that are reported by the debug version of MS's CRT which can only detect +// if an alloc is missing a matching deallocation. +// Example: +// MemoryIsNotDeallocated memory_is_not_deallocated; +// critical_section_ = new CRITICAL_SECTION; +// +class MemoryIsNotDeallocated +{ + public: + MemoryIsNotDeallocated() : old_crtdbg_flag_(0) { + old_crtdbg_flag_ = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + // Set heap allocation block type to _IGNORE_BLOCK so that MS debug CRT + // doesn't report mem leak if there's no matching deallocation. + _CrtSetDbgFlag(old_crtdbg_flag_ & ~_CRTDBG_ALLOC_MEM_DF); + } + + ~MemoryIsNotDeallocated() { + // Restore the original _CRTDBG_ALLOC_MEM_DF flag + _CrtSetDbgFlag(old_crtdbg_flag_); + } + + private: + int old_crtdbg_flag_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(MemoryIsNotDeallocated); +}; +#endif // _MSC_VER + +} // namespace + +// Initializes owner_thread_id_ and critical_section_ in static mutexes. +void Mutex::ThreadSafeLazyInit() { + // Dynamic mutexes are initialized in the constructor. + if (type_ == kStatic) { + switch ( + ::InterlockedCompareExchange(&critical_section_init_phase_, 1L, 0L)) { + case 0: + // If critical_section_init_phase_ was 0 before the exchange, we + // are the first to test it and need to perform the initialization. + owner_thread_id_ = 0; + { + // Use RAII to flag that following mem alloc is never deallocated. +#ifdef _MSC_VER + MemoryIsNotDeallocated memory_is_not_deallocated; +#endif // _MSC_VER + critical_section_ = new CRITICAL_SECTION; + } + ::InitializeCriticalSection(critical_section_); + // Updates the critical_section_init_phase_ to 2 to signal + // initialization complete. + GTEST_CHECK_(::InterlockedCompareExchange( + &critical_section_init_phase_, 2L, 1L) == + 1L); + break; + case 1: + // Somebody else is already initializing the mutex; spin until they + // are done. + while (::InterlockedCompareExchange(&critical_section_init_phase_, + 2L, + 2L) != 2L) { + // Possibly yields the rest of the thread's time slice to other + // threads. + ::Sleep(0); + } + break; + + case 2: + break; // The mutex is already initialized and ready for use. + + default: + GTEST_CHECK_(false) + << "Unexpected value of critical_section_init_phase_ " + << "while initializing a static mutex."; + } + } +} + +namespace { + +class ThreadWithParamSupport : public ThreadWithParamBase { + public: + static HANDLE CreateThread(Runnable* runnable, + Notification* thread_can_start) { + ThreadMainParam* param = new ThreadMainParam(runnable, thread_can_start); + DWORD thread_id; + HANDLE thread_handle = ::CreateThread( + nullptr, // Default security. + 0, // Default stack size. + &ThreadWithParamSupport::ThreadMain, + param, // Parameter to ThreadMainStatic + 0x0, // Default creation flags. + &thread_id); // Need a valid pointer for the call to work under Win98. + GTEST_CHECK_(thread_handle != nullptr) + << "CreateThread failed with error " << ::GetLastError() << "."; + if (thread_handle == nullptr) { + delete param; + } + return thread_handle; + } + + private: + struct ThreadMainParam { + ThreadMainParam(Runnable* runnable, Notification* thread_can_start) + : runnable_(runnable), + thread_can_start_(thread_can_start) { + } + std::unique_ptr<Runnable> runnable_; + // Does not own. + Notification* thread_can_start_; + }; + + static DWORD WINAPI ThreadMain(void* ptr) { + // Transfers ownership. + std::unique_ptr<ThreadMainParam> param(static_cast<ThreadMainParam*>(ptr)); + if (param->thread_can_start_ != nullptr) + param->thread_can_start_->WaitForNotification(); + param->runnable_->Run(); + return 0; + } + + // Prohibit instantiation. + ThreadWithParamSupport(); + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParamSupport); +}; + +} // namespace + +ThreadWithParamBase::ThreadWithParamBase(Runnable *runnable, + Notification* thread_can_start) + : thread_(ThreadWithParamSupport::CreateThread(runnable, + thread_can_start)) { +} + +ThreadWithParamBase::~ThreadWithParamBase() { + Join(); +} + +void ThreadWithParamBase::Join() { + GTEST_CHECK_(::WaitForSingleObject(thread_.Get(), INFINITE) == WAIT_OBJECT_0) + << "Failed to join the thread with error " << ::GetLastError() << "."; +} + +// Maps a thread to a set of ThreadIdToThreadLocals that have values +// instantiated on that thread and notifies them when the thread exits. A +// ThreadLocal instance is expected to persist until all threads it has +// values on have terminated. +class ThreadLocalRegistryImpl { + public: + // Registers thread_local_instance as having value on the current thread. + // Returns a value that can be used to identify the thread from other threads. + static ThreadLocalValueHolderBase* GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance) { +#ifdef _MSC_VER + MemoryIsNotDeallocated memory_is_not_deallocated; +#endif // _MSC_VER + DWORD current_thread = ::GetCurrentThreadId(); + MutexLock lock(&mutex_); + ThreadIdToThreadLocals* const thread_to_thread_locals = + GetThreadLocalsMapLocked(); + ThreadIdToThreadLocals::iterator thread_local_pos = + thread_to_thread_locals->find(current_thread); + if (thread_local_pos == thread_to_thread_locals->end()) { + thread_local_pos = thread_to_thread_locals->insert( + std::make_pair(current_thread, ThreadLocalValues())).first; + StartWatcherThreadFor(current_thread); + } + ThreadLocalValues& thread_local_values = thread_local_pos->second; + ThreadLocalValues::iterator value_pos = + thread_local_values.find(thread_local_instance); + if (value_pos == thread_local_values.end()) { + value_pos = + thread_local_values + .insert(std::make_pair( + thread_local_instance, + std::shared_ptr<ThreadLocalValueHolderBase>( + thread_local_instance->NewValueForCurrentThread()))) + .first; + } + return value_pos->second.get(); + } + + static void OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance) { + std::vector<std::shared_ptr<ThreadLocalValueHolderBase> > value_holders; + // Clean up the ThreadLocalValues data structure while holding the lock, but + // defer the destruction of the ThreadLocalValueHolderBases. + { + MutexLock lock(&mutex_); + ThreadIdToThreadLocals* const thread_to_thread_locals = + GetThreadLocalsMapLocked(); + for (ThreadIdToThreadLocals::iterator it = + thread_to_thread_locals->begin(); + it != thread_to_thread_locals->end(); + ++it) { + ThreadLocalValues& thread_local_values = it->second; + ThreadLocalValues::iterator value_pos = + thread_local_values.find(thread_local_instance); + if (value_pos != thread_local_values.end()) { + value_holders.push_back(value_pos->second); + thread_local_values.erase(value_pos); + // This 'if' can only be successful at most once, so theoretically we + // could break out of the loop here, but we don't bother doing so. + } + } + } + // Outside the lock, let the destructor for 'value_holders' deallocate the + // ThreadLocalValueHolderBases. + } + + static void OnThreadExit(DWORD thread_id) { + GTEST_CHECK_(thread_id != 0) << ::GetLastError(); + std::vector<std::shared_ptr<ThreadLocalValueHolderBase> > value_holders; + // Clean up the ThreadIdToThreadLocals data structure while holding the + // lock, but defer the destruction of the ThreadLocalValueHolderBases. + { + MutexLock lock(&mutex_); + ThreadIdToThreadLocals* const thread_to_thread_locals = + GetThreadLocalsMapLocked(); + ThreadIdToThreadLocals::iterator thread_local_pos = + thread_to_thread_locals->find(thread_id); + if (thread_local_pos != thread_to_thread_locals->end()) { + ThreadLocalValues& thread_local_values = thread_local_pos->second; + for (ThreadLocalValues::iterator value_pos = + thread_local_values.begin(); + value_pos != thread_local_values.end(); + ++value_pos) { + value_holders.push_back(value_pos->second); + } + thread_to_thread_locals->erase(thread_local_pos); + } + } + // Outside the lock, let the destructor for 'value_holders' deallocate the + // ThreadLocalValueHolderBases. + } + + private: + // In a particular thread, maps a ThreadLocal object to its value. + typedef std::map<const ThreadLocalBase*, + std::shared_ptr<ThreadLocalValueHolderBase> > + ThreadLocalValues; + // Stores all ThreadIdToThreadLocals having values in a thread, indexed by + // thread's ID. + typedef std::map<DWORD, ThreadLocalValues> ThreadIdToThreadLocals; + + // Holds the thread id and thread handle that we pass from + // StartWatcherThreadFor to WatcherThreadFunc. + typedef std::pair<DWORD, HANDLE> ThreadIdAndHandle; + + static void StartWatcherThreadFor(DWORD thread_id) { + // The returned handle will be kept in thread_map and closed by + // watcher_thread in WatcherThreadFunc. + HANDLE thread = ::OpenThread(SYNCHRONIZE | THREAD_QUERY_INFORMATION, + FALSE, + thread_id); + GTEST_CHECK_(thread != nullptr); + // We need to pass a valid thread ID pointer into CreateThread for it + // to work correctly under Win98. + DWORD watcher_thread_id; + HANDLE watcher_thread = ::CreateThread( + nullptr, // Default security. + 0, // Default stack size + &ThreadLocalRegistryImpl::WatcherThreadFunc, + reinterpret_cast<LPVOID>(new ThreadIdAndHandle(thread_id, thread)), + CREATE_SUSPENDED, &watcher_thread_id); + GTEST_CHECK_(watcher_thread != nullptr); + // Give the watcher thread the same priority as ours to avoid being + // blocked by it. + ::SetThreadPriority(watcher_thread, + ::GetThreadPriority(::GetCurrentThread())); + ::ResumeThread(watcher_thread); + ::CloseHandle(watcher_thread); + } + + // Monitors exit from a given thread and notifies those + // ThreadIdToThreadLocals about thread termination. + static DWORD WINAPI WatcherThreadFunc(LPVOID param) { + const ThreadIdAndHandle* tah = + reinterpret_cast<const ThreadIdAndHandle*>(param); + GTEST_CHECK_( + ::WaitForSingleObject(tah->second, INFINITE) == WAIT_OBJECT_0); + OnThreadExit(tah->first); + ::CloseHandle(tah->second); + delete tah; + return 0; + } + + // Returns map of thread local instances. + static ThreadIdToThreadLocals* GetThreadLocalsMapLocked() { + mutex_.AssertHeld(); +#ifdef _MSC_VER + MemoryIsNotDeallocated memory_is_not_deallocated; +#endif // _MSC_VER + static ThreadIdToThreadLocals* map = new ThreadIdToThreadLocals(); + return map; + } + + // Protects access to GetThreadLocalsMapLocked() and its return value. + static Mutex mutex_; + // Protects access to GetThreadMapLocked() and its return value. + static Mutex thread_map_mutex_; +}; + +Mutex ThreadLocalRegistryImpl::mutex_(Mutex::kStaticMutex); +Mutex ThreadLocalRegistryImpl::thread_map_mutex_(Mutex::kStaticMutex); + +ThreadLocalValueHolderBase* ThreadLocalRegistry::GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance) { + return ThreadLocalRegistryImpl::GetValueOnCurrentThread( + thread_local_instance); +} + +void ThreadLocalRegistry::OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance) { + ThreadLocalRegistryImpl::OnThreadLocalDestroyed(thread_local_instance); +} + +#endif // GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS + +#if GTEST_USES_POSIX_RE + +// Implements RE. Currently only needed for death tests. + +RE::~RE() { + if (is_valid_) { + // regfree'ing an invalid regex might crash because the content + // of the regex is undefined. Since the regex's are essentially + // the same, one cannot be valid (or invalid) without the other + // being so too. + regfree(&partial_regex_); + regfree(&full_regex_); + } + free(const_cast<char*>(pattern_)); +} + +// Returns true if and only if regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.full_regex_, str, 1, &match, 0) == 0; +} + +// Returns true if and only if regular expression re matches a substring of +// str (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.partial_regex_, str, 1, &match, 0) == 0; +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = posix::StrDup(regex); + + // Reserves enough bytes to hold the regular expression used for a + // full match. + const size_t full_regex_len = strlen(regex) + 10; + char* const full_pattern = new char[full_regex_len]; + + snprintf(full_pattern, full_regex_len, "^(%s)$", regex); + is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0; + // We want to call regcomp(&partial_regex_, ...) even if the + // previous expression returns false. Otherwise partial_regex_ may + // not be properly initialized can may cause trouble when it's + // freed. + // + // Some implementation of POSIX regex (e.g. on at least some + // versions of Cygwin) doesn't accept the empty string as a valid + // regex. We change it to an equivalent form "()" to be safe. + if (is_valid_) { + const char* const partial_regex = (*regex == '\0') ? "()" : regex; + is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0; + } + EXPECT_TRUE(is_valid_) + << "Regular expression \"" << regex + << "\" is not a valid POSIX Extended regular expression."; + + delete[] full_pattern; +} + +#elif GTEST_USES_SIMPLE_RE + +// Returns true if and only if ch appears anywhere in str (excluding the +// terminating '\0' character). +bool IsInSet(char ch, const char* str) { + return ch != '\0' && strchr(str, ch) != nullptr; +} + +// Returns true if and only if ch belongs to the given classification. +// Unlike similar functions in <ctype.h>, these aren't affected by the +// current locale. +bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; } +bool IsAsciiPunct(char ch) { + return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"); +} +bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); } +bool IsAsciiWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); } +bool IsAsciiWordChar(char ch) { + return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || + ('0' <= ch && ch <= '9') || ch == '_'; +} + +// Returns true if and only if "\\c" is a supported escape sequence. +bool IsValidEscape(char c) { + return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW")); +} + +// Returns true if and only if the given atom (specified by escaped and +// pattern) matches ch. The result is undefined if the atom is invalid. +bool AtomMatchesChar(bool escaped, char pattern_char, char ch) { + if (escaped) { // "\\p" where p is pattern_char. + switch (pattern_char) { + case 'd': return IsAsciiDigit(ch); + case 'D': return !IsAsciiDigit(ch); + case 'f': return ch == '\f'; + case 'n': return ch == '\n'; + case 'r': return ch == '\r'; + case 's': return IsAsciiWhiteSpace(ch); + case 'S': return !IsAsciiWhiteSpace(ch); + case 't': return ch == '\t'; + case 'v': return ch == '\v'; + case 'w': return IsAsciiWordChar(ch); + case 'W': return !IsAsciiWordChar(ch); + } + return IsAsciiPunct(pattern_char) && pattern_char == ch; + } + + return (pattern_char == '.' && ch != '\n') || pattern_char == ch; +} + +// Helper function used by ValidateRegex() to format error messages. +static std::string FormatRegexSyntaxError(const char* regex, int index) { + return (Message() << "Syntax error at index " << index + << " in simple regular expression \"" << regex << "\": ").GetString(); +} + +// Generates non-fatal failures and returns false if regex is invalid; +// otherwise returns true. +bool ValidateRegex(const char* regex) { + if (regex == nullptr) { + ADD_FAILURE() << "NULL is not a valid simple regular expression."; + return false; + } + + bool is_valid = true; + + // True if and only if ?, *, or + can follow the previous atom. + bool prev_repeatable = false; + for (int i = 0; regex[i]; i++) { + if (regex[i] == '\\') { // An escape sequence + i++; + if (regex[i] == '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "'\\' cannot appear at the end."; + return false; + } + + if (!IsValidEscape(regex[i])) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "invalid escape sequence \"\\" << regex[i] << "\"."; + is_valid = false; + } + prev_repeatable = true; + } else { // Not an escape sequence. + const char ch = regex[i]; + + if (ch == '^' && i > 0) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'^' can only appear at the beginning."; + is_valid = false; + } else if (ch == '$' && regex[i + 1] != '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'$' can only appear at the end."; + is_valid = false; + } else if (IsInSet(ch, "()[]{}|")) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' is unsupported."; + is_valid = false; + } else if (IsRepeat(ch) && !prev_repeatable) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' can only follow a repeatable token."; + is_valid = false; + } + + prev_repeatable = !IsInSet(ch, "^$?*+"); + } + } + + return is_valid; +} + +// Matches a repeated regex atom followed by a valid simple regular +// expression. The regex atom is defined as c if escaped is false, +// or \c otherwise. repeat is the repetition meta character (?, *, +// or +). The behavior is undefined if str contains too many +// characters to be indexable by size_t, in which case the test will +// probably time out anyway. We are fine with this limitation as +// std::string has it too. +bool MatchRepetitionAndRegexAtHead( + bool escaped, char c, char repeat, const char* regex, + const char* str) { + const size_t min_count = (repeat == '+') ? 1 : 0; + const size_t max_count = (repeat == '?') ? 1 : + static_cast<size_t>(-1) - 1; + // We cannot call numeric_limits::max() as it conflicts with the + // max() macro on Windows. + + for (size_t i = 0; i <= max_count; ++i) { + // We know that the atom matches each of the first i characters in str. + if (i >= min_count && MatchRegexAtHead(regex, str + i)) { + // We have enough matches at the head, and the tail matches too. + // Since we only care about *whether* the pattern matches str + // (as opposed to *how* it matches), there is no need to find a + // greedy match. + return true; + } + if (str[i] == '\0' || !AtomMatchesChar(escaped, c, str[i])) + return false; + } + return false; +} + +// Returns true if and only if regex matches a prefix of str. regex must +// be a valid simple regular expression and not start with "^", or the +// result is undefined. +bool MatchRegexAtHead(const char* regex, const char* str) { + if (*regex == '\0') // An empty regex matches a prefix of anything. + return true; + + // "$" only matches the end of a string. Note that regex being + // valid guarantees that there's nothing after "$" in it. + if (*regex == '$') + return *str == '\0'; + + // Is the first thing in regex an escape sequence? + const bool escaped = *regex == '\\'; + if (escaped) + ++regex; + if (IsRepeat(regex[1])) { + // MatchRepetitionAndRegexAtHead() calls MatchRegexAtHead(), so + // here's an indirect recursion. It terminates as the regex gets + // shorter in each recursion. + return MatchRepetitionAndRegexAtHead( + escaped, regex[0], regex[1], regex + 2, str); + } else { + // regex isn't empty, isn't "$", and doesn't start with a + // repetition. We match the first atom of regex with the first + // character of str and recurse. + return (*str != '\0') && AtomMatchesChar(escaped, *regex, *str) && + MatchRegexAtHead(regex + 1, str + 1); + } +} + +// Returns true if and only if regex matches any substring of str. regex must +// be a valid simple regular expression, or the result is undefined. +// +// The algorithm is recursive, but the recursion depth doesn't exceed +// the regex length, so we won't need to worry about running out of +// stack space normally. In rare cases the time complexity can be +// exponential with respect to the regex length + the string length, +// but usually it's must faster (often close to linear). +bool MatchRegexAnywhere(const char* regex, const char* str) { + if (regex == nullptr || str == nullptr) return false; + + if (*regex == '^') + return MatchRegexAtHead(regex + 1, str); + + // A successful match can be anywhere in str. + do { + if (MatchRegexAtHead(regex, str)) + return true; + } while (*str++ != '\0'); + return false; +} + +// Implements the RE class. + +RE::~RE() { + free(const_cast<char*>(pattern_)); + free(const_cast<char*>(full_pattern_)); +} + +// Returns true if and only if regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str); +} + +// Returns true if and only if regular expression re matches a substring of +// str (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str); +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = full_pattern_ = nullptr; + if (regex != nullptr) { + pattern_ = posix::StrDup(regex); + } + + is_valid_ = ValidateRegex(regex); + if (!is_valid_) { + // No need to calculate the full pattern when the regex is invalid. + return; + } + + const size_t len = strlen(regex); + // Reserves enough bytes to hold the regular expression used for a + // full match: we need space to prepend a '^', append a '$', and + // terminate the string with '\0'. + char* buffer = static_cast<char*>(malloc(len + 3)); + full_pattern_ = buffer; + + if (*regex != '^') + *buffer++ = '^'; // Makes sure full_pattern_ starts with '^'. + + // We don't use snprintf or strncpy, as they trigger a warning when + // compiled with VC++ 8.0. + memcpy(buffer, regex, len); + buffer += len; + + if (len == 0 || regex[len - 1] != '$') + *buffer++ = '$'; // Makes sure full_pattern_ ends with '$'. + + *buffer = '\0'; +} + +#endif // GTEST_USES_POSIX_RE + +const char kUnknownFile[] = "unknown file"; + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) { + const std::string file_name(file == nullptr ? kUnknownFile : file); + + if (line < 0) { + return file_name + ":"; + } +#ifdef _MSC_VER + return file_name + "(" + StreamableToString(line) + "):"; +#else + return file_name + ":" + StreamableToString(line) + ":"; +#endif // _MSC_VER +} + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +// Note that FormatCompilerIndependentFileLocation() does NOT append colon +// to the file location it produces, unlike FormatFileLocation(). +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation( + const char* file, int line) { + const std::string file_name(file == nullptr ? kUnknownFile : file); + + if (line < 0) + return file_name; + else + return file_name + ":" + StreamableToString(line); +} + +GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line) + : severity_(severity) { + const char* const marker = + severity == GTEST_INFO ? "[ INFO ]" : + severity == GTEST_WARNING ? "[WARNING]" : + severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]"; + GetStream() << ::std::endl << marker << " " + << FormatFileLocation(file, line).c_str() << ": "; +} + +// Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. +GTestLog::~GTestLog() { + GetStream() << ::std::endl; + if (severity_ == GTEST_FATAL) { + fflush(stderr); + posix::Abort(); + } +} + +// Disable Microsoft deprecation warnings for POSIX functions called from +// this class (creat, dup, dup2, and close) +GTEST_DISABLE_MSC_DEPRECATED_PUSH_() + +#if GTEST_HAS_STREAM_REDIRECTION + +// Object that captures an output stream (stdout/stderr). +class CapturedStream { + public: + // The ctor redirects the stream to a temporary file. + explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { +# if GTEST_OS_WINDOWS + char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT + char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT + + ::GetTempPathA(sizeof(temp_dir_path), temp_dir_path); + const UINT success = ::GetTempFileNameA(temp_dir_path, + "gtest_redir", + 0, // Generate unique file name. + temp_file_path); + GTEST_CHECK_(success != 0) + << "Unable to create a temporary file in " << temp_dir_path; + const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE); + GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file " + << temp_file_path; + filename_ = temp_file_path; +# else + // There's no guarantee that a test has write access to the current + // directory, so we create the temporary file in the /tmp directory + // instead. We use /tmp on most systems, and /sdcard on Android. + // That's because Android doesn't have /tmp. +# if GTEST_OS_LINUX_ANDROID + // Note: Android applications are expected to call the framework's + // Context.getExternalStorageDirectory() method through JNI to get + // the location of the world-writable SD Card directory. However, + // this requires a Context handle, which cannot be retrieved + // globally from native code. Doing so also precludes running the + // code as part of a regular standalone executable, which doesn't + // run in a Dalvik process (e.g. when running it through 'adb shell'). + // + // The location /data/local/tmp is directly accessible from native code. + // '/sdcard' and other variants cannot be relied on, as they are not + // guaranteed to be mounted, or may have a delay in mounting. + char name_template[] = "/data/local/tmp/gtest_captured_stream.XXXXXX"; +# else + char name_template[] = "/tmp/captured_stream.XXXXXX"; +# endif // GTEST_OS_LINUX_ANDROID + const int captured_fd = mkstemp(name_template); + if (captured_fd == -1) { + GTEST_LOG_(WARNING) + << "Failed to create tmp file " << name_template + << " for test; does the test have access to the /tmp directory?"; + } + filename_ = name_template; +# endif // GTEST_OS_WINDOWS + fflush(nullptr); + dup2(captured_fd, fd_); + close(captured_fd); + } + + ~CapturedStream() { + remove(filename_.c_str()); + } + + std::string GetCapturedString() { + if (uncaptured_fd_ != -1) { + // Restores the original stream. + fflush(nullptr); + dup2(uncaptured_fd_, fd_); + close(uncaptured_fd_); + uncaptured_fd_ = -1; + } + + FILE* const file = posix::FOpen(filename_.c_str(), "r"); + if (file == nullptr) { + GTEST_LOG_(FATAL) << "Failed to open tmp file " << filename_ + << " for capturing stream."; + } + const std::string content = ReadEntireFile(file); + posix::FClose(file); + return content; + } + + private: + const int fd_; // A stream to capture. + int uncaptured_fd_; + // Name of the temporary file holding the stderr output. + ::std::string filename_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream); +}; + +GTEST_DISABLE_MSC_DEPRECATED_POP_() + +static CapturedStream* g_captured_stderr = nullptr; +static CapturedStream* g_captured_stdout = nullptr; + +// Starts capturing an output stream (stdout/stderr). +static void CaptureStream(int fd, const char* stream_name, + CapturedStream** stream) { + if (*stream != nullptr) { + GTEST_LOG_(FATAL) << "Only one " << stream_name + << " capturer can exist at a time."; + } + *stream = new CapturedStream(fd); +} + +// Stops capturing the output stream and returns the captured string. +static std::string GetCapturedStream(CapturedStream** captured_stream) { + const std::string content = (*captured_stream)->GetCapturedString(); + + delete *captured_stream; + *captured_stream = nullptr; + + return content; +} + +// Starts capturing stdout. +void CaptureStdout() { + CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout); +} + +// Starts capturing stderr. +void CaptureStderr() { + CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr); +} + +// Stops capturing stdout and returns the captured string. +std::string GetCapturedStdout() { + return GetCapturedStream(&g_captured_stdout); +} + +// Stops capturing stderr and returns the captured string. +std::string GetCapturedStderr() { + return GetCapturedStream(&g_captured_stderr); +} + +#endif // GTEST_HAS_STREAM_REDIRECTION + + + + + +size_t GetFileSize(FILE* file) { + fseek(file, 0, SEEK_END); + return static_cast<size_t>(ftell(file)); +} + +std::string ReadEntireFile(FILE* file) { + const size_t file_size = GetFileSize(file); + char* const buffer = new char[file_size]; + + size_t bytes_last_read = 0; // # of bytes read in the last fread() + size_t bytes_read = 0; // # of bytes read so far + + fseek(file, 0, SEEK_SET); + + // Keeps reading the file until we cannot read further or the + // pre-determined file size is reached. + do { + bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file); + bytes_read += bytes_last_read; + } while (bytes_last_read > 0 && bytes_read < file_size); + + const std::string content(buffer, bytes_read); + delete[] buffer; + + return content; +} + +#if GTEST_HAS_DEATH_TEST +static const std::vector<std::string>* g_injected_test_argvs = + nullptr; // Owned. + +std::vector<std::string> GetInjectableArgvs() { + if (g_injected_test_argvs != nullptr) { + return *g_injected_test_argvs; + } + return GetArgvs(); +} + +void SetInjectableArgvs(const std::vector<std::string>* new_argvs) { + if (g_injected_test_argvs != new_argvs) delete g_injected_test_argvs; + g_injected_test_argvs = new_argvs; +} + +void SetInjectableArgvs(const std::vector<std::string>& new_argvs) { + SetInjectableArgvs( + new std::vector<std::string>(new_argvs.begin(), new_argvs.end())); +} + +void ClearInjectableArgvs() { + delete g_injected_test_argvs; + g_injected_test_argvs = nullptr; +} +#endif // GTEST_HAS_DEATH_TEST + +#if GTEST_OS_WINDOWS_MOBILE +namespace posix { +void Abort() { + DebugBreak(); + TerminateProcess(GetCurrentProcess(), 1); +} +} // namespace posix +#endif // GTEST_OS_WINDOWS_MOBILE + +// Returns the name of the environment variable corresponding to the +// given flag. For example, FlagToEnvVar("foo") will return +// "GTEST_FOO" in the open-source version. +static std::string FlagToEnvVar(const char* flag) { + const std::string full_flag = + (Message() << GTEST_FLAG_PREFIX_ << flag).GetString(); + + Message env_var; + for (size_t i = 0; i != full_flag.length(); i++) { + env_var << ToUpper(full_flag.c_str()[i]); + } + + return env_var.GetString(); +} + +// Parses 'str' for a 32-bit signed integer. If successful, writes +// the result to *value and returns true; otherwise leaves *value +// unchanged and returns false. +bool ParseInt32(const Message& src_text, const char* str, int32_t* value) { + // Parses the environment variable as a decimal integer. + char* end = nullptr; + const long long_value = strtol(str, &end, 10); // NOLINT + + // Has strtol() consumed all characters in the string? + if (*end != '\0') { + // No - an invalid character was encountered. + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value \"" << str << "\".\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + // Is the parsed value in the range of an int32_t? + const auto result = static_cast<int32_t>(long_value); + if (long_value == LONG_MAX || long_value == LONG_MIN || + // The parsed value overflows as a long. (strtol() returns + // LONG_MAX or LONG_MIN when the input overflows.) + result != long_value + // The parsed value overflows as an int32_t. + ) { + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value " << str << ", which overflows.\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + *value = result; + return true; +} + +// Reads and returns the Boolean environment variable corresponding to +// the given flag; if it's not set, returns default_value. +// +// The value is considered true if and only if it's not "0". +bool BoolFromGTestEnv(const char* flag, bool default_value) { +#if defined(GTEST_GET_BOOL_FROM_ENV_) + return GTEST_GET_BOOL_FROM_ENV_(flag, default_value); +#else + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + return string_value == nullptr ? default_value + : strcmp(string_value, "0") != 0; +#endif // defined(GTEST_GET_BOOL_FROM_ENV_) +} + +// Reads and returns a 32-bit integer stored in the environment +// variable corresponding to the given flag; if it isn't set or +// doesn't represent a valid 32-bit integer, returns default_value. +int32_t Int32FromGTestEnv(const char* flag, int32_t default_value) { +#if defined(GTEST_GET_INT32_FROM_ENV_) + return GTEST_GET_INT32_FROM_ENV_(flag, default_value); +#else + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + if (string_value == nullptr) { + // The environment variable is not set. + return default_value; + } + + int32_t result = default_value; + if (!ParseInt32(Message() << "Environment variable " << env_var, + string_value, &result)) { + printf("The default value %s is used.\n", + (Message() << default_value).GetString().c_str()); + fflush(stdout); + return default_value; + } + + return result; +#endif // defined(GTEST_GET_INT32_FROM_ENV_) +} + +// As a special case for the 'output' flag, if GTEST_OUTPUT is not +// set, we look for XML_OUTPUT_FILE, which is set by the Bazel build +// system. The value of XML_OUTPUT_FILE is a filename without the +// "xml:" prefix of GTEST_OUTPUT. +// Note that this is meant to be called at the call site so it does +// not check that the flag is 'output' +// In essence this checks an env variable called XML_OUTPUT_FILE +// and if it is set we prepend "xml:" to its value, if it not set we return "" +std::string OutputFlagAlsoCheckEnvVar(){ + std::string default_value_for_output_flag = ""; + const char* xml_output_file_env = posix::GetEnv("XML_OUTPUT_FILE"); + if (nullptr != xml_output_file_env) { + default_value_for_output_flag = std::string("xml:") + xml_output_file_env; + } + return default_value_for_output_flag; +} + +// Reads and returns the string environment variable corresponding to +// the given flag; if it's not set, returns default_value. +const char* StringFromGTestEnv(const char* flag, const char* default_value) { +#if defined(GTEST_GET_STRING_FROM_ENV_) + return GTEST_GET_STRING_FROM_ENV_(flag, default_value); +#else + const std::string env_var = FlagToEnvVar(flag); + const char* const value = posix::GetEnv(env_var.c_str()); + return value == nullptr ? default_value : value; +#endif // defined(GTEST_GET_STRING_FROM_ENV_) +} + +} // namespace internal +} // namespace testing +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Google Test - The Google C++ Testing and Mocking Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr); +// +// It uses the << operator when possible, and prints the bytes in the +// object otherwise. A user can override its behavior for a class +// type Foo by defining either operator<<(::std::ostream&, const Foo&) +// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that +// defines Foo. + + +#include <stdio.h> + +#include <cctype> +#include <cstdint> +#include <cwchar> +#include <ostream> // NOLINT +#include <string> +#include <type_traits> + + +namespace testing { + +namespace { + +using ::std::ostream; + +// Prints a segment of bytes in the given object. +GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start, + size_t count, ostream* os) { + char text[5] = ""; + for (size_t i = 0; i != count; i++) { + const size_t j = start + i; + if (i != 0) { + // Organizes the bytes into groups of 2 for easy parsing by + // human. + if ((j % 2) == 0) + *os << ' '; + else + *os << '-'; + } + GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]); + *os << text; + } +} + +// Prints the bytes in the given value to the given ostream. +void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count, + ostream* os) { + // Tells the user how big the object is. + *os << count << "-byte object <"; + + const size_t kThreshold = 132; + const size_t kChunkSize = 64; + // If the object size is bigger than kThreshold, we'll have to omit + // some details by printing only the first and the last kChunkSize + // bytes. + if (count < kThreshold) { + PrintByteSegmentInObjectTo(obj_bytes, 0, count, os); + } else { + PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os); + *os << " ... "; + // Rounds up to 2-byte boundary. + const size_t resume_pos = (count - kChunkSize + 1)/2*2; + PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os); + } + *os << ">"; +} + +// Helpers for widening a character to char32_t. Since the standard does not +// specify if char / wchar_t is signed or unsigned, it is important to first +// convert it to the unsigned type of the same width before widening it to +// char32_t. +template <typename CharType> +char32_t ToChar32(CharType in) { + return static_cast<char32_t>( + static_cast<typename std::make_unsigned<CharType>::type>(in)); +} + +} // namespace + +namespace internal { + +// Delegates to PrintBytesInObjectToImpl() to print the bytes in the +// given object. The delegation simplifies the implementation, which +// uses the << operator and thus is easier done outside of the +// ::testing::internal namespace, which contains a << operator that +// sometimes conflicts with the one in STL. +void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count, + ostream* os) { + PrintBytesInObjectToImpl(obj_bytes, count, os); +} + +// Depending on the value of a char (or wchar_t), we print it in one +// of three formats: +// - as is if it's a printable ASCII (e.g. 'a', '2', ' '), +// - as a hexadecimal escape sequence (e.g. '\x7F'), or +// - as a special escape sequence (e.g. '\r', '\n'). +enum CharFormat { + kAsIs, + kHexEscape, + kSpecialEscape +}; + +// Returns true if c is a printable ASCII character. We test the +// value of c directly instead of calling isprint(), which is buggy on +// Windows Mobile. +inline bool IsPrintableAscii(char32_t c) { return 0x20 <= c && c <= 0x7E; } + +// Prints c (of type char, char8_t, char16_t, char32_t, or wchar_t) as a +// character literal without the quotes, escaping it when necessary; returns how +// c was formatted. +template <typename Char> +static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) { + const char32_t u_c = ToChar32(c); + switch (u_c) { + case L'\0': + *os << "\\0"; + break; + case L'\'': + *os << "\\'"; + break; + case L'\\': + *os << "\\\\"; + break; + case L'\a': + *os << "\\a"; + break; + case L'\b': + *os << "\\b"; + break; + case L'\f': + *os << "\\f"; + break; + case L'\n': + *os << "\\n"; + break; + case L'\r': + *os << "\\r"; + break; + case L'\t': + *os << "\\t"; + break; + case L'\v': + *os << "\\v"; + break; + default: + if (IsPrintableAscii(u_c)) { + *os << static_cast<char>(c); + return kAsIs; + } else { + ostream::fmtflags flags = os->flags(); + *os << "\\x" << std::hex << std::uppercase << static_cast<int>(u_c); + os->flags(flags); + return kHexEscape; + } + } + return kSpecialEscape; +} + +// Prints a char32_t c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(char32_t c, ostream* os) { + switch (c) { + case L'\'': + *os << "'"; + return kAsIs; + case L'"': + *os << "\\\""; + return kSpecialEscape; + default: + return PrintAsCharLiteralTo(c, os); + } +} + +static const char* GetCharWidthPrefix(char) { + return ""; +} + +static const char* GetCharWidthPrefix(signed char) { + return ""; +} + +static const char* GetCharWidthPrefix(unsigned char) { + return ""; +} + +#ifdef __cpp_char8_t +static const char* GetCharWidthPrefix(char8_t) { + return "u8"; +} +#endif + +static const char* GetCharWidthPrefix(char16_t) { + return "u"; +} + +static const char* GetCharWidthPrefix(char32_t) { + return "U"; +} + +static const char* GetCharWidthPrefix(wchar_t) { + return "L"; +} + +// Prints a char c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(char c, ostream* os) { + return PrintAsStringLiteralTo(ToChar32(c), os); +} + +#ifdef __cpp_char8_t +static CharFormat PrintAsStringLiteralTo(char8_t c, ostream* os) { + return PrintAsStringLiteralTo(ToChar32(c), os); +} +#endif + +static CharFormat PrintAsStringLiteralTo(char16_t c, ostream* os) { + return PrintAsStringLiteralTo(ToChar32(c), os); +} + +static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) { + return PrintAsStringLiteralTo(ToChar32(c), os); +} + +// Prints a character c (of type char, char8_t, char16_t, char32_t, or wchar_t) +// and its code. '\0' is printed as "'\\0'", other unprintable characters are +// also properly escaped using the standard C++ escape sequence. +template <typename Char> +void PrintCharAndCodeTo(Char c, ostream* os) { + // First, print c as a literal in the most readable form we can find. + *os << GetCharWidthPrefix(c) << "'"; + const CharFormat format = PrintAsCharLiteralTo(c, os); + *os << "'"; + + // To aid user debugging, we also print c's code in decimal, unless + // it's 0 (in which case c was printed as '\\0', making the code + // obvious). + if (c == 0) + return; + *os << " (" << static_cast<int>(c); + + // For more convenience, we print c's code again in hexadecimal, + // unless c was already printed in the form '\x##' or the code is in + // [1, 9]. + if (format == kHexEscape || (1 <= c && c <= 9)) { + // Do nothing. + } else { + *os << ", 0x" << String::FormatHexInt(static_cast<int>(c)); + } + *os << ")"; +} + +void PrintTo(unsigned char c, ::std::ostream* os) { PrintCharAndCodeTo(c, os); } +void PrintTo(signed char c, ::std::ostream* os) { PrintCharAndCodeTo(c, os); } + +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its code. L'\0' is printed as "L'\\0'". +void PrintTo(wchar_t wc, ostream* os) { PrintCharAndCodeTo(wc, os); } + +// TODO(dcheng): Consider making this delegate to PrintCharAndCodeTo() as well. +void PrintTo(char32_t c, ::std::ostream* os) { + *os << std::hex << "U+" << std::uppercase << std::setfill('0') << std::setw(4) + << static_cast<uint32_t>(c); +} + +// Prints the given array of characters to the ostream. CharType must be either +// char, char8_t, char16_t, char32_t, or wchar_t. +// The array starts at begin, the length is len, it may include '\0' characters +// and may not be NUL-terminated. +template <typename CharType> +GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +static CharFormat PrintCharsAsStringTo( + const CharType* begin, size_t len, ostream* os) { + const char* const quote_prefix = GetCharWidthPrefix(*begin); + *os << quote_prefix << "\""; + bool is_previous_hex = false; + CharFormat print_format = kAsIs; + for (size_t index = 0; index < len; ++index) { + const CharType cur = begin[index]; + if (is_previous_hex && IsXDigit(cur)) { + // Previous character is of '\x..' form and this character can be + // interpreted as another hexadecimal digit in its number. Break string to + // disambiguate. + *os << "\" " << quote_prefix << "\""; + } + is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape; + // Remember if any characters required hex escaping. + if (is_previous_hex) { + print_format = kHexEscape; + } + } + *os << "\""; + return print_format; +} + +// Prints a (const) char/wchar_t array of 'len' elements, starting at address +// 'begin'. CharType must be either char or wchar_t. +template <typename CharType> +GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +static void UniversalPrintCharArray( + const CharType* begin, size_t len, ostream* os) { + // The code + // const char kFoo[] = "foo"; + // generates an array of 4, not 3, elements, with the last one being '\0'. + // + // Therefore when printing a char array, we don't print the last element if + // it's '\0', such that the output matches the string literal as it's + // written in the source code. + if (len > 0 && begin[len - 1] == '\0') { + PrintCharsAsStringTo(begin, len - 1, os); + return; + } + + // If, however, the last element in the array is not '\0', e.g. + // const char kFoo[] = { 'f', 'o', 'o' }; + // we must print the entire array. We also print a message to indicate + // that the array is not NUL-terminated. + PrintCharsAsStringTo(begin, len, os); + *os << " (no terminating NUL)"; +} + +// Prints a (const) char array of 'len' elements, starting at address 'begin'. +void UniversalPrintArray(const char* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +#ifdef __cpp_char8_t +// Prints a (const) char8_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const char8_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} +#endif + +// Prints a (const) char16_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const char16_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints a (const) char32_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const char32_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints a (const) wchar_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +namespace { + +// Prints a null-terminated C-style string to the ostream. +template <typename Char> +void PrintCStringTo(const Char* s, ostream* os) { + if (s == nullptr) { + *os << "NULL"; + } else { + *os << ImplicitCast_<const void*>(s) << " pointing to "; + PrintCharsAsStringTo(s, std::char_traits<Char>::length(s), os); + } +} + +} // anonymous namespace + +void PrintTo(const char* s, ostream* os) { PrintCStringTo(s, os); } + +#ifdef __cpp_char8_t +void PrintTo(const char8_t* s, ostream* os) { PrintCStringTo(s, os); } +#endif + +void PrintTo(const char16_t* s, ostream* os) { PrintCStringTo(s, os); } + +void PrintTo(const char32_t* s, ostream* os) { PrintCStringTo(s, os); } + +// MSVC compiler can be configured to define whar_t as a typedef +// of unsigned short. Defining an overload for const wchar_t* in that case +// would cause pointers to unsigned shorts be printed as wide strings, +// possibly accessing more memory than intended and causing invalid +// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when +// wchar_t is implemented as a native type. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Prints the given wide C string to the ostream. +void PrintTo(const wchar_t* s, ostream* os) { PrintCStringTo(s, os); } +#endif // wchar_t is native + +namespace { + +bool ContainsUnprintableControlCodes(const char* str, size_t length) { + const unsigned char *s = reinterpret_cast<const unsigned char *>(str); + + for (size_t i = 0; i < length; i++) { + unsigned char ch = *s++; + if (std::iscntrl(ch)) { + switch (ch) { + case '\t': + case '\n': + case '\r': + break; + default: + return true; + } + } + } + return false; +} + +bool IsUTF8TrailByte(unsigned char t) { return 0x80 <= t && t<= 0xbf; } + +bool IsValidUTF8(const char* str, size_t length) { + const unsigned char *s = reinterpret_cast<const unsigned char *>(str); + + for (size_t i = 0; i < length;) { + unsigned char lead = s[i++]; + + if (lead <= 0x7f) { + continue; // single-byte character (ASCII) 0..7F + } + if (lead < 0xc2) { + return false; // trail byte or non-shortest form + } else if (lead <= 0xdf && (i + 1) <= length && IsUTF8TrailByte(s[i])) { + ++i; // 2-byte character + } else if (0xe0 <= lead && lead <= 0xef && (i + 2) <= length && + IsUTF8TrailByte(s[i]) && + IsUTF8TrailByte(s[i + 1]) && + // check for non-shortest form and surrogate + (lead != 0xe0 || s[i] >= 0xa0) && + (lead != 0xed || s[i] < 0xa0)) { + i += 2; // 3-byte character + } else if (0xf0 <= lead && lead <= 0xf4 && (i + 3) <= length && + IsUTF8TrailByte(s[i]) && + IsUTF8TrailByte(s[i + 1]) && + IsUTF8TrailByte(s[i + 2]) && + // check for non-shortest form + (lead != 0xf0 || s[i] >= 0x90) && + (lead != 0xf4 || s[i] < 0x90)) { + i += 3; // 4-byte character + } else { + return false; + } + } + return true; +} + +void ConditionalPrintAsText(const char* str, size_t length, ostream* os) { + if (!ContainsUnprintableControlCodes(str, length) && + IsValidUTF8(str, length)) { + *os << "\n As Text: \"" << str << "\""; + } +} + +} // anonymous namespace + +void PrintStringTo(const ::std::string& s, ostream* os) { + if (PrintCharsAsStringTo(s.data(), s.size(), os) == kHexEscape) { + if (GTEST_FLAG(print_utf8)) { + ConditionalPrintAsText(s.data(), s.size(), os); + } + } +} + +#ifdef __cpp_char8_t +void PrintU8StringTo(const ::std::u8string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif + +void PrintU16StringTo(const ::std::u16string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} + +void PrintU32StringTo(const ::std::u32string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} + +#if GTEST_HAS_STD_WSTRING +void PrintWideStringTo(const ::std::wstring& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_STD_WSTRING + +} // namespace internal + +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// +// The Google C++ Testing and Mocking Framework (Google Test) + + + +namespace testing { + +using internal::GetUnitTestImpl; + +// Gets the summary of the failure message by omitting the stack trace +// in it. +std::string TestPartResult::ExtractSummary(const char* message) { + const char* const stack_trace = strstr(message, internal::kStackTraceMarker); + return stack_trace == nullptr ? message : std::string(message, stack_trace); +} + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result) { + return os << internal::FormatFileLocation(result.file_name(), + result.line_number()) + << " " + << (result.type() == TestPartResult::kSuccess + ? "Success" + : result.type() == TestPartResult::kSkip + ? "Skipped" + : result.type() == TestPartResult::kFatalFailure + ? "Fatal failure" + : "Non-fatal failure") + << ":\n" + << result.message() << std::endl; +} + +// Appends a TestPartResult to the array. +void TestPartResultArray::Append(const TestPartResult& result) { + array_.push_back(result); +} + +// Returns the TestPartResult at the given index (0-based). +const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const { + if (index < 0 || index >= size()) { + printf("\nInvalid index (%d) into TestPartResultArray.\n", index); + internal::posix::Abort(); + } + + return array_[static_cast<size_t>(index)]; +} + +// Returns the number of TestPartResult objects in the array. +int TestPartResultArray::size() const { + return static_cast<int>(array_.size()); +} + +namespace internal { + +HasNewFatalFailureHelper::HasNewFatalFailureHelper() + : has_new_fatal_failure_(false), + original_reporter_(GetUnitTestImpl()-> + GetTestPartResultReporterForCurrentThread()) { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this); +} + +HasNewFatalFailureHelper::~HasNewFatalFailureHelper() { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread( + original_reporter_); +} + +void HasNewFatalFailureHelper::ReportTestPartResult( + const TestPartResult& result) { + if (result.fatally_failed()) + has_new_fatal_failure_ = true; + original_reporter_->ReportTestPartResult(result); +} + +} // namespace internal + +} // namespace testing +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + + + +namespace testing { +namespace internal { + +// Skips to the first non-space char in str. Returns an empty string if str +// contains only whitespace characters. +static const char* SkipSpaces(const char* str) { + while (IsSpace(*str)) + str++; + return str; +} + +static std::vector<std::string> SplitIntoTestNames(const char* src) { + std::vector<std::string> name_vec; + src = SkipSpaces(src); + for (; src != nullptr; src = SkipComma(src)) { + name_vec.push_back(StripTrailingSpaces(GetPrefixUntilComma(src))); + } + return name_vec; +} + +// Verifies that registered_tests match the test names in +// registered_tests_; returns registered_tests if successful, or +// aborts the program otherwise. +const char* TypedTestSuitePState::VerifyRegisteredTestNames( + const char* test_suite_name, const char* file, int line, + const char* registered_tests) { + RegisterTypeParameterizedTestSuite(test_suite_name, CodeLocation(file, line)); + + typedef RegisteredTestsMap::const_iterator RegisteredTestIter; + registered_ = true; + + std::vector<std::string> name_vec = SplitIntoTestNames(registered_tests); + + Message errors; + + std::set<std::string> tests; + for (std::vector<std::string>::const_iterator name_it = name_vec.begin(); + name_it != name_vec.end(); ++name_it) { + const std::string& name = *name_it; + if (tests.count(name) != 0) { + errors << "Test " << name << " is listed more than once.\n"; + continue; + } + + if (registered_tests_.count(name) != 0) { + tests.insert(name); + } else { + errors << "No test named " << name + << " can be found in this test suite.\n"; + } + } + + for (RegisteredTestIter it = registered_tests_.begin(); + it != registered_tests_.end(); + ++it) { + if (tests.count(it->first) == 0) { + errors << "You forgot to list test " << it->first << ".\n"; + } + } + + const std::string& errors_str = errors.GetString(); + if (errors_str != "") { + fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), + errors_str.c_str()); + fflush(stderr); + posix::Abort(); + } + + return registered_tests; +} + +} // namespace internal +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// +// Google C++ Mocking Framework (Google Mock) +// +// This file #includes all Google Mock implementation .cc files. The +// purpose is to allow a user to build Google Mock by compiling this +// file alone. + +// This line ensures that gmock.h can be compiled on its own, even +// when it's fused. +#include "gmock/gmock.h" + +// The following lines pull in the real gmock *.cc files. +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements cardinalities. + + +#include <limits.h> +#include <ostream> // NOLINT +#include <sstream> +#include <string> + +namespace testing { + +namespace { + +// Implements the Between(m, n) cardinality. +class BetweenCardinalityImpl : public CardinalityInterface { + public: + BetweenCardinalityImpl(int min, int max) + : min_(min >= 0 ? min : 0), + max_(max >= min_ ? max : min_) { + std::stringstream ss; + if (min < 0) { + ss << "The invocation lower bound must be >= 0, " + << "but is actually " << min << "."; + internal::Expect(false, __FILE__, __LINE__, ss.str()); + } else if (max < 0) { + ss << "The invocation upper bound must be >= 0, " + << "but is actually " << max << "."; + internal::Expect(false, __FILE__, __LINE__, ss.str()); + } else if (min > max) { + ss << "The invocation upper bound (" << max + << ") must be >= the invocation lower bound (" << min + << ")."; + internal::Expect(false, __FILE__, __LINE__, ss.str()); + } + } + + // Conservative estimate on the lower/upper bound of the number of + // calls allowed. + int ConservativeLowerBound() const override { return min_; } + int ConservativeUpperBound() const override { return max_; } + + bool IsSatisfiedByCallCount(int call_count) const override { + return min_ <= call_count && call_count <= max_; + } + + bool IsSaturatedByCallCount(int call_count) const override { + return call_count >= max_; + } + + void DescribeTo(::std::ostream* os) const override; + + private: + const int min_; + const int max_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(BetweenCardinalityImpl); +}; + +// Formats "n times" in a human-friendly way. +inline std::string FormatTimes(int n) { + if (n == 1) { + return "once"; + } else if (n == 2) { + return "twice"; + } else { + std::stringstream ss; + ss << n << " times"; + return ss.str(); + } +} + +// Describes the Between(m, n) cardinality in human-friendly text. +void BetweenCardinalityImpl::DescribeTo(::std::ostream* os) const { + if (min_ == 0) { + if (max_ == 0) { + *os << "never called"; + } else if (max_ == INT_MAX) { + *os << "called any number of times"; + } else { + *os << "called at most " << FormatTimes(max_); + } + } else if (min_ == max_) { + *os << "called " << FormatTimes(min_); + } else if (max_ == INT_MAX) { + *os << "called at least " << FormatTimes(min_); + } else { + // 0 < min_ < max_ < INT_MAX + *os << "called between " << min_ << " and " << max_ << " times"; + } +} + +} // Unnamed namespace + +// Describes the given call count to an ostream. +void Cardinality::DescribeActualCallCountTo(int actual_call_count, + ::std::ostream* os) { + if (actual_call_count > 0) { + *os << "called " << FormatTimes(actual_call_count); + } else { + *os << "never called"; + } +} + +// Creates a cardinality that allows at least n calls. +GTEST_API_ Cardinality AtLeast(int n) { return Between(n, INT_MAX); } + +// Creates a cardinality that allows at most n calls. +GTEST_API_ Cardinality AtMost(int n) { return Between(0, n); } + +// Creates a cardinality that allows any number of calls. +GTEST_API_ Cardinality AnyNumber() { return AtLeast(0); } + +// Creates a cardinality that allows between min and max calls. +GTEST_API_ Cardinality Between(int min, int max) { + return Cardinality(new BetweenCardinalityImpl(min, max)); +} + +// Creates a cardinality that allows exactly n calls. +GTEST_API_ Cardinality Exactly(int n) { return Between(n, n); } + +} // namespace testing +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Google Mock - a framework for writing C++ mock classes. +// +// This file defines some utilities useful for implementing Google +// Mock. They are subject to change without notice, so please DO NOT +// USE THEM IN USER CODE. + + +#include <ctype.h> +#include <ostream> // NOLINT +#include <string> + +namespace testing { +namespace internal { + +// Joins a vector of strings as if they are fields of a tuple; returns +// the joined string. +GTEST_API_ std::string JoinAsTuple(const Strings& fields) { + switch (fields.size()) { + case 0: + return ""; + case 1: + return fields[0]; + default: + std::string result = "(" + fields[0]; + for (size_t i = 1; i < fields.size(); i++) { + result += ", "; + result += fields[i]; + } + result += ")"; + return result; + } +} + +// Converts an identifier name to a space-separated list of lower-case +// words. Each maximum substring of the form [A-Za-z][a-z]*|\d+ is +// treated as one word. For example, both "FooBar123" and +// "foo_bar_123" are converted to "foo bar 123". +GTEST_API_ std::string ConvertIdentifierNameToWords(const char* id_name) { + std::string result; + char prev_char = '\0'; + for (const char* p = id_name; *p != '\0'; prev_char = *(p++)) { + // We don't care about the current locale as the input is + // guaranteed to be a valid C++ identifier name. + const bool starts_new_word = IsUpper(*p) || + (!IsAlpha(prev_char) && IsLower(*p)) || + (!IsDigit(prev_char) && IsDigit(*p)); + + if (IsAlNum(*p)) { + if (starts_new_word && result != "") + result += ' '; + result += ToLower(*p); + } + } + return result; +} + +// This class reports Google Mock failures as Google Test failures. A +// user can define another class in a similar fashion if they intend to +// use Google Mock with a testing framework other than Google Test. +class GoogleTestFailureReporter : public FailureReporterInterface { + public: + void ReportFailure(FailureType type, const char* file, int line, + const std::string& message) override { + AssertHelper(type == kFatal ? + TestPartResult::kFatalFailure : + TestPartResult::kNonFatalFailure, + file, + line, + message.c_str()) = Message(); + if (type == kFatal) { + posix::Abort(); + } + } +}; + +// Returns the global failure reporter. Will create a +// GoogleTestFailureReporter and return it the first time called. +GTEST_API_ FailureReporterInterface* GetFailureReporter() { + // Points to the global failure reporter used by Google Mock. gcc + // guarantees that the following use of failure_reporter is + // thread-safe. We may need to add additional synchronization to + // protect failure_reporter if we port Google Mock to other + // compilers. + static FailureReporterInterface* const failure_reporter = + new GoogleTestFailureReporter(); + return failure_reporter; +} + +// Protects global resources (stdout in particular) used by Log(). +static GTEST_DEFINE_STATIC_MUTEX_(g_log_mutex); + +// Returns true if and only if a log with the given severity is visible +// according to the --gmock_verbose flag. +GTEST_API_ bool LogIsVisible(LogSeverity severity) { + if (GMOCK_FLAG(verbose) == kInfoVerbosity) { + // Always show the log if --gmock_verbose=info. + return true; + } else if (GMOCK_FLAG(verbose) == kErrorVerbosity) { + // Always hide it if --gmock_verbose=error. + return false; + } else { + // If --gmock_verbose is neither "info" nor "error", we treat it + // as "warning" (its default value). + return severity == kWarning; + } +} + +// Prints the given message to stdout if and only if 'severity' >= the level +// specified by the --gmock_verbose flag. If stack_frames_to_skip >= +// 0, also prints the stack trace excluding the top +// stack_frames_to_skip frames. In opt mode, any positive +// stack_frames_to_skip is treated as 0, since we don't know which +// function calls will be inlined by the compiler and need to be +// conservative. +GTEST_API_ void Log(LogSeverity severity, const std::string& message, + int stack_frames_to_skip) { + if (!LogIsVisible(severity)) + return; + + // Ensures that logs from different threads don't interleave. + MutexLock l(&g_log_mutex); + + if (severity == kWarning) { + // Prints a GMOCK WARNING marker to make the warnings easily searchable. + std::cout << "\nGMOCK WARNING:"; + } + // Pre-pends a new-line to message if it doesn't start with one. + if (message.empty() || message[0] != '\n') { + std::cout << "\n"; + } + std::cout << message; + if (stack_frames_to_skip >= 0) { +#ifdef NDEBUG + // In opt mode, we have to be conservative and skip no stack frame. + const int actual_to_skip = 0; +#else + // In dbg mode, we can do what the caller tell us to do (plus one + // for skipping this function's stack frame). + const int actual_to_skip = stack_frames_to_skip + 1; +#endif // NDEBUG + + // Appends a new-line to message if it doesn't end with one. + if (!message.empty() && *message.rbegin() != '\n') { + std::cout << "\n"; + } + std::cout << "Stack trace:\n" + << ::testing::internal::GetCurrentOsStackTraceExceptTop( + ::testing::UnitTest::GetInstance(), actual_to_skip); + } + std::cout << ::std::flush; +} + +GTEST_API_ WithoutMatchers GetWithoutMatchers() { return WithoutMatchers(); } + +GTEST_API_ void IllegalDoDefault(const char* file, int line) { + internal::Assert( + false, file, line, + "You are using DoDefault() inside a composite action like " + "DoAll() or WithArgs(). This is not supported for technical " + "reasons. Please instead spell out the default action, or " + "assign the default action to an Action variable and use " + "the variable in various places."); +} + +} // namespace internal +} // namespace testing +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements Matcher<const string&>, Matcher<string>, and +// utilities for defining matchers. + + +#include <string.h> +#include <iostream> +#include <sstream> +#include <string> + +namespace testing { +namespace internal { + +// Returns the description for a matcher defined using the MATCHER*() +// macro where the user-supplied description string is "", if +// 'negation' is false; otherwise returns the description of the +// negation of the matcher. 'param_values' contains a list of strings +// that are the print-out of the matcher's parameters. +GTEST_API_ std::string FormatMatcherDescription(bool negation, + const char* matcher_name, + const Strings& param_values) { + std::string result = ConvertIdentifierNameToWords(matcher_name); + if (param_values.size() >= 1) result += " " + JoinAsTuple(param_values); + return negation ? "not (" + result + ")" : result; +} + +// FindMaxBipartiteMatching and its helper class. +// +// Uses the well-known Ford-Fulkerson max flow method to find a maximum +// bipartite matching. Flow is considered to be from left to right. +// There is an implicit source node that is connected to all of the left +// nodes, and an implicit sink node that is connected to all of the +// right nodes. All edges have unit capacity. +// +// Neither the flow graph nor the residual flow graph are represented +// explicitly. Instead, they are implied by the information in 'graph' and +// a vector<int> called 'left_' whose elements are initialized to the +// value kUnused. This represents the initial state of the algorithm, +// where the flow graph is empty, and the residual flow graph has the +// following edges: +// - An edge from source to each left_ node +// - An edge from each right_ node to sink +// - An edge from each left_ node to each right_ node, if the +// corresponding edge exists in 'graph'. +// +// When the TryAugment() method adds a flow, it sets left_[l] = r for some +// nodes l and r. This induces the following changes: +// - The edges (source, l), (l, r), and (r, sink) are added to the +// flow graph. +// - The same three edges are removed from the residual flow graph. +// - The reverse edges (l, source), (r, l), and (sink, r) are added +// to the residual flow graph, which is a directional graph +// representing unused flow capacity. +// +// When the method augments a flow (moving left_[l] from some r1 to some +// other r2), this can be thought of as "undoing" the above steps with +// respect to r1 and "redoing" them with respect to r2. +// +// It bears repeating that the flow graph and residual flow graph are +// never represented explicitly, but can be derived by looking at the +// information in 'graph' and in left_. +// +// As an optimization, there is a second vector<int> called right_ which +// does not provide any new information. Instead, it enables more +// efficient queries about edges entering or leaving the right-side nodes +// of the flow or residual flow graphs. The following invariants are +// maintained: +// +// left[l] == kUnused or right[left[l]] == l +// right[r] == kUnused or left[right[r]] == r +// +// . [ source ] . +// . ||| . +// . ||| . +// . ||\--> left[0]=1 ---\ right[0]=-1 ----\ . +// . || | | . +// . |\---> left[1]=-1 \--> right[1]=0 ---\| . +// . | || . +// . \----> left[2]=2 ------> right[2]=2 --\|| . +// . ||| . +// . elements matchers vvv . +// . [ sink ] . +// +// See Also: +// [1] Cormen, et al (2001). "Section 26.2: The Ford-Fulkerson method". +// "Introduction to Algorithms (Second ed.)", pp. 651-664. +// [2] "Ford-Fulkerson algorithm", Wikipedia, +// 'http://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm' +class MaxBipartiteMatchState { + public: + explicit MaxBipartiteMatchState(const MatchMatrix& graph) + : graph_(&graph), + left_(graph_->LhsSize(), kUnused), + right_(graph_->RhsSize(), kUnused) {} + + // Returns the edges of a maximal match, each in the form {left, right}. + ElementMatcherPairs Compute() { + // 'seen' is used for path finding { 0: unseen, 1: seen }. + ::std::vector<char> seen; + // Searches the residual flow graph for a path from each left node to + // the sink in the residual flow graph, and if one is found, add flow + // to the graph. It's okay to search through the left nodes once. The + // edge from the implicit source node to each previously-visited left + // node will have flow if that left node has any path to the sink + // whatsoever. Subsequent augmentations can only add flow to the + // network, and cannot take away that previous flow unit from the source. + // Since the source-to-left edge can only carry one flow unit (or, + // each element can be matched to only one matcher), there is no need + // to visit the left nodes more than once looking for augmented paths. + // The flow is known to be possible or impossible by looking at the + // node once. + for (size_t ilhs = 0; ilhs < graph_->LhsSize(); ++ilhs) { + // Reset the path-marking vector and try to find a path from + // source to sink starting at the left_[ilhs] node. + GTEST_CHECK_(left_[ilhs] == kUnused) + << "ilhs: " << ilhs << ", left_[ilhs]: " << left_[ilhs]; + // 'seen' initialized to 'graph_->RhsSize()' copies of 0. + seen.assign(graph_->RhsSize(), 0); + TryAugment(ilhs, &seen); + } + ElementMatcherPairs result; + for (size_t ilhs = 0; ilhs < left_.size(); ++ilhs) { + size_t irhs = left_[ilhs]; + if (irhs == kUnused) continue; + result.push_back(ElementMatcherPair(ilhs, irhs)); + } + return result; + } + + private: + static const size_t kUnused = static_cast<size_t>(-1); + + // Perform a depth-first search from left node ilhs to the sink. If a + // path is found, flow is added to the network by linking the left and + // right vector elements corresponding each segment of the path. + // Returns true if a path to sink was found, which means that a unit of + // flow was added to the network. The 'seen' vector elements correspond + // to right nodes and are marked to eliminate cycles from the search. + // + // Left nodes will only be explored at most once because they + // are accessible from at most one right node in the residual flow + // graph. + // + // Note that left_[ilhs] is the only element of left_ that TryAugment will + // potentially transition from kUnused to another value. Any other + // left_ element holding kUnused before TryAugment will be holding it + // when TryAugment returns. + // + bool TryAugment(size_t ilhs, ::std::vector<char>* seen) { + for (size_t irhs = 0; irhs < graph_->RhsSize(); ++irhs) { + if ((*seen)[irhs]) continue; + if (!graph_->HasEdge(ilhs, irhs)) continue; + // There's an available edge from ilhs to irhs. + (*seen)[irhs] = 1; + // Next a search is performed to determine whether + // this edge is a dead end or leads to the sink. + // + // right_[irhs] == kUnused means that there is residual flow from + // right node irhs to the sink, so we can use that to finish this + // flow path and return success. + // + // Otherwise there is residual flow to some ilhs. We push flow + // along that path and call ourselves recursively to see if this + // ultimately leads to sink. + if (right_[irhs] == kUnused || TryAugment(right_[irhs], seen)) { + // Add flow from left_[ilhs] to right_[irhs]. + left_[ilhs] = irhs; + right_[irhs] = ilhs; + return true; + } + } + return false; + } + + const MatchMatrix* graph_; // not owned + // Each element of the left_ vector represents a left hand side node + // (i.e. an element) and each element of right_ is a right hand side + // node (i.e. a matcher). The values in the left_ vector indicate + // outflow from that node to a node on the right_ side. The values + // in the right_ indicate inflow, and specify which left_ node is + // feeding that right_ node, if any. For example, left_[3] == 1 means + // there's a flow from element #3 to matcher #1. Such a flow would also + // be redundantly represented in the right_ vector as right_[1] == 3. + // Elements of left_ and right_ are either kUnused or mutually + // referent. Mutually referent means that left_[right_[i]] = i and + // right_[left_[i]] = i. + ::std::vector<size_t> left_; + ::std::vector<size_t> right_; +}; + +const size_t MaxBipartiteMatchState::kUnused; + +GTEST_API_ ElementMatcherPairs FindMaxBipartiteMatching(const MatchMatrix& g) { + return MaxBipartiteMatchState(g).Compute(); +} + +static void LogElementMatcherPairVec(const ElementMatcherPairs& pairs, + ::std::ostream* stream) { + typedef ElementMatcherPairs::const_iterator Iter; + ::std::ostream& os = *stream; + os << "{"; + const char* sep = ""; + for (Iter it = pairs.begin(); it != pairs.end(); ++it) { + os << sep << "\n (" + << "element #" << it->first << ", " + << "matcher #" << it->second << ")"; + sep = ","; + } + os << "\n}"; +} + +bool MatchMatrix::NextGraph() { + for (size_t ilhs = 0; ilhs < LhsSize(); ++ilhs) { + for (size_t irhs = 0; irhs < RhsSize(); ++irhs) { + char& b = matched_[SpaceIndex(ilhs, irhs)]; + if (!b) { + b = 1; + return true; + } + b = 0; + } + } + return false; +} + +void MatchMatrix::Randomize() { + for (size_t ilhs = 0; ilhs < LhsSize(); ++ilhs) { + for (size_t irhs = 0; irhs < RhsSize(); ++irhs) { + char& b = matched_[SpaceIndex(ilhs, irhs)]; + b = static_cast<char>(rand() & 1); // NOLINT + } + } +} + +std::string MatchMatrix::DebugString() const { + ::std::stringstream ss; + const char* sep = ""; + for (size_t i = 0; i < LhsSize(); ++i) { + ss << sep; + for (size_t j = 0; j < RhsSize(); ++j) { + ss << HasEdge(i, j); + } + sep = ";"; + } + return ss.str(); +} + +void UnorderedElementsAreMatcherImplBase::DescribeToImpl( + ::std::ostream* os) const { + switch (match_flags()) { + case UnorderedMatcherRequire::ExactMatch: + if (matcher_describers_.empty()) { + *os << "is empty"; + return; + } + if (matcher_describers_.size() == 1) { + *os << "has " << Elements(1) << " and that element "; + matcher_describers_[0]->DescribeTo(os); + return; + } + *os << "has " << Elements(matcher_describers_.size()) + << " and there exists some permutation of elements such that:\n"; + break; + case UnorderedMatcherRequire::Superset: + *os << "a surjection from elements to requirements exists such that:\n"; + break; + case UnorderedMatcherRequire::Subset: + *os << "an injection from elements to requirements exists such that:\n"; + break; + } + + const char* sep = ""; + for (size_t i = 0; i != matcher_describers_.size(); ++i) { + *os << sep; + if (match_flags() == UnorderedMatcherRequire::ExactMatch) { + *os << " - element #" << i << " "; + } else { + *os << " - an element "; + } + matcher_describers_[i]->DescribeTo(os); + if (match_flags() == UnorderedMatcherRequire::ExactMatch) { + sep = ", and\n"; + } else { + sep = "\n"; + } + } +} + +void UnorderedElementsAreMatcherImplBase::DescribeNegationToImpl( + ::std::ostream* os) const { + switch (match_flags()) { + case UnorderedMatcherRequire::ExactMatch: + if (matcher_describers_.empty()) { + *os << "isn't empty"; + return; + } + if (matcher_describers_.size() == 1) { + *os << "doesn't have " << Elements(1) << ", or has " << Elements(1) + << " that "; + matcher_describers_[0]->DescribeNegationTo(os); + return; + } + *os << "doesn't have " << Elements(matcher_describers_.size()) + << ", or there exists no permutation of elements such that:\n"; + break; + case UnorderedMatcherRequire::Superset: + *os << "no surjection from elements to requirements exists such that:\n"; + break; + case UnorderedMatcherRequire::Subset: + *os << "no injection from elements to requirements exists such that:\n"; + break; + } + const char* sep = ""; + for (size_t i = 0; i != matcher_describers_.size(); ++i) { + *os << sep; + if (match_flags() == UnorderedMatcherRequire::ExactMatch) { + *os << " - element #" << i << " "; + } else { + *os << " - an element "; + } + matcher_describers_[i]->DescribeTo(os); + if (match_flags() == UnorderedMatcherRequire::ExactMatch) { + sep = ", and\n"; + } else { + sep = "\n"; + } + } +} + +// Checks that all matchers match at least one element, and that all +// elements match at least one matcher. This enables faster matching +// and better error reporting. +// Returns false, writing an explanation to 'listener', if and only +// if the success criteria are not met. +bool UnorderedElementsAreMatcherImplBase::VerifyMatchMatrix( + const ::std::vector<std::string>& element_printouts, + const MatchMatrix& matrix, MatchResultListener* listener) const { + bool result = true; + ::std::vector<char> element_matched(matrix.LhsSize(), 0); + ::std::vector<char> matcher_matched(matrix.RhsSize(), 0); + + for (size_t ilhs = 0; ilhs < matrix.LhsSize(); ilhs++) { + for (size_t irhs = 0; irhs < matrix.RhsSize(); irhs++) { + char matched = matrix.HasEdge(ilhs, irhs); + element_matched[ilhs] |= matched; + matcher_matched[irhs] |= matched; + } + } + + if (match_flags() & UnorderedMatcherRequire::Superset) { + const char* sep = + "where the following matchers don't match any elements:\n"; + for (size_t mi = 0; mi < matcher_matched.size(); ++mi) { + if (matcher_matched[mi]) continue; + result = false; + if (listener->IsInterested()) { + *listener << sep << "matcher #" << mi << ": "; + matcher_describers_[mi]->DescribeTo(listener->stream()); + sep = ",\n"; + } + } + } + + if (match_flags() & UnorderedMatcherRequire::Subset) { + const char* sep = + "where the following elements don't match any matchers:\n"; + const char* outer_sep = ""; + if (!result) { + outer_sep = "\nand "; + } + for (size_t ei = 0; ei < element_matched.size(); ++ei) { + if (element_matched[ei]) continue; + result = false; + if (listener->IsInterested()) { + *listener << outer_sep << sep << "element #" << ei << ": " + << element_printouts[ei]; + sep = ",\n"; + outer_sep = ""; + } + } + } + return result; +} + +bool UnorderedElementsAreMatcherImplBase::FindPairing( + const MatchMatrix& matrix, MatchResultListener* listener) const { + ElementMatcherPairs matches = FindMaxBipartiteMatching(matrix); + + size_t max_flow = matches.size(); + if ((match_flags() & UnorderedMatcherRequire::Superset) && + max_flow < matrix.RhsSize()) { + if (listener->IsInterested()) { + *listener << "where no permutation of the elements can satisfy all " + "matchers, and the closest match is " + << max_flow << " of " << matrix.RhsSize() + << " matchers with the pairings:\n"; + LogElementMatcherPairVec(matches, listener->stream()); + } + return false; + } + if ((match_flags() & UnorderedMatcherRequire::Subset) && + max_flow < matrix.LhsSize()) { + if (listener->IsInterested()) { + *listener + << "where not all elements can be matched, and the closest match is " + << max_flow << " of " << matrix.RhsSize() + << " matchers with the pairings:\n"; + LogElementMatcherPairVec(matches, listener->stream()); + } + return false; + } + + if (matches.size() > 1) { + if (listener->IsInterested()) { + const char* sep = "where:\n"; + for (size_t mi = 0; mi < matches.size(); ++mi) { + *listener << sep << " - element #" << matches[mi].first + << " is matched by matcher #" << matches[mi].second; + sep = ",\n"; + } + } + } + return true; +} + +} // namespace internal +} // namespace testing +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements the spec builder syntax (ON_CALL and +// EXPECT_CALL). + + +#include <stdlib.h> + +#include <iostream> // NOLINT +#include <map> +#include <memory> +#include <set> +#include <string> +#include <vector> + + +#if GTEST_OS_CYGWIN || GTEST_OS_LINUX || GTEST_OS_MAC +# include <unistd.h> // NOLINT +#endif + +// Silence C4800 (C4800: 'int *const ': forcing value +// to bool 'true' or 'false') for MSVC 15 +#ifdef _MSC_VER +#if _MSC_VER == 1900 +# pragma warning(push) +# pragma warning(disable:4800) +#endif +#endif + +namespace testing { +namespace internal { + +// Protects the mock object registry (in class Mock), all function +// mockers, and all expectations. +GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_gmock_mutex); + +// Logs a message including file and line number information. +GTEST_API_ void LogWithLocation(testing::internal::LogSeverity severity, + const char* file, int line, + const std::string& message) { + ::std::ostringstream s; + s << internal::FormatFileLocation(file, line) << " " << message + << ::std::endl; + Log(severity, s.str(), 0); +} + +// Constructs an ExpectationBase object. +ExpectationBase::ExpectationBase(const char* a_file, int a_line, + const std::string& a_source_text) + : file_(a_file), + line_(a_line), + source_text_(a_source_text), + cardinality_specified_(false), + cardinality_(Exactly(1)), + call_count_(0), + retired_(false), + extra_matcher_specified_(false), + repeated_action_specified_(false), + retires_on_saturation_(false), + last_clause_(kNone), + action_count_checked_(false) {} + +// Destructs an ExpectationBase object. +ExpectationBase::~ExpectationBase() {} + +// Explicitly specifies the cardinality of this expectation. Used by +// the subclasses to implement the .Times() clause. +void ExpectationBase::SpecifyCardinality(const Cardinality& a_cardinality) { + cardinality_specified_ = true; + cardinality_ = a_cardinality; +} + +// Retires all pre-requisites of this expectation. +void ExpectationBase::RetireAllPreRequisites() + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + if (is_retired()) { + // We can take this short-cut as we never retire an expectation + // until we have retired all its pre-requisites. + return; + } + + ::std::vector<ExpectationBase*> expectations(1, this); + while (!expectations.empty()) { + ExpectationBase* exp = expectations.back(); + expectations.pop_back(); + + for (ExpectationSet::const_iterator it = + exp->immediate_prerequisites_.begin(); + it != exp->immediate_prerequisites_.end(); ++it) { + ExpectationBase* next = it->expectation_base().get(); + if (!next->is_retired()) { + next->Retire(); + expectations.push_back(next); + } + } + } +} + +// Returns true if and only if all pre-requisites of this expectation +// have been satisfied. +bool ExpectationBase::AllPrerequisitesAreSatisfied() const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + ::std::vector<const ExpectationBase*> expectations(1, this); + while (!expectations.empty()) { + const ExpectationBase* exp = expectations.back(); + expectations.pop_back(); + + for (ExpectationSet::const_iterator it = + exp->immediate_prerequisites_.begin(); + it != exp->immediate_prerequisites_.end(); ++it) { + const ExpectationBase* next = it->expectation_base().get(); + if (!next->IsSatisfied()) return false; + expectations.push_back(next); + } + } + return true; +} + +// Adds unsatisfied pre-requisites of this expectation to 'result'. +void ExpectationBase::FindUnsatisfiedPrerequisites(ExpectationSet* result) const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + ::std::vector<const ExpectationBase*> expectations(1, this); + while (!expectations.empty()) { + const ExpectationBase* exp = expectations.back(); + expectations.pop_back(); + + for (ExpectationSet::const_iterator it = + exp->immediate_prerequisites_.begin(); + it != exp->immediate_prerequisites_.end(); ++it) { + const ExpectationBase* next = it->expectation_base().get(); + + if (next->IsSatisfied()) { + // If *it is satisfied and has a call count of 0, some of its + // pre-requisites may not be satisfied yet. + if (next->call_count_ == 0) { + expectations.push_back(next); + } + } else { + // Now that we know next is unsatisfied, we are not so interested + // in whether its pre-requisites are satisfied. Therefore we + // don't iterate into it here. + *result += *it; + } + } + } +} + +// Describes how many times a function call matching this +// expectation has occurred. +void ExpectationBase::DescribeCallCountTo(::std::ostream* os) const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + + // Describes how many times the function is expected to be called. + *os << " Expected: to be "; + cardinality().DescribeTo(os); + *os << "\n Actual: "; + Cardinality::DescribeActualCallCountTo(call_count(), os); + + // Describes the state of the expectation (e.g. is it satisfied? + // is it active?). + *os << " - " << (IsOverSaturated() ? "over-saturated" : + IsSaturated() ? "saturated" : + IsSatisfied() ? "satisfied" : "unsatisfied") + << " and " + << (is_retired() ? "retired" : "active"); +} + +// Checks the action count (i.e. the number of WillOnce() and +// WillRepeatedly() clauses) against the cardinality if this hasn't +// been done before. Prints a warning if there are too many or too +// few actions. +void ExpectationBase::CheckActionCountIfNotDone() const + GTEST_LOCK_EXCLUDED_(mutex_) { + bool should_check = false; + { + MutexLock l(&mutex_); + if (!action_count_checked_) { + action_count_checked_ = true; + should_check = true; + } + } + + if (should_check) { + if (!cardinality_specified_) { + // The cardinality was inferred - no need to check the action + // count against it. + return; + } + + // The cardinality was explicitly specified. + const int action_count = static_cast<int>(untyped_actions_.size()); + const int upper_bound = cardinality().ConservativeUpperBound(); + const int lower_bound = cardinality().ConservativeLowerBound(); + bool too_many; // True if there are too many actions, or false + // if there are too few. + if (action_count > upper_bound || + (action_count == upper_bound && repeated_action_specified_)) { + too_many = true; + } else if (0 < action_count && action_count < lower_bound && + !repeated_action_specified_) { + too_many = false; + } else { + return; + } + + ::std::stringstream ss; + DescribeLocationTo(&ss); + ss << "Too " << (too_many ? "many" : "few") + << " actions specified in " << source_text() << "...\n" + << "Expected to be "; + cardinality().DescribeTo(&ss); + ss << ", but has " << (too_many ? "" : "only ") + << action_count << " WillOnce()" + << (action_count == 1 ? "" : "s"); + if (repeated_action_specified_) { + ss << " and a WillRepeatedly()"; + } + ss << "."; + Log(kWarning, ss.str(), -1); // -1 means "don't print stack trace". + } +} + +// Implements the .Times() clause. +void ExpectationBase::UntypedTimes(const Cardinality& a_cardinality) { + if (last_clause_ == kTimes) { + ExpectSpecProperty(false, + ".Times() cannot appear " + "more than once in an EXPECT_CALL()."); + } else { + ExpectSpecProperty(last_clause_ < kTimes, + ".Times() cannot appear after " + ".InSequence(), .WillOnce(), .WillRepeatedly(), " + "or .RetiresOnSaturation()."); + } + last_clause_ = kTimes; + + SpecifyCardinality(a_cardinality); +} + +// Points to the implicit sequence introduced by a living InSequence +// object (if any) in the current thread or NULL. +GTEST_API_ ThreadLocal<Sequence*> g_gmock_implicit_sequence; + +// Reports an uninteresting call (whose description is in msg) in the +// manner specified by 'reaction'. +void ReportUninterestingCall(CallReaction reaction, const std::string& msg) { + // Include a stack trace only if --gmock_verbose=info is specified. + const int stack_frames_to_skip = + GMOCK_FLAG(verbose) == kInfoVerbosity ? 3 : -1; + switch (reaction) { + case kAllow: + Log(kInfo, msg, stack_frames_to_skip); + break; + case kWarn: + Log(kWarning, + msg + + "\nNOTE: You can safely ignore the above warning unless this " + "call should not happen. Do not suppress it by blindly adding " + "an EXPECT_CALL() if you don't mean to enforce the call. " + "See " + "https://github.com/google/googletest/blob/master/docs/" + "gmock_cook_book.md#" + "knowing-when-to-expect for details.\n", + stack_frames_to_skip); + break; + default: // FAIL + Expect(false, nullptr, -1, msg); + } +} + +UntypedFunctionMockerBase::UntypedFunctionMockerBase() + : mock_obj_(nullptr), name_("") {} + +UntypedFunctionMockerBase::~UntypedFunctionMockerBase() {} + +// Sets the mock object this mock method belongs to, and registers +// this information in the global mock registry. Will be called +// whenever an EXPECT_CALL() or ON_CALL() is executed on this mock +// method. +void UntypedFunctionMockerBase::RegisterOwner(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + { + MutexLock l(&g_gmock_mutex); + mock_obj_ = mock_obj; + } + Mock::Register(mock_obj, this); +} + +// Sets the mock object this mock method belongs to, and sets the name +// of the mock function. Will be called upon each invocation of this +// mock function. +void UntypedFunctionMockerBase::SetOwnerAndName(const void* mock_obj, + const char* name) + GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + // We protect name_ under g_gmock_mutex in case this mock function + // is called from two threads concurrently. + MutexLock l(&g_gmock_mutex); + mock_obj_ = mock_obj; + name_ = name; +} + +// Returns the name of the function being mocked. Must be called +// after RegisterOwner() or SetOwnerAndName() has been called. +const void* UntypedFunctionMockerBase::MockObject() const + GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + const void* mock_obj; + { + // We protect mock_obj_ under g_gmock_mutex in case this mock + // function is called from two threads concurrently. + MutexLock l(&g_gmock_mutex); + Assert(mock_obj_ != nullptr, __FILE__, __LINE__, + "MockObject() must not be called before RegisterOwner() or " + "SetOwnerAndName() has been called."); + mock_obj = mock_obj_; + } + return mock_obj; +} + +// Returns the name of this mock method. Must be called after +// SetOwnerAndName() has been called. +const char* UntypedFunctionMockerBase::Name() const + GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + const char* name; + { + // We protect name_ under g_gmock_mutex in case this mock + // function is called from two threads concurrently. + MutexLock l(&g_gmock_mutex); + Assert(name_ != nullptr, __FILE__, __LINE__, + "Name() must not be called before SetOwnerAndName() has " + "been called."); + name = name_; + } + return name; +} + +// Calculates the result of invoking this mock function with the given +// arguments, prints it, and returns it. The caller is responsible +// for deleting the result. +UntypedActionResultHolderBase* UntypedFunctionMockerBase::UntypedInvokeWith( + void* const untyped_args) GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + // See the definition of untyped_expectations_ for why access to it + // is unprotected here. + if (untyped_expectations_.size() == 0) { + // No expectation is set on this mock method - we have an + // uninteresting call. + + // We must get Google Mock's reaction on uninteresting calls + // made on this mock object BEFORE performing the action, + // because the action may DELETE the mock object and make the + // following expression meaningless. + const CallReaction reaction = + Mock::GetReactionOnUninterestingCalls(MockObject()); + + // True if and only if we need to print this call's arguments and return + // value. This definition must be kept in sync with + // the behavior of ReportUninterestingCall(). + const bool need_to_report_uninteresting_call = + // If the user allows this uninteresting call, we print it + // only when they want informational messages. + reaction == kAllow ? LogIsVisible(kInfo) : + // If the user wants this to be a warning, we print + // it only when they want to see warnings. + reaction == kWarn + ? LogIsVisible(kWarning) + : + // Otherwise, the user wants this to be an error, and we + // should always print detailed information in the error. + true; + + if (!need_to_report_uninteresting_call) { + // Perform the action without printing the call information. + return this->UntypedPerformDefaultAction( + untyped_args, "Function call: " + std::string(Name())); + } + + // Warns about the uninteresting call. + ::std::stringstream ss; + this->UntypedDescribeUninterestingCall(untyped_args, &ss); + + // Calculates the function result. + UntypedActionResultHolderBase* const result = + this->UntypedPerformDefaultAction(untyped_args, ss.str()); + + // Prints the function result. + if (result != nullptr) result->PrintAsActionResult(&ss); + + ReportUninterestingCall(reaction, ss.str()); + return result; + } + + bool is_excessive = false; + ::std::stringstream ss; + ::std::stringstream why; + ::std::stringstream loc; + const void* untyped_action = nullptr; + + // The UntypedFindMatchingExpectation() function acquires and + // releases g_gmock_mutex. + + const ExpectationBase* const untyped_expectation = + this->UntypedFindMatchingExpectation(untyped_args, &untyped_action, + &is_excessive, &ss, &why); + const bool found = untyped_expectation != nullptr; + + // True if and only if we need to print the call's arguments + // and return value. + // This definition must be kept in sync with the uses of Expect() + // and Log() in this function. + const bool need_to_report_call = + !found || is_excessive || LogIsVisible(kInfo); + if (!need_to_report_call) { + // Perform the action without printing the call information. + return untyped_action == nullptr + ? this->UntypedPerformDefaultAction(untyped_args, "") + : this->UntypedPerformAction(untyped_action, untyped_args); + } + + ss << " Function call: " << Name(); + this->UntypedPrintArgs(untyped_args, &ss); + + // In case the action deletes a piece of the expectation, we + // generate the message beforehand. + if (found && !is_excessive) { + untyped_expectation->DescribeLocationTo(&loc); + } + + UntypedActionResultHolderBase* result = nullptr; + + auto perform_action = [&] { + return untyped_action == nullptr + ? this->UntypedPerformDefaultAction(untyped_args, ss.str()) + : this->UntypedPerformAction(untyped_action, untyped_args); + }; + auto handle_failures = [&] { + ss << "\n" << why.str(); + + if (!found) { + // No expectation matches this call - reports a failure. + Expect(false, nullptr, -1, ss.str()); + } else if (is_excessive) { + // We had an upper-bound violation and the failure message is in ss. + Expect(false, untyped_expectation->file(), untyped_expectation->line(), + ss.str()); + } else { + // We had an expected call and the matching expectation is + // described in ss. + Log(kInfo, loc.str() + ss.str(), 2); + } + }; +#if GTEST_HAS_EXCEPTIONS + try { + result = perform_action(); + } catch (...) { + handle_failures(); + throw; + } +#else + result = perform_action(); +#endif + + if (result != nullptr) result->PrintAsActionResult(&ss); + handle_failures(); + return result; +} + +// Returns an Expectation object that references and co-owns exp, +// which must be an expectation on this mock function. +Expectation UntypedFunctionMockerBase::GetHandleOf(ExpectationBase* exp) { + // See the definition of untyped_expectations_ for why access to it + // is unprotected here. + for (UntypedExpectations::const_iterator it = + untyped_expectations_.begin(); + it != untyped_expectations_.end(); ++it) { + if (it->get() == exp) { + return Expectation(*it); + } + } + + Assert(false, __FILE__, __LINE__, "Cannot find expectation."); + return Expectation(); + // The above statement is just to make the code compile, and will + // never be executed. +} + +// Verifies that all expectations on this mock function have been +// satisfied. Reports one or more Google Test non-fatal failures +// and returns false if not. +bool UntypedFunctionMockerBase::VerifyAndClearExpectationsLocked() + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + bool expectations_met = true; + for (UntypedExpectations::const_iterator it = + untyped_expectations_.begin(); + it != untyped_expectations_.end(); ++it) { + ExpectationBase* const untyped_expectation = it->get(); + if (untyped_expectation->IsOverSaturated()) { + // There was an upper-bound violation. Since the error was + // already reported when it occurred, there is no need to do + // anything here. + expectations_met = false; + } else if (!untyped_expectation->IsSatisfied()) { + expectations_met = false; + ::std::stringstream ss; + ss << "Actual function call count doesn't match " + << untyped_expectation->source_text() << "...\n"; + // No need to show the source file location of the expectation + // in the description, as the Expect() call that follows already + // takes care of it. + untyped_expectation->MaybeDescribeExtraMatcherTo(&ss); + untyped_expectation->DescribeCallCountTo(&ss); + Expect(false, untyped_expectation->file(), + untyped_expectation->line(), ss.str()); + } + } + + // Deleting our expectations may trigger other mock objects to be deleted, for + // example if an action contains a reference counted smart pointer to that + // mock object, and that is the last reference. So if we delete our + // expectations within the context of the global mutex we may deadlock when + // this method is called again. Instead, make a copy of the set of + // expectations to delete, clear our set within the mutex, and then clear the + // copied set outside of it. + UntypedExpectations expectations_to_delete; + untyped_expectations_.swap(expectations_to_delete); + + g_gmock_mutex.Unlock(); + expectations_to_delete.clear(); + g_gmock_mutex.Lock(); + + return expectations_met; +} + +CallReaction intToCallReaction(int mock_behavior) { + if (mock_behavior >= kAllow && mock_behavior <= kFail) { + return static_cast<internal::CallReaction>(mock_behavior); + } + return kWarn; +} + +} // namespace internal + +// Class Mock. + +namespace { + +typedef std::set<internal::UntypedFunctionMockerBase*> FunctionMockers; + +// The current state of a mock object. Such information is needed for +// detecting leaked mock objects and explicitly verifying a mock's +// expectations. +struct MockObjectState { + MockObjectState() + : first_used_file(nullptr), first_used_line(-1), leakable(false) {} + + // Where in the source file an ON_CALL or EXPECT_CALL is first + // invoked on this mock object. + const char* first_used_file; + int first_used_line; + ::std::string first_used_test_suite; + ::std::string first_used_test; + bool leakable; // true if and only if it's OK to leak the object. + FunctionMockers function_mockers; // All registered methods of the object. +}; + +// A global registry holding the state of all mock objects that are +// alive. A mock object is added to this registry the first time +// Mock::AllowLeak(), ON_CALL(), or EXPECT_CALL() is called on it. It +// is removed from the registry in the mock object's destructor. +class MockObjectRegistry { + public: + // Maps a mock object (identified by its address) to its state. + typedef std::map<const void*, MockObjectState> StateMap; + + // This destructor will be called when a program exits, after all + // tests in it have been run. By then, there should be no mock + // object alive. Therefore we report any living object as test + // failure, unless the user explicitly asked us to ignore it. + ~MockObjectRegistry() { + if (!GMOCK_FLAG(catch_leaked_mocks)) + return; + + int leaked_count = 0; + for (StateMap::const_iterator it = states_.begin(); it != states_.end(); + ++it) { + if (it->second.leakable) // The user said it's fine to leak this object. + continue; + + // FIXME: Print the type of the leaked object. + // This can help the user identify the leaked object. + std::cout << "\n"; + const MockObjectState& state = it->second; + std::cout << internal::FormatFileLocation(state.first_used_file, + state.first_used_line); + std::cout << " ERROR: this mock object"; + if (state.first_used_test != "") { + std::cout << " (used in test " << state.first_used_test_suite << "." + << state.first_used_test << ")"; + } + std::cout << " should be deleted but never is. Its address is @" + << it->first << "."; + leaked_count++; + } + if (leaked_count > 0) { + std::cout << "\nERROR: " << leaked_count << " leaked mock " + << (leaked_count == 1 ? "object" : "objects") + << " found at program exit. Expectations on a mock object are " + "verified when the object is destructed. Leaking a mock " + "means that its expectations aren't verified, which is " + "usually a test bug. If you really intend to leak a mock, " + "you can suppress this error using " + "testing::Mock::AllowLeak(mock_object), or you may use a " + "fake or stub instead of a mock.\n"; + std::cout.flush(); + ::std::cerr.flush(); + // RUN_ALL_TESTS() has already returned when this destructor is + // called. Therefore we cannot use the normal Google Test + // failure reporting mechanism. + _exit(1); // We cannot call exit() as it is not reentrant and + // may already have been called. + } + } + + StateMap& states() { return states_; } + + private: + StateMap states_; +}; + +// Protected by g_gmock_mutex. +MockObjectRegistry g_mock_object_registry; + +// Maps a mock object to the reaction Google Mock should have when an +// uninteresting method is called. Protected by g_gmock_mutex. +std::map<const void*, internal::CallReaction> g_uninteresting_call_reaction; + +// Sets the reaction Google Mock should have when an uninteresting +// method of the given mock object is called. +void SetReactionOnUninterestingCalls(const void* mock_obj, + internal::CallReaction reaction) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + g_uninteresting_call_reaction[mock_obj] = reaction; +} + +} // namespace + +// Tells Google Mock to allow uninteresting calls on the given mock +// object. +void Mock::AllowUninterestingCalls(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + SetReactionOnUninterestingCalls(mock_obj, internal::kAllow); +} + +// Tells Google Mock to warn the user about uninteresting calls on the +// given mock object. +void Mock::WarnUninterestingCalls(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + SetReactionOnUninterestingCalls(mock_obj, internal::kWarn); +} + +// Tells Google Mock to fail uninteresting calls on the given mock +// object. +void Mock::FailUninterestingCalls(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + SetReactionOnUninterestingCalls(mock_obj, internal::kFail); +} + +// Tells Google Mock the given mock object is being destroyed and its +// entry in the call-reaction table should be removed. +void Mock::UnregisterCallReaction(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + g_uninteresting_call_reaction.erase(mock_obj); +} + +// Returns the reaction Google Mock will have on uninteresting calls +// made on the given mock object. +internal::CallReaction Mock::GetReactionOnUninterestingCalls( + const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + return (g_uninteresting_call_reaction.count(mock_obj) == 0) ? + internal::intToCallReaction(GMOCK_FLAG(default_mock_behavior)) : + g_uninteresting_call_reaction[mock_obj]; +} + +// Tells Google Mock to ignore mock_obj when checking for leaked mock +// objects. +void Mock::AllowLeak(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + g_mock_object_registry.states()[mock_obj].leakable = true; +} + +// Verifies and clears all expectations on the given mock object. If +// the expectations aren't satisfied, generates one or more Google +// Test non-fatal failures and returns false. +bool Mock::VerifyAndClearExpectations(void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + return VerifyAndClearExpectationsLocked(mock_obj); +} + +// Verifies all expectations on the given mock object and clears its +// default actions and expectations. Returns true if and only if the +// verification was successful. +bool Mock::VerifyAndClear(void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + ClearDefaultActionsLocked(mock_obj); + return VerifyAndClearExpectationsLocked(mock_obj); +} + +// Verifies and clears all expectations on the given mock object. If +// the expectations aren't satisfied, generates one or more Google +// Test non-fatal failures and returns false. +bool Mock::VerifyAndClearExpectationsLocked(void* mock_obj) + GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex) { + internal::g_gmock_mutex.AssertHeld(); + if (g_mock_object_registry.states().count(mock_obj) == 0) { + // No EXPECT_CALL() was set on the given mock object. + return true; + } + + // Verifies and clears the expectations on each mock method in the + // given mock object. + bool expectations_met = true; + FunctionMockers& mockers = + g_mock_object_registry.states()[mock_obj].function_mockers; + for (FunctionMockers::const_iterator it = mockers.begin(); + it != mockers.end(); ++it) { + if (!(*it)->VerifyAndClearExpectationsLocked()) { + expectations_met = false; + } + } + + // We don't clear the content of mockers, as they may still be + // needed by ClearDefaultActionsLocked(). + return expectations_met; +} + +bool Mock::IsNaggy(void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + return Mock::GetReactionOnUninterestingCalls(mock_obj) == internal::kWarn; +} +bool Mock::IsNice(void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + return Mock::GetReactionOnUninterestingCalls(mock_obj) == internal::kAllow; +} +bool Mock::IsStrict(void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + return Mock::GetReactionOnUninterestingCalls(mock_obj) == internal::kFail; +} + +// Registers a mock object and a mock method it owns. +void Mock::Register(const void* mock_obj, + internal::UntypedFunctionMockerBase* mocker) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + g_mock_object_registry.states()[mock_obj].function_mockers.insert(mocker); +} + +// Tells Google Mock where in the source code mock_obj is used in an +// ON_CALL or EXPECT_CALL. In case mock_obj is leaked, this +// information helps the user identify which object it is. +void Mock::RegisterUseByOnCallOrExpectCall(const void* mock_obj, + const char* file, int line) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) { + internal::MutexLock l(&internal::g_gmock_mutex); + MockObjectState& state = g_mock_object_registry.states()[mock_obj]; + if (state.first_used_file == nullptr) { + state.first_used_file = file; + state.first_used_line = line; + const TestInfo* const test_info = + UnitTest::GetInstance()->current_test_info(); + if (test_info != nullptr) { + state.first_used_test_suite = test_info->test_suite_name(); + state.first_used_test = test_info->name(); + } + } +} + +// Unregisters a mock method; removes the owning mock object from the +// registry when the last mock method associated with it has been +// unregistered. This is called only in the destructor of +// FunctionMockerBase. +void Mock::UnregisterLocked(internal::UntypedFunctionMockerBase* mocker) + GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex) { + internal::g_gmock_mutex.AssertHeld(); + for (MockObjectRegistry::StateMap::iterator it = + g_mock_object_registry.states().begin(); + it != g_mock_object_registry.states().end(); ++it) { + FunctionMockers& mockers = it->second.function_mockers; + if (mockers.erase(mocker) > 0) { + // mocker was in mockers and has been just removed. + if (mockers.empty()) { + g_mock_object_registry.states().erase(it); + } + return; + } + } +} + +// Clears all ON_CALL()s set on the given mock object. +void Mock::ClearDefaultActionsLocked(void* mock_obj) + GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex) { + internal::g_gmock_mutex.AssertHeld(); + + if (g_mock_object_registry.states().count(mock_obj) == 0) { + // No ON_CALL() was set on the given mock object. + return; + } + + // Clears the default actions for each mock method in the given mock + // object. + FunctionMockers& mockers = + g_mock_object_registry.states()[mock_obj].function_mockers; + for (FunctionMockers::const_iterator it = mockers.begin(); + it != mockers.end(); ++it) { + (*it)->ClearDefaultActionsLocked(); + } + + // We don't clear the content of mockers, as they may still be + // needed by VerifyAndClearExpectationsLocked(). +} + +Expectation::Expectation() {} + +Expectation::Expectation( + const std::shared_ptr<internal::ExpectationBase>& an_expectation_base) + : expectation_base_(an_expectation_base) {} + +Expectation::~Expectation() {} + +// Adds an expectation to a sequence. +void Sequence::AddExpectation(const Expectation& expectation) const { + if (*last_expectation_ != expectation) { + if (last_expectation_->expectation_base() != nullptr) { + expectation.expectation_base()->immediate_prerequisites_ + += *last_expectation_; + } + *last_expectation_ = expectation; + } +} + +// Creates the implicit sequence if there isn't one. +InSequence::InSequence() { + if (internal::g_gmock_implicit_sequence.get() == nullptr) { + internal::g_gmock_implicit_sequence.set(new Sequence); + sequence_created_ = true; + } else { + sequence_created_ = false; + } +} + +// Deletes the implicit sequence if it was created by the constructor +// of this object. +InSequence::~InSequence() { + if (sequence_created_) { + delete internal::g_gmock_implicit_sequence.get(); + internal::g_gmock_implicit_sequence.set(nullptr); + } +} + +} // namespace testing + +#ifdef _MSC_VER +#if _MSC_VER == 1900 +# pragma warning(pop) +#endif +#endif +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + + +namespace testing { + +GMOCK_DEFINE_bool_(catch_leaked_mocks, true, + "true if and only if Google Mock should report leaked " + "mock objects as failures."); + +GMOCK_DEFINE_string_(verbose, internal::kWarningVerbosity, + "Controls how verbose Google Mock's output is." + " Valid values:\n" + " info - prints all messages.\n" + " warning - prints warnings and errors.\n" + " error - prints errors only."); + +GMOCK_DEFINE_int32_(default_mock_behavior, 1, + "Controls the default behavior of mocks." + " Valid values:\n" + " 0 - by default, mocks act as NiceMocks.\n" + " 1 - by default, mocks act as NaggyMocks.\n" + " 2 - by default, mocks act as StrictMocks."); + +namespace internal { + +// Parses a string as a command line flag. The string should have the +// format "--gmock_flag=value". When def_optional is true, the +// "=value" part can be omitted. +// +// Returns the value of the flag, or NULL if the parsing failed. +static const char* ParseGoogleMockFlagValue(const char* str, + const char* flag, + bool def_optional) { + // str and flag must not be NULL. + if (str == nullptr || flag == nullptr) return nullptr; + + // The flag must start with "--gmock_". + const std::string flag_str = std::string("--gmock_") + flag; + const size_t flag_len = flag_str.length(); + if (strncmp(str, flag_str.c_str(), flag_len) != 0) return nullptr; + + // Skips the flag name. + const char* flag_end = str + flag_len; + + // When def_optional is true, it's OK to not have a "=value" part. + if (def_optional && (flag_end[0] == '\0')) { + return flag_end; + } + + // If def_optional is true and there are more characters after the + // flag name, or if def_optional is false, there must be a '=' after + // the flag name. + if (flag_end[0] != '=') return nullptr; + + // Returns the string after "=". + return flag_end + 1; +} + +// Parses a string for a Google Mock bool flag, in the form of +// "--gmock_flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +static bool ParseGoogleMockBoolFlag(const char* str, const char* flag, + bool* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseGoogleMockFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == nullptr) return false; + + // Converts the string value to a bool. + *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + return true; +} + +// Parses a string for a Google Mock string flag, in the form of +// "--gmock_flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +template <typename String> +static bool ParseGoogleMockStringFlag(const char* str, const char* flag, + String* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseGoogleMockFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == nullptr) return false; + + // Sets *value to the value of the flag. + *value = value_str; + return true; +} + +static bool ParseGoogleMockIntFlag(const char* str, const char* flag, + int32_t* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseGoogleMockFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == nullptr) return false; + + // Sets *value to the value of the flag. + return ParseInt32(Message() << "The value of flag --" << flag, + value_str, value); +} + +// The internal implementation of InitGoogleMock(). +// +// The type parameter CharType can be instantiated to either char or +// wchar_t. +template <typename CharType> +void InitGoogleMockImpl(int* argc, CharType** argv) { + // Makes sure Google Test is initialized. InitGoogleTest() is + // idempotent, so it's fine if the user has already called it. + InitGoogleTest(argc, argv); + if (*argc <= 0) return; + + for (int i = 1; i != *argc; i++) { + const std::string arg_string = StreamableToString(argv[i]); + const char* const arg = arg_string.c_str(); + + // Do we see a Google Mock flag? + if (ParseGoogleMockBoolFlag(arg, "catch_leaked_mocks", + &GMOCK_FLAG(catch_leaked_mocks)) || + ParseGoogleMockStringFlag(arg, "verbose", &GMOCK_FLAG(verbose)) || + ParseGoogleMockIntFlag(arg, "default_mock_behavior", + &GMOCK_FLAG(default_mock_behavior))) { + // Yes. Shift the remainder of the argv list left by one. Note + // that argv has (*argc + 1) elements, the last one always being + // NULL. The following loop moves the trailing NULL element as + // well. + for (int j = i; j != *argc; j++) { + argv[j] = argv[j + 1]; + } + + // Decrements the argument count. + (*argc)--; + + // We also need to decrement the iterator as we just removed + // an element. + i--; + } + } +} + +} // namespace internal + +// Initializes Google Mock. This must be called before running the +// tests. In particular, it parses a command line for the flags that +// Google Mock recognizes. Whenever a Google Mock flag is seen, it is +// removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Mock flag variables are +// updated. +// +// Since Google Test is needed for Google Mock to work, this function +// also initializes Google Test and parses its flags, if that hasn't +// been done. +GTEST_API_ void InitGoogleMock(int* argc, char** argv) { + internal::InitGoogleMockImpl(argc, argv); +} + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +GTEST_API_ void InitGoogleMock(int* argc, wchar_t** argv) { + internal::InitGoogleMockImpl(argc, argv); +} + +// This overloaded version can be used on Arduino/embedded platforms where +// there is no argc/argv. +GTEST_API_ void InitGoogleMock() { + // Since Arduino doesn't have a command line, fake out the argc/argv arguments + int argc = 1; + const auto arg0 = "dummy"; + char* argv0 = const_cast<char*>(arg0); + char** argv = &argv0; + + internal::InitGoogleMockImpl(&argc, argv); +} + +} // namespace testing diff --git a/src/fmt/test/gtest/gmock/gmock.h b/src/fmt/test/gtest/gmock/gmock.h new file mode 100644 index 000000000..d121f7df2 --- /dev/null +++ b/src/fmt/test/gtest/gmock/gmock.h @@ -0,0 +1,11645 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Google Mock - a framework for writing C++ mock classes. +// +// This is the main header file a user should include. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_H_ + +// This file implements the following syntax: +// +// ON_CALL(mock_object, Method(...)) +// .With(...) ? +// .WillByDefault(...); +// +// where With() is optional and WillByDefault() must appear exactly +// once. +// +// EXPECT_CALL(mock_object, Method(...)) +// .With(...) ? +// .Times(...) ? +// .InSequence(...) * +// .WillOnce(...) * +// .WillRepeatedly(...) ? +// .RetiresOnSaturation() ? ; +// +// where all clauses are optional and WillOnce() can be repeated. + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Google Mock - a framework for writing C++ mock classes. +// +// The ACTION* family of macros can be used in a namespace scope to +// define custom actions easily. The syntax: +// +// ACTION(name) { statements; } +// +// will define an action with the given name that executes the +// statements. The value returned by the statements will be used as +// the return value of the action. Inside the statements, you can +// refer to the K-th (0-based) argument of the mock function by +// 'argK', and refer to its type by 'argK_type'. For example: +// +// ACTION(IncrementArg1) { +// arg1_type temp = arg1; +// return ++(*temp); +// } +// +// allows you to write +// +// ...WillOnce(IncrementArg1()); +// +// You can also refer to the entire argument tuple and its type by +// 'args' and 'args_type', and refer to the mock function type and its +// return type by 'function_type' and 'return_type'. +// +// Note that you don't need to specify the types of the mock function +// arguments. However rest assured that your code is still type-safe: +// you'll get a compiler error if *arg1 doesn't support the ++ +// operator, or if the type of ++(*arg1) isn't compatible with the +// mock function's return type, for example. +// +// Sometimes you'll want to parameterize the action. For that you can use +// another macro: +// +// ACTION_P(name, param_name) { statements; } +// +// For example: +// +// ACTION_P(Add, n) { return arg0 + n; } +// +// will allow you to write: +// +// ...WillOnce(Add(5)); +// +// Note that you don't need to provide the type of the parameter +// either. If you need to reference the type of a parameter named +// 'foo', you can write 'foo_type'. For example, in the body of +// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type +// of 'n'. +// +// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P10 to support +// multi-parameter actions. +// +// For the purpose of typing, you can view +// +// ACTION_Pk(Foo, p1, ..., pk) { ... } +// +// as shorthand for +// +// template <typename p1_type, ..., typename pk_type> +// FooActionPk<p1_type, ..., pk_type> Foo(p1_type p1, ..., pk_type pk) { ... } +// +// In particular, you can provide the template type arguments +// explicitly when invoking Foo(), as in Foo<long, bool>(5, false); +// although usually you can rely on the compiler to infer the types +// for you automatically. You can assign the result of expression +// Foo(p1, ..., pk) to a variable of type FooActionPk<p1_type, ..., +// pk_type>. This can be useful when composing actions. +// +// You can also overload actions with different numbers of parameters: +// +// ACTION_P(Plus, a) { ... } +// ACTION_P2(Plus, a, b) { ... } +// +// While it's tempting to always use the ACTION* macros when defining +// a new action, you should also consider implementing ActionInterface +// or using MakePolymorphicAction() instead, especially if you need to +// use the action a lot. While these approaches require more work, +// they give you more control on the types of the mock function +// arguments and the action parameters, which in general leads to +// better compiler error messages that pay off in the long run. They +// also allow overloading actions based on parameter types (as opposed +// to just based on the number of parameters). +// +// CAVEAT: +// +// ACTION*() can only be used in a namespace scope as templates cannot be +// declared inside of a local class. +// Users can, however, define any local functors (e.g. a lambda) that +// can be used as actions. +// +// MORE INFORMATION: +// +// To learn more about using these macros, please search for 'ACTION' on +// https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ + +#ifndef _WIN32_WCE +# include <errno.h> +#endif + +#include <algorithm> +#include <functional> +#include <memory> +#include <string> +#include <tuple> +#include <type_traits> +#include <utility> + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Google Mock - a framework for writing C++ mock classes. +// +// This file defines some utilities useful for implementing Google +// Mock. They are subject to change without notice, so please DO NOT +// USE THEM IN USER CODE. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_ + +#include <stdio.h> +#include <ostream> // NOLINT +#include <string> +#include <type_traits> +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// +// Low-level types and utilities for porting Google Mock to various +// platforms. All macros ending with _ and symbols defined in an +// internal namespace are subject to change without notice. Code +// outside Google Mock MUST NOT USE THEM DIRECTLY. Macros that don't +// end with _ are part of Google Mock's public API and can be used by +// code outside Google Mock. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_ + +#include <assert.h> +#include <stdlib.h> +#include <cstdint> +#include <iostream> + +// Most of the utilities needed for porting Google Mock are also +// required for Google Test and are defined in gtest-port.h. +// +// Note to maintainers: to reduce code duplication, prefer adding +// portability utilities to Google Test's gtest-port.h instead of +// here, as Google Mock depends on Google Test. Only add a utility +// here if it's truly specific to Google Mock. + +#include "gtest/gtest.h" +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_ + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_ + +// For MS Visual C++, check the compiler version. At least VS 2015 is +// required to compile Google Mock. +#if defined(_MSC_VER) && _MSC_VER < 1900 +# error "At least Visual C++ 2015 (14.0) is required to compile Google Mock." +#endif + +// Macro for referencing flags. This is public as we want the user to +// use this syntax to reference Google Mock flags. +#define GMOCK_FLAG(name) FLAGS_gmock_##name + +#if !defined(GMOCK_DECLARE_bool_) + +// Macros for declaring flags. +# define GMOCK_DECLARE_bool_(name) extern GTEST_API_ bool GMOCK_FLAG(name) +# define GMOCK_DECLARE_int32_(name) extern GTEST_API_ int32_t GMOCK_FLAG(name) +# define GMOCK_DECLARE_string_(name) \ + extern GTEST_API_ ::std::string GMOCK_FLAG(name) + +// Macros for defining flags. +# define GMOCK_DEFINE_bool_(name, default_val, doc) \ + GTEST_API_ bool GMOCK_FLAG(name) = (default_val) +# define GMOCK_DEFINE_int32_(name, default_val, doc) \ + GTEST_API_ int32_t GMOCK_FLAG(name) = (default_val) +# define GMOCK_DEFINE_string_(name, default_val, doc) \ + GTEST_API_ ::std::string GMOCK_FLAG(name) = (default_val) + +#endif // !defined(GMOCK_DECLARE_bool_) + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_ + +namespace testing { + +template <typename> +class Matcher; + +namespace internal { + +// Silence MSVC C4100 (unreferenced formal parameter) and +// C4805('==': unsafe mix of type 'const int' and type 'const bool') +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +# pragma warning(disable:4805) +#endif + +// Joins a vector of strings as if they are fields of a tuple; returns +// the joined string. +GTEST_API_ std::string JoinAsTuple(const Strings& fields); + +// Converts an identifier name to a space-separated list of lower-case +// words. Each maximum substring of the form [A-Za-z][a-z]*|\d+ is +// treated as one word. For example, both "FooBar123" and +// "foo_bar_123" are converted to "foo bar 123". +GTEST_API_ std::string ConvertIdentifierNameToWords(const char* id_name); + +// GetRawPointer(p) returns the raw pointer underlying p when p is a +// smart pointer, or returns p itself when p is already a raw pointer. +// The following default implementation is for the smart pointer case. +template <typename Pointer> +inline const typename Pointer::element_type* GetRawPointer(const Pointer& p) { + return p.get(); +} +// This overloaded version is for the raw pointer case. +template <typename Element> +inline Element* GetRawPointer(Element* p) { return p; } + +// MSVC treats wchar_t as a native type usually, but treats it as the +// same as unsigned short when the compiler option /Zc:wchar_t- is +// specified. It defines _NATIVE_WCHAR_T_DEFINED symbol when wchar_t +// is a native type. +#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) +// wchar_t is a typedef. +#else +# define GMOCK_WCHAR_T_IS_NATIVE_ 1 +#endif + +// In what follows, we use the term "kind" to indicate whether a type +// is bool, an integer type (excluding bool), a floating-point type, +// or none of them. This categorization is useful for determining +// when a matcher argument type can be safely converted to another +// type in the implementation of SafeMatcherCast. +enum TypeKind { + kBool, kInteger, kFloatingPoint, kOther +}; + +// KindOf<T>::value is the kind of type T. +template <typename T> struct KindOf { + enum { value = kOther }; // The default kind. +}; + +// This macro declares that the kind of 'type' is 'kind'. +#define GMOCK_DECLARE_KIND_(type, kind) \ + template <> struct KindOf<type> { enum { value = kind }; } + +GMOCK_DECLARE_KIND_(bool, kBool); + +// All standard integer types. +GMOCK_DECLARE_KIND_(char, kInteger); +GMOCK_DECLARE_KIND_(signed char, kInteger); +GMOCK_DECLARE_KIND_(unsigned char, kInteger); +GMOCK_DECLARE_KIND_(short, kInteger); // NOLINT +GMOCK_DECLARE_KIND_(unsigned short, kInteger); // NOLINT +GMOCK_DECLARE_KIND_(int, kInteger); +GMOCK_DECLARE_KIND_(unsigned int, kInteger); +GMOCK_DECLARE_KIND_(long, kInteger); // NOLINT +GMOCK_DECLARE_KIND_(unsigned long, kInteger); // NOLINT +GMOCK_DECLARE_KIND_(long long, kInteger); // NOLINT +GMOCK_DECLARE_KIND_(unsigned long long, kInteger); // NOLINT + +#if GMOCK_WCHAR_T_IS_NATIVE_ +GMOCK_DECLARE_KIND_(wchar_t, kInteger); +#endif + +// All standard floating-point types. +GMOCK_DECLARE_KIND_(float, kFloatingPoint); +GMOCK_DECLARE_KIND_(double, kFloatingPoint); +GMOCK_DECLARE_KIND_(long double, kFloatingPoint); + +#undef GMOCK_DECLARE_KIND_ + +// Evaluates to the kind of 'type'. +#define GMOCK_KIND_OF_(type) \ + static_cast< ::testing::internal::TypeKind>( \ + ::testing::internal::KindOf<type>::value) + +// LosslessArithmeticConvertibleImpl<kFromKind, From, kToKind, To>::value +// is true if and only if arithmetic type From can be losslessly converted to +// arithmetic type To. +// +// It's the user's responsibility to ensure that both From and To are +// raw (i.e. has no CV modifier, is not a pointer, and is not a +// reference) built-in arithmetic types, kFromKind is the kind of +// From, and kToKind is the kind of To; the value is +// implementation-defined when the above pre-condition is violated. +template <TypeKind kFromKind, typename From, TypeKind kToKind, typename To> +using LosslessArithmeticConvertibleImpl = std::integral_constant< + bool, + // clang-format off + // Converting from bool is always lossless + (kFromKind == kBool) ? true + // Converting between any other type kinds will be lossy if the type + // kinds are not the same. + : (kFromKind != kToKind) ? false + : (kFromKind == kInteger && + // Converting between integers of different widths is allowed so long + // as the conversion does not go from signed to unsigned. + (((sizeof(From) < sizeof(To)) && + !(std::is_signed<From>::value && !std::is_signed<To>::value)) || + // Converting between integers of the same width only requires the + // two types to have the same signedness. + ((sizeof(From) == sizeof(To)) && + (std::is_signed<From>::value == std::is_signed<To>::value))) + ) ? true + // Floating point conversions are lossless if and only if `To` is at least + // as wide as `From`. + : (kFromKind == kFloatingPoint && (sizeof(From) <= sizeof(To))) ? true + : false + // clang-format on + >; + +// LosslessArithmeticConvertible<From, To>::value is true if and only if +// arithmetic type From can be losslessly converted to arithmetic type To. +// +// It's the user's responsibility to ensure that both From and To are +// raw (i.e. has no CV modifier, is not a pointer, and is not a +// reference) built-in arithmetic types; the value is +// implementation-defined when the above pre-condition is violated. +template <typename From, typename To> +using LosslessArithmeticConvertible = + LosslessArithmeticConvertibleImpl<GMOCK_KIND_OF_(From), From, + GMOCK_KIND_OF_(To), To>; + +// This interface knows how to report a Google Mock failure (either +// non-fatal or fatal). +class FailureReporterInterface { + public: + // The type of a failure (either non-fatal or fatal). + enum FailureType { + kNonfatal, kFatal + }; + + virtual ~FailureReporterInterface() {} + + // Reports a failure that occurred at the given source file location. + virtual void ReportFailure(FailureType type, const char* file, int line, + const std::string& message) = 0; +}; + +// Returns the failure reporter used by Google Mock. +GTEST_API_ FailureReporterInterface* GetFailureReporter(); + +// Asserts that condition is true; aborts the process with the given +// message if condition is false. We cannot use LOG(FATAL) or CHECK() +// as Google Mock might be used to mock the log sink itself. We +// inline this function to prevent it from showing up in the stack +// trace. +inline void Assert(bool condition, const char* file, int line, + const std::string& msg) { + if (!condition) { + GetFailureReporter()->ReportFailure(FailureReporterInterface::kFatal, + file, line, msg); + } +} +inline void Assert(bool condition, const char* file, int line) { + Assert(condition, file, line, "Assertion failed."); +} + +// Verifies that condition is true; generates a non-fatal failure if +// condition is false. +inline void Expect(bool condition, const char* file, int line, + const std::string& msg) { + if (!condition) { + GetFailureReporter()->ReportFailure(FailureReporterInterface::kNonfatal, + file, line, msg); + } +} +inline void Expect(bool condition, const char* file, int line) { + Expect(condition, file, line, "Expectation failed."); +} + +// Severity level of a log. +enum LogSeverity { + kInfo = 0, + kWarning = 1 +}; + +// Valid values for the --gmock_verbose flag. + +// All logs (informational and warnings) are printed. +const char kInfoVerbosity[] = "info"; +// Only warnings are printed. +const char kWarningVerbosity[] = "warning"; +// No logs are printed. +const char kErrorVerbosity[] = "error"; + +// Returns true if and only if a log with the given severity is visible +// according to the --gmock_verbose flag. +GTEST_API_ bool LogIsVisible(LogSeverity severity); + +// Prints the given message to stdout if and only if 'severity' >= the level +// specified by the --gmock_verbose flag. If stack_frames_to_skip >= +// 0, also prints the stack trace excluding the top +// stack_frames_to_skip frames. In opt mode, any positive +// stack_frames_to_skip is treated as 0, since we don't know which +// function calls will be inlined by the compiler and need to be +// conservative. +GTEST_API_ void Log(LogSeverity severity, const std::string& message, + int stack_frames_to_skip); + +// A marker class that is used to resolve parameterless expectations to the +// correct overload. This must not be instantiable, to prevent client code from +// accidentally resolving to the overload; for example: +// +// ON_CALL(mock, Method({}, nullptr))... +// +class WithoutMatchers { + private: + WithoutMatchers() {} + friend GTEST_API_ WithoutMatchers GetWithoutMatchers(); +}; + +// Internal use only: access the singleton instance of WithoutMatchers. +GTEST_API_ WithoutMatchers GetWithoutMatchers(); + +// Disable MSVC warnings for infinite recursion, since in this case the +// the recursion is unreachable. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4717) +#endif + +// Invalid<T>() is usable as an expression of type T, but will terminate +// the program with an assertion failure if actually run. This is useful +// when a value of type T is needed for compilation, but the statement +// will not really be executed (or we don't care if the statement +// crashes). +template <typename T> +inline T Invalid() { + Assert(false, "", -1, "Internal error: attempt to return invalid value"); + // This statement is unreachable, and would never terminate even if it + // could be reached. It is provided only to placate compiler warnings + // about missing return statements. + return Invalid<T>(); +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +// Given a raw type (i.e. having no top-level reference or const +// modifier) RawContainer that's either an STL-style container or a +// native array, class StlContainerView<RawContainer> has the +// following members: +// +// - type is a type that provides an STL-style container view to +// (i.e. implements the STL container concept for) RawContainer; +// - const_reference is a type that provides a reference to a const +// RawContainer; +// - ConstReference(raw_container) returns a const reference to an STL-style +// container view to raw_container, which is a RawContainer. +// - Copy(raw_container) returns an STL-style container view of a +// copy of raw_container, which is a RawContainer. +// +// This generic version is used when RawContainer itself is already an +// STL-style container. +template <class RawContainer> +class StlContainerView { + public: + typedef RawContainer type; + typedef const type& const_reference; + + static const_reference ConstReference(const RawContainer& container) { + static_assert(!std::is_const<RawContainer>::value, + "RawContainer type must not be const"); + return container; + } + static type Copy(const RawContainer& container) { return container; } +}; + +// This specialization is used when RawContainer is a native array type. +template <typename Element, size_t N> +class StlContainerView<Element[N]> { + public: + typedef typename std::remove_const<Element>::type RawElement; + typedef internal::NativeArray<RawElement> type; + // NativeArray<T> can represent a native array either by value or by + // reference (selected by a constructor argument), so 'const type' + // can be used to reference a const native array. We cannot + // 'typedef const type& const_reference' here, as that would mean + // ConstReference() has to return a reference to a local variable. + typedef const type const_reference; + + static const_reference ConstReference(const Element (&array)[N]) { + static_assert(std::is_same<Element, RawElement>::value, + "Element type must not be const"); + return type(array, N, RelationToSourceReference()); + } + static type Copy(const Element (&array)[N]) { + return type(array, N, RelationToSourceCopy()); + } +}; + +// This specialization is used when RawContainer is a native array +// represented as a (pointer, size) tuple. +template <typename ElementPointer, typename Size> +class StlContainerView< ::std::tuple<ElementPointer, Size> > { + public: + typedef typename std::remove_const< + typename std::pointer_traits<ElementPointer>::element_type>::type + RawElement; + typedef internal::NativeArray<RawElement> type; + typedef const type const_reference; + + static const_reference ConstReference( + const ::std::tuple<ElementPointer, Size>& array) { + return type(std::get<0>(array), std::get<1>(array), + RelationToSourceReference()); + } + static type Copy(const ::std::tuple<ElementPointer, Size>& array) { + return type(std::get<0>(array), std::get<1>(array), RelationToSourceCopy()); + } +}; + +// The following specialization prevents the user from instantiating +// StlContainer with a reference type. +template <typename T> class StlContainerView<T&>; + +// A type transform to remove constness from the first part of a pair. +// Pairs like that are used as the value_type of associative containers, +// and this transform produces a similar but assignable pair. +template <typename T> +struct RemoveConstFromKey { + typedef T type; +}; + +// Partially specialized to remove constness from std::pair<const K, V>. +template <typename K, typename V> +struct RemoveConstFromKey<std::pair<const K, V> > { + typedef std::pair<K, V> type; +}; + +// Emit an assertion failure due to incorrect DoDefault() usage. Out-of-lined to +// reduce code size. +GTEST_API_ void IllegalDoDefault(const char* file, int line); + +template <typename F, typename Tuple, size_t... Idx> +auto ApplyImpl(F&& f, Tuple&& args, IndexSequence<Idx...>) -> decltype( + std::forward<F>(f)(std::get<Idx>(std::forward<Tuple>(args))...)) { + return std::forward<F>(f)(std::get<Idx>(std::forward<Tuple>(args))...); +} + +// Apply the function to a tuple of arguments. +template <typename F, typename Tuple> +auto Apply(F&& f, Tuple&& args) -> decltype( + ApplyImpl(std::forward<F>(f), std::forward<Tuple>(args), + MakeIndexSequence<std::tuple_size< + typename std::remove_reference<Tuple>::type>::value>())) { + return ApplyImpl(std::forward<F>(f), std::forward<Tuple>(args), + MakeIndexSequence<std::tuple_size< + typename std::remove_reference<Tuple>::type>::value>()); +} + +// Template struct Function<F>, where F must be a function type, contains +// the following typedefs: +// +// Result: the function's return type. +// Arg<N>: the type of the N-th argument, where N starts with 0. +// ArgumentTuple: the tuple type consisting of all parameters of F. +// ArgumentMatcherTuple: the tuple type consisting of Matchers for all +// parameters of F. +// MakeResultVoid: the function type obtained by substituting void +// for the return type of F. +// MakeResultIgnoredValue: +// the function type obtained by substituting Something +// for the return type of F. +template <typename T> +struct Function; + +template <typename R, typename... Args> +struct Function<R(Args...)> { + using Result = R; + static constexpr size_t ArgumentCount = sizeof...(Args); + template <size_t I> + using Arg = ElemFromList<I, Args...>; + using ArgumentTuple = std::tuple<Args...>; + using ArgumentMatcherTuple = std::tuple<Matcher<Args>...>; + using MakeResultVoid = void(Args...); + using MakeResultIgnoredValue = IgnoredValue(Args...); +}; + +template <typename R, typename... Args> +constexpr size_t Function<R(Args...)>::ArgumentCount; + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +} // namespace internal +} // namespace testing + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_ +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PP_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PP_H_ + +// Expands and concatenates the arguments. Constructed macros reevaluate. +#define GMOCK_PP_CAT(_1, _2) GMOCK_PP_INTERNAL_CAT(_1, _2) + +// Expands and stringifies the only argument. +#define GMOCK_PP_STRINGIZE(...) GMOCK_PP_INTERNAL_STRINGIZE(__VA_ARGS__) + +// Returns empty. Given a variadic number of arguments. +#define GMOCK_PP_EMPTY(...) + +// Returns a comma. Given a variadic number of arguments. +#define GMOCK_PP_COMMA(...) , + +// Returns the only argument. +#define GMOCK_PP_IDENTITY(_1) _1 + +// Evaluates to the number of arguments after expansion. +// +// #define PAIR x, y +// +// GMOCK_PP_NARG() => 1 +// GMOCK_PP_NARG(x) => 1 +// GMOCK_PP_NARG(x, y) => 2 +// GMOCK_PP_NARG(PAIR) => 2 +// +// Requires: the number of arguments after expansion is at most 15. +#define GMOCK_PP_NARG(...) \ + GMOCK_PP_INTERNAL_16TH( \ + (__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) + +// Returns 1 if the expansion of arguments has an unprotected comma. Otherwise +// returns 0. Requires no more than 15 unprotected commas. +#define GMOCK_PP_HAS_COMMA(...) \ + GMOCK_PP_INTERNAL_16TH( \ + (__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0)) + +// Returns the first argument. +#define GMOCK_PP_HEAD(...) GMOCK_PP_INTERNAL_HEAD((__VA_ARGS__, unusedArg)) + +// Returns the tail. A variadic list of all arguments minus the first. Requires +// at least one argument. +#define GMOCK_PP_TAIL(...) GMOCK_PP_INTERNAL_TAIL((__VA_ARGS__)) + +// Calls CAT(_Macro, NARG(__VA_ARGS__))(__VA_ARGS__) +#define GMOCK_PP_VARIADIC_CALL(_Macro, ...) \ + GMOCK_PP_IDENTITY( \ + GMOCK_PP_CAT(_Macro, GMOCK_PP_NARG(__VA_ARGS__))(__VA_ARGS__)) + +// If the arguments after expansion have no tokens, evaluates to `1`. Otherwise +// evaluates to `0`. +// +// Requires: * the number of arguments after expansion is at most 15. +// * If the argument is a macro, it must be able to be called with one +// argument. +// +// Implementation details: +// +// There is one case when it generates a compile error: if the argument is macro +// that cannot be called with one argument. +// +// #define M(a, b) // it doesn't matter what it expands to +// +// // Expected: expands to `0`. +// // Actual: compile error. +// GMOCK_PP_IS_EMPTY(M) +// +// There are 4 cases tested: +// +// * __VA_ARGS__ possible expansion has no unparen'd commas. Expected 0. +// * __VA_ARGS__ possible expansion is not enclosed in parenthesis. Expected 0. +// * __VA_ARGS__ possible expansion is not a macro that ()-evaluates to a comma. +// Expected 0 +// * __VA_ARGS__ is empty, or has unparen'd commas, or is enclosed in +// parenthesis, or is a macro that ()-evaluates to comma. Expected 1. +// +// We trigger detection on '0001', i.e. on empty. +#define GMOCK_PP_IS_EMPTY(...) \ + GMOCK_PP_INTERNAL_IS_EMPTY(GMOCK_PP_HAS_COMMA(__VA_ARGS__), \ + GMOCK_PP_HAS_COMMA(GMOCK_PP_COMMA __VA_ARGS__), \ + GMOCK_PP_HAS_COMMA(__VA_ARGS__()), \ + GMOCK_PP_HAS_COMMA(GMOCK_PP_COMMA __VA_ARGS__())) + +// Evaluates to _Then if _Cond is 1 and _Else if _Cond is 0. +#define GMOCK_PP_IF(_Cond, _Then, _Else) \ + GMOCK_PP_CAT(GMOCK_PP_INTERNAL_IF_, _Cond)(_Then, _Else) + +// Similar to GMOCK_PP_IF but takes _Then and _Else in parentheses. +// +// GMOCK_PP_GENERIC_IF(1, (a, b, c), (d, e, f)) => a, b, c +// GMOCK_PP_GENERIC_IF(0, (a, b, c), (d, e, f)) => d, e, f +// +#define GMOCK_PP_GENERIC_IF(_Cond, _Then, _Else) \ + GMOCK_PP_REMOVE_PARENS(GMOCK_PP_IF(_Cond, _Then, _Else)) + +// Evaluates to the number of arguments after expansion. Identifies 'empty' as +// 0. +// +// #define PAIR x, y +// +// GMOCK_PP_NARG0() => 0 +// GMOCK_PP_NARG0(x) => 1 +// GMOCK_PP_NARG0(x, y) => 2 +// GMOCK_PP_NARG0(PAIR) => 2 +// +// Requires: * the number of arguments after expansion is at most 15. +// * If the argument is a macro, it must be able to be called with one +// argument. +#define GMOCK_PP_NARG0(...) \ + GMOCK_PP_IF(GMOCK_PP_IS_EMPTY(__VA_ARGS__), 0, GMOCK_PP_NARG(__VA_ARGS__)) + +// Expands to 1 if the first argument starts with something in parentheses, +// otherwise to 0. +#define GMOCK_PP_IS_BEGIN_PARENS(...) \ + GMOCK_PP_HEAD(GMOCK_PP_CAT(GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_, \ + GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C __VA_ARGS__)) + +// Expands to 1 is there is only one argument and it is enclosed in parentheses. +#define GMOCK_PP_IS_ENCLOSED_PARENS(...) \ + GMOCK_PP_IF(GMOCK_PP_IS_BEGIN_PARENS(__VA_ARGS__), \ + GMOCK_PP_IS_EMPTY(GMOCK_PP_EMPTY __VA_ARGS__), 0) + +// Remove the parens, requires GMOCK_PP_IS_ENCLOSED_PARENS(args) => 1. +#define GMOCK_PP_REMOVE_PARENS(...) GMOCK_PP_INTERNAL_REMOVE_PARENS __VA_ARGS__ + +// Expands to _Macro(0, _Data, e1) _Macro(1, _Data, e2) ... _Macro(K -1, _Data, +// eK) as many of GMOCK_INTERNAL_NARG0 _Tuple. +// Requires: * |_Macro| can be called with 3 arguments. +// * |_Tuple| expansion has no more than 15 elements. +#define GMOCK_PP_FOR_EACH(_Macro, _Data, _Tuple) \ + GMOCK_PP_CAT(GMOCK_PP_INTERNAL_FOR_EACH_IMPL_, GMOCK_PP_NARG0 _Tuple) \ + (0, _Macro, _Data, _Tuple) + +// Expands to _Macro(0, _Data, ) _Macro(1, _Data, ) ... _Macro(K - 1, _Data, ) +// Empty if _K = 0. +// Requires: * |_Macro| can be called with 3 arguments. +// * |_K| literal between 0 and 15 +#define GMOCK_PP_REPEAT(_Macro, _Data, _N) \ + GMOCK_PP_CAT(GMOCK_PP_INTERNAL_FOR_EACH_IMPL_, _N) \ + (0, _Macro, _Data, GMOCK_PP_INTENRAL_EMPTY_TUPLE) + +// Increments the argument, requires the argument to be between 0 and 15. +#define GMOCK_PP_INC(_i) GMOCK_PP_CAT(GMOCK_PP_INTERNAL_INC_, _i) + +// Returns comma if _i != 0. Requires _i to be between 0 and 15. +#define GMOCK_PP_COMMA_IF(_i) GMOCK_PP_CAT(GMOCK_PP_INTERNAL_COMMA_IF_, _i) + +// Internal details follow. Do not use any of these symbols outside of this +// file or we will break your code. +#define GMOCK_PP_INTENRAL_EMPTY_TUPLE (, , , , , , , , , , , , , , , ) +#define GMOCK_PP_INTERNAL_CAT(_1, _2) _1##_2 +#define GMOCK_PP_INTERNAL_STRINGIZE(...) #__VA_ARGS__ +#define GMOCK_PP_INTERNAL_CAT_5(_1, _2, _3, _4, _5) _1##_2##_3##_4##_5 +#define GMOCK_PP_INTERNAL_IS_EMPTY(_1, _2, _3, _4) \ + GMOCK_PP_HAS_COMMA(GMOCK_PP_INTERNAL_CAT_5(GMOCK_PP_INTERNAL_IS_EMPTY_CASE_, \ + _1, _2, _3, _4)) +#define GMOCK_PP_INTERNAL_IS_EMPTY_CASE_0001 , +#define GMOCK_PP_INTERNAL_IF_1(_Then, _Else) _Then +#define GMOCK_PP_INTERNAL_IF_0(_Then, _Else) _Else + +// Because of MSVC treating a token with a comma in it as a single token when +// passed to another macro, we need to force it to evaluate it as multiple +// tokens. We do that by using a "IDENTITY(MACRO PARENTHESIZED_ARGS)" macro. We +// define one per possible macro that relies on this behavior. Note "_Args" must +// be parenthesized. +#define GMOCK_PP_INTERNAL_INTERNAL_16TH(_1, _2, _3, _4, _5, _6, _7, _8, _9, \ + _10, _11, _12, _13, _14, _15, _16, \ + ...) \ + _16 +#define GMOCK_PP_INTERNAL_16TH(_Args) \ + GMOCK_PP_IDENTITY(GMOCK_PP_INTERNAL_INTERNAL_16TH _Args) +#define GMOCK_PP_INTERNAL_INTERNAL_HEAD(_1, ...) _1 +#define GMOCK_PP_INTERNAL_HEAD(_Args) \ + GMOCK_PP_IDENTITY(GMOCK_PP_INTERNAL_INTERNAL_HEAD _Args) +#define GMOCK_PP_INTERNAL_INTERNAL_TAIL(_1, ...) __VA_ARGS__ +#define GMOCK_PP_INTERNAL_TAIL(_Args) \ + GMOCK_PP_IDENTITY(GMOCK_PP_INTERNAL_INTERNAL_TAIL _Args) + +#define GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C(...) 1 _ +#define GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_1 1, +#define GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C \ + 0, +#define GMOCK_PP_INTERNAL_REMOVE_PARENS(...) __VA_ARGS__ +#define GMOCK_PP_INTERNAL_INC_0 1 +#define GMOCK_PP_INTERNAL_INC_1 2 +#define GMOCK_PP_INTERNAL_INC_2 3 +#define GMOCK_PP_INTERNAL_INC_3 4 +#define GMOCK_PP_INTERNAL_INC_4 5 +#define GMOCK_PP_INTERNAL_INC_5 6 +#define GMOCK_PP_INTERNAL_INC_6 7 +#define GMOCK_PP_INTERNAL_INC_7 8 +#define GMOCK_PP_INTERNAL_INC_8 9 +#define GMOCK_PP_INTERNAL_INC_9 10 +#define GMOCK_PP_INTERNAL_INC_10 11 +#define GMOCK_PP_INTERNAL_INC_11 12 +#define GMOCK_PP_INTERNAL_INC_12 13 +#define GMOCK_PP_INTERNAL_INC_13 14 +#define GMOCK_PP_INTERNAL_INC_14 15 +#define GMOCK_PP_INTERNAL_INC_15 16 +#define GMOCK_PP_INTERNAL_COMMA_IF_0 +#define GMOCK_PP_INTERNAL_COMMA_IF_1 , +#define GMOCK_PP_INTERNAL_COMMA_IF_2 , +#define GMOCK_PP_INTERNAL_COMMA_IF_3 , +#define GMOCK_PP_INTERNAL_COMMA_IF_4 , +#define GMOCK_PP_INTERNAL_COMMA_IF_5 , +#define GMOCK_PP_INTERNAL_COMMA_IF_6 , +#define GMOCK_PP_INTERNAL_COMMA_IF_7 , +#define GMOCK_PP_INTERNAL_COMMA_IF_8 , +#define GMOCK_PP_INTERNAL_COMMA_IF_9 , +#define GMOCK_PP_INTERNAL_COMMA_IF_10 , +#define GMOCK_PP_INTERNAL_COMMA_IF_11 , +#define GMOCK_PP_INTERNAL_COMMA_IF_12 , +#define GMOCK_PP_INTERNAL_COMMA_IF_13 , +#define GMOCK_PP_INTERNAL_COMMA_IF_14 , +#define GMOCK_PP_INTERNAL_COMMA_IF_15 , +#define GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, _element) \ + _Macro(_i, _Data, _element) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_0(_i, _Macro, _Data, _Tuple) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_1(_i, _Macro, _Data, _Tuple) \ + GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_2(_i, _Macro, _Data, _Tuple) \ + GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \ + GMOCK_PP_INTERNAL_FOR_EACH_IMPL_1(GMOCK_PP_INC(_i), _Macro, _Data, \ + (GMOCK_PP_TAIL _Tuple)) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_3(_i, _Macro, _Data, _Tuple) \ + GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \ + GMOCK_PP_INTERNAL_FOR_EACH_IMPL_2(GMOCK_PP_INC(_i), _Macro, _Data, \ + (GMOCK_PP_TAIL _Tuple)) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_4(_i, _Macro, _Data, _Tuple) \ + GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \ + GMOCK_PP_INTERNAL_FOR_EACH_IMPL_3(GMOCK_PP_INC(_i), _Macro, _Data, \ + (GMOCK_PP_TAIL _Tuple)) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_5(_i, _Macro, _Data, _Tuple) \ + GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \ + GMOCK_PP_INTERNAL_FOR_EACH_IMPL_4(GMOCK_PP_INC(_i), _Macro, _Data, \ + (GMOCK_PP_TAIL _Tuple)) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_6(_i, _Macro, _Data, _Tuple) \ + GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \ + GMOCK_PP_INTERNAL_FOR_EACH_IMPL_5(GMOCK_PP_INC(_i), _Macro, _Data, \ + (GMOCK_PP_TAIL _Tuple)) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_7(_i, _Macro, _Data, _Tuple) \ + GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \ + GMOCK_PP_INTERNAL_FOR_EACH_IMPL_6(GMOCK_PP_INC(_i), _Macro, _Data, \ + (GMOCK_PP_TAIL _Tuple)) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_8(_i, _Macro, _Data, _Tuple) \ + GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \ + GMOCK_PP_INTERNAL_FOR_EACH_IMPL_7(GMOCK_PP_INC(_i), _Macro, _Data, \ + (GMOCK_PP_TAIL _Tuple)) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_9(_i, _Macro, _Data, _Tuple) \ + GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \ + GMOCK_PP_INTERNAL_FOR_EACH_IMPL_8(GMOCK_PP_INC(_i), _Macro, _Data, \ + (GMOCK_PP_TAIL _Tuple)) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_10(_i, _Macro, _Data, _Tuple) \ + GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \ + GMOCK_PP_INTERNAL_FOR_EACH_IMPL_9(GMOCK_PP_INC(_i), _Macro, _Data, \ + (GMOCK_PP_TAIL _Tuple)) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_11(_i, _Macro, _Data, _Tuple) \ + GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \ + GMOCK_PP_INTERNAL_FOR_EACH_IMPL_10(GMOCK_PP_INC(_i), _Macro, _Data, \ + (GMOCK_PP_TAIL _Tuple)) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_12(_i, _Macro, _Data, _Tuple) \ + GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \ + GMOCK_PP_INTERNAL_FOR_EACH_IMPL_11(GMOCK_PP_INC(_i), _Macro, _Data, \ + (GMOCK_PP_TAIL _Tuple)) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_13(_i, _Macro, _Data, _Tuple) \ + GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \ + GMOCK_PP_INTERNAL_FOR_EACH_IMPL_12(GMOCK_PP_INC(_i), _Macro, _Data, \ + (GMOCK_PP_TAIL _Tuple)) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_14(_i, _Macro, _Data, _Tuple) \ + GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \ + GMOCK_PP_INTERNAL_FOR_EACH_IMPL_13(GMOCK_PP_INC(_i), _Macro, _Data, \ + (GMOCK_PP_TAIL _Tuple)) +#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_15(_i, _Macro, _Data, _Tuple) \ + GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \ + GMOCK_PP_INTERNAL_FOR_EACH_IMPL_14(GMOCK_PP_INC(_i), _Macro, _Data, \ + (GMOCK_PP_TAIL _Tuple)) + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PP_H_ + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#endif + +namespace testing { + +// To implement an action Foo, define: +// 1. a class FooAction that implements the ActionInterface interface, and +// 2. a factory function that creates an Action object from a +// const FooAction*. +// +// The two-level delegation design follows that of Matcher, providing +// consistency for extension developers. It also eases ownership +// management as Action objects can now be copied like plain values. + +namespace internal { + +// BuiltInDefaultValueGetter<T, true>::Get() returns a +// default-constructed T value. BuiltInDefaultValueGetter<T, +// false>::Get() crashes with an error. +// +// This primary template is used when kDefaultConstructible is true. +template <typename T, bool kDefaultConstructible> +struct BuiltInDefaultValueGetter { + static T Get() { return T(); } +}; +template <typename T> +struct BuiltInDefaultValueGetter<T, false> { + static T Get() { + Assert(false, __FILE__, __LINE__, + "Default action undefined for the function return type."); + return internal::Invalid<T>(); + // The above statement will never be reached, but is required in + // order for this function to compile. + } +}; + +// BuiltInDefaultValue<T>::Get() returns the "built-in" default value +// for type T, which is NULL when T is a raw pointer type, 0 when T is +// a numeric type, false when T is bool, or "" when T is string or +// std::string. In addition, in C++11 and above, it turns a +// default-constructed T value if T is default constructible. For any +// other type T, the built-in default T value is undefined, and the +// function will abort the process. +template <typename T> +class BuiltInDefaultValue { + public: + // This function returns true if and only if type T has a built-in default + // value. + static bool Exists() { + return ::std::is_default_constructible<T>::value; + } + + static T Get() { + return BuiltInDefaultValueGetter< + T, ::std::is_default_constructible<T>::value>::Get(); + } +}; + +// This partial specialization says that we use the same built-in +// default value for T and const T. +template <typename T> +class BuiltInDefaultValue<const T> { + public: + static bool Exists() { return BuiltInDefaultValue<T>::Exists(); } + static T Get() { return BuiltInDefaultValue<T>::Get(); } +}; + +// This partial specialization defines the default values for pointer +// types. +template <typename T> +class BuiltInDefaultValue<T*> { + public: + static bool Exists() { return true; } + static T* Get() { return nullptr; } +}; + +// The following specializations define the default values for +// specific types we care about. +#define GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(type, value) \ + template <> \ + class BuiltInDefaultValue<type> { \ + public: \ + static bool Exists() { return true; } \ + static type Get() { return value; } \ + } + +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(void, ); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::std::string, ""); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(bool, false); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned char, '\0'); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed char, '\0'); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(char, '\0'); + +// There's no need for a default action for signed wchar_t, as that +// type is the same as wchar_t for gcc, and invalid for MSVC. +// +// There's also no need for a default action for unsigned wchar_t, as +// that type is the same as unsigned int for gcc, and invalid for +// MSVC. +#if GMOCK_WCHAR_T_IS_NATIVE_ +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(wchar_t, 0U); // NOLINT +#endif + +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned short, 0U); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed short, 0); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned int, 0U); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed int, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long, 0UL); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long, 0L); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long long, 0); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long long, 0); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(float, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(double, 0); + +#undef GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_ + +// Simple two-arg form of std::disjunction. +template <typename P, typename Q> +using disjunction = typename ::std::conditional<P::value, P, Q>::type; + +} // namespace internal + +// When an unexpected function call is encountered, Google Mock will +// let it return a default value if the user has specified one for its +// return type, or if the return type has a built-in default value; +// otherwise Google Mock won't know what value to return and will have +// to abort the process. +// +// The DefaultValue<T> class allows a user to specify the +// default value for a type T that is both copyable and publicly +// destructible (i.e. anything that can be used as a function return +// type). The usage is: +// +// // Sets the default value for type T to be foo. +// DefaultValue<T>::Set(foo); +template <typename T> +class DefaultValue { + public: + // Sets the default value for type T; requires T to be + // copy-constructable and have a public destructor. + static void Set(T x) { + delete producer_; + producer_ = new FixedValueProducer(x); + } + + // Provides a factory function to be called to generate the default value. + // This method can be used even if T is only move-constructible, but it is not + // limited to that case. + typedef T (*FactoryFunction)(); + static void SetFactory(FactoryFunction factory) { + delete producer_; + producer_ = new FactoryValueProducer(factory); + } + + // Unsets the default value for type T. + static void Clear() { + delete producer_; + producer_ = nullptr; + } + + // Returns true if and only if the user has set the default value for type T. + static bool IsSet() { return producer_ != nullptr; } + + // Returns true if T has a default return value set by the user or there + // exists a built-in default value. + static bool Exists() { + return IsSet() || internal::BuiltInDefaultValue<T>::Exists(); + } + + // Returns the default value for type T if the user has set one; + // otherwise returns the built-in default value. Requires that Exists() + // is true, which ensures that the return value is well-defined. + static T Get() { + return producer_ == nullptr ? internal::BuiltInDefaultValue<T>::Get() + : producer_->Produce(); + } + + private: + class ValueProducer { + public: + virtual ~ValueProducer() {} + virtual T Produce() = 0; + }; + + class FixedValueProducer : public ValueProducer { + public: + explicit FixedValueProducer(T value) : value_(value) {} + T Produce() override { return value_; } + + private: + const T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(FixedValueProducer); + }; + + class FactoryValueProducer : public ValueProducer { + public: + explicit FactoryValueProducer(FactoryFunction factory) + : factory_(factory) {} + T Produce() override { return factory_(); } + + private: + const FactoryFunction factory_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(FactoryValueProducer); + }; + + static ValueProducer* producer_; +}; + +// This partial specialization allows a user to set default values for +// reference types. +template <typename T> +class DefaultValue<T&> { + public: + // Sets the default value for type T&. + static void Set(T& x) { // NOLINT + address_ = &x; + } + + // Unsets the default value for type T&. + static void Clear() { address_ = nullptr; } + + // Returns true if and only if the user has set the default value for type T&. + static bool IsSet() { return address_ != nullptr; } + + // Returns true if T has a default return value set by the user or there + // exists a built-in default value. + static bool Exists() { + return IsSet() || internal::BuiltInDefaultValue<T&>::Exists(); + } + + // Returns the default value for type T& if the user has set one; + // otherwise returns the built-in default value if there is one; + // otherwise aborts the process. + static T& Get() { + return address_ == nullptr ? internal::BuiltInDefaultValue<T&>::Get() + : *address_; + } + + private: + static T* address_; +}; + +// This specialization allows DefaultValue<void>::Get() to +// compile. +template <> +class DefaultValue<void> { + public: + static bool Exists() { return true; } + static void Get() {} +}; + +// Points to the user-set default value for type T. +template <typename T> +typename DefaultValue<T>::ValueProducer* DefaultValue<T>::producer_ = nullptr; + +// Points to the user-set default value for type T&. +template <typename T> +T* DefaultValue<T&>::address_ = nullptr; + +// Implement this interface to define an action for function type F. +template <typename F> +class ActionInterface { + public: + typedef typename internal::Function<F>::Result Result; + typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple; + + ActionInterface() {} + virtual ~ActionInterface() {} + + // Performs the action. This method is not const, as in general an + // action can have side effects and be stateful. For example, a + // get-the-next-element-from-the-collection action will need to + // remember the current element. + virtual Result Perform(const ArgumentTuple& args) = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionInterface); +}; + +// An Action<F> is a copyable and IMMUTABLE (except by assignment) +// object that represents an action to be taken when a mock function +// of type F is called. The implementation of Action<T> is just a +// std::shared_ptr to const ActionInterface<T>. Don't inherit from Action! +// You can view an object implementing ActionInterface<F> as a +// concrete action (including its current state), and an Action<F> +// object as a handle to it. +template <typename F> +class Action { + // Adapter class to allow constructing Action from a legacy ActionInterface. + // New code should create Actions from functors instead. + struct ActionAdapter { + // Adapter must be copyable to satisfy std::function requirements. + ::std::shared_ptr<ActionInterface<F>> impl_; + + template <typename... Args> + typename internal::Function<F>::Result operator()(Args&&... args) { + return impl_->Perform( + ::std::forward_as_tuple(::std::forward<Args>(args)...)); + } + }; + + template <typename G> + using IsCompatibleFunctor = std::is_constructible<std::function<F>, G>; + + public: + typedef typename internal::Function<F>::Result Result; + typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple; + + // Constructs a null Action. Needed for storing Action objects in + // STL containers. + Action() {} + + // Construct an Action from a specified callable. + // This cannot take std::function directly, because then Action would not be + // directly constructible from lambda (it would require two conversions). + template < + typename G, + typename = typename std::enable_if<internal::disjunction< + IsCompatibleFunctor<G>, std::is_constructible<std::function<Result()>, + G>>::value>::type> + Action(G&& fun) { // NOLINT + Init(::std::forward<G>(fun), IsCompatibleFunctor<G>()); + } + + // Constructs an Action from its implementation. + explicit Action(ActionInterface<F>* impl) + : fun_(ActionAdapter{::std::shared_ptr<ActionInterface<F>>(impl)}) {} + + // This constructor allows us to turn an Action<Func> object into an + // Action<F>, as long as F's arguments can be implicitly converted + // to Func's and Func's return type can be implicitly converted to F's. + template <typename Func> + explicit Action(const Action<Func>& action) : fun_(action.fun_) {} + + // Returns true if and only if this is the DoDefault() action. + bool IsDoDefault() const { return fun_ == nullptr; } + + // Performs the action. Note that this method is const even though + // the corresponding method in ActionInterface is not. The reason + // is that a const Action<F> means that it cannot be re-bound to + // another concrete action, not that the concrete action it binds to + // cannot change state. (Think of the difference between a const + // pointer and a pointer to const.) + Result Perform(ArgumentTuple args) const { + if (IsDoDefault()) { + internal::IllegalDoDefault(__FILE__, __LINE__); + } + return internal::Apply(fun_, ::std::move(args)); + } + + private: + template <typename G> + friend class Action; + + template <typename G> + void Init(G&& g, ::std::true_type) { + fun_ = ::std::forward<G>(g); + } + + template <typename G> + void Init(G&& g, ::std::false_type) { + fun_ = IgnoreArgs<typename ::std::decay<G>::type>{::std::forward<G>(g)}; + } + + template <typename FunctionImpl> + struct IgnoreArgs { + template <typename... Args> + Result operator()(const Args&...) const { + return function_impl(); + } + + FunctionImpl function_impl; + }; + + // fun_ is an empty function if and only if this is the DoDefault() action. + ::std::function<F> fun_; +}; + +// The PolymorphicAction class template makes it easy to implement a +// polymorphic action (i.e. an action that can be used in mock +// functions of than one type, e.g. Return()). +// +// To define a polymorphic action, a user first provides a COPYABLE +// implementation class that has a Perform() method template: +// +// class FooAction { +// public: +// template <typename Result, typename ArgumentTuple> +// Result Perform(const ArgumentTuple& args) const { +// // Processes the arguments and returns a result, using +// // std::get<N>(args) to get the N-th (0-based) argument in the tuple. +// } +// ... +// }; +// +// Then the user creates the polymorphic action using +// MakePolymorphicAction(object) where object has type FooAction. See +// the definition of Return(void) and SetArgumentPointee<N>(value) for +// complete examples. +template <typename Impl> +class PolymorphicAction { + public: + explicit PolymorphicAction(const Impl& impl) : impl_(impl) {} + + template <typename F> + operator Action<F>() const { + return Action<F>(new MonomorphicImpl<F>(impl_)); + } + + private: + template <typename F> + class MonomorphicImpl : public ActionInterface<F> { + public: + typedef typename internal::Function<F>::Result Result; + typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple; + + explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} + + Result Perform(const ArgumentTuple& args) override { + return impl_.template Perform<Result>(args); + } + + private: + Impl impl_; + }; + + Impl impl_; +}; + +// Creates an Action from its implementation and returns it. The +// created Action object owns the implementation. +template <typename F> +Action<F> MakeAction(ActionInterface<F>* impl) { + return Action<F>(impl); +} + +// Creates a polymorphic action from its implementation. This is +// easier to use than the PolymorphicAction<Impl> constructor as it +// doesn't require you to explicitly write the template argument, e.g. +// +// MakePolymorphicAction(foo); +// vs +// PolymorphicAction<TypeOfFoo>(foo); +template <typename Impl> +inline PolymorphicAction<Impl> MakePolymorphicAction(const Impl& impl) { + return PolymorphicAction<Impl>(impl); +} + +namespace internal { + +// Helper struct to specialize ReturnAction to execute a move instead of a copy +// on return. Useful for move-only types, but could be used on any type. +template <typename T> +struct ByMoveWrapper { + explicit ByMoveWrapper(T value) : payload(std::move(value)) {} + T payload; +}; + +// Implements the polymorphic Return(x) action, which can be used in +// any function that returns the type of x, regardless of the argument +// types. +// +// Note: The value passed into Return must be converted into +// Function<F>::Result when this action is cast to Action<F> rather than +// when that action is performed. This is important in scenarios like +// +// MOCK_METHOD1(Method, T(U)); +// ... +// { +// Foo foo; +// X x(&foo); +// EXPECT_CALL(mock, Method(_)).WillOnce(Return(x)); +// } +// +// In the example above the variable x holds reference to foo which leaves +// scope and gets destroyed. If copying X just copies a reference to foo, +// that copy will be left with a hanging reference. If conversion to T +// makes a copy of foo, the above code is safe. To support that scenario, we +// need to make sure that the type conversion happens inside the EXPECT_CALL +// statement, and conversion of the result of Return to Action<T(U)> is a +// good place for that. +// +// The real life example of the above scenario happens when an invocation +// of gtl::Container() is passed into Return. +// +template <typename R> +class ReturnAction { + public: + // Constructs a ReturnAction object from the value to be returned. + // 'value' is passed by value instead of by const reference in order + // to allow Return("string literal") to compile. + explicit ReturnAction(R value) : value_(new R(std::move(value))) {} + + // This template type conversion operator allows Return(x) to be + // used in ANY function that returns x's type. + template <typename F> + operator Action<F>() const { // NOLINT + // Assert statement belongs here because this is the best place to verify + // conditions on F. It produces the clearest error messages + // in most compilers. + // Impl really belongs in this scope as a local class but can't + // because MSVC produces duplicate symbols in different translation units + // in this case. Until MS fixes that bug we put Impl into the class scope + // and put the typedef both here (for use in assert statement) and + // in the Impl class. But both definitions must be the same. + typedef typename Function<F>::Result Result; + GTEST_COMPILE_ASSERT_( + !std::is_reference<Result>::value, + use_ReturnRef_instead_of_Return_to_return_a_reference); + static_assert(!std::is_void<Result>::value, + "Can't use Return() on an action expected to return `void`."); + return Action<F>(new Impl<R, F>(value_)); + } + + private: + // Implements the Return(x) action for a particular function type F. + template <typename R_, typename F> + class Impl : public ActionInterface<F> { + public: + typedef typename Function<F>::Result Result; + typedef typename Function<F>::ArgumentTuple ArgumentTuple; + + // The implicit cast is necessary when Result has more than one + // single-argument constructor (e.g. Result is std::vector<int>) and R + // has a type conversion operator template. In that case, value_(value) + // won't compile as the compiler doesn't known which constructor of + // Result to call. ImplicitCast_ forces the compiler to convert R to + // Result without considering explicit constructors, thus resolving the + // ambiguity. value_ is then initialized using its copy constructor. + explicit Impl(const std::shared_ptr<R>& value) + : value_before_cast_(*value), + value_(ImplicitCast_<Result>(value_before_cast_)) {} + + Result Perform(const ArgumentTuple&) override { return value_; } + + private: + GTEST_COMPILE_ASSERT_(!std::is_reference<Result>::value, + Result_cannot_be_a_reference_type); + // We save the value before casting just in case it is being cast to a + // wrapper type. + R value_before_cast_; + Result value_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Impl); + }; + + // Partially specialize for ByMoveWrapper. This version of ReturnAction will + // move its contents instead. + template <typename R_, typename F> + class Impl<ByMoveWrapper<R_>, F> : public ActionInterface<F> { + public: + typedef typename Function<F>::Result Result; + typedef typename Function<F>::ArgumentTuple ArgumentTuple; + + explicit Impl(const std::shared_ptr<R>& wrapper) + : performed_(false), wrapper_(wrapper) {} + + Result Perform(const ArgumentTuple&) override { + GTEST_CHECK_(!performed_) + << "A ByMove() action should only be performed once."; + performed_ = true; + return std::move(wrapper_->payload); + } + + private: + bool performed_; + const std::shared_ptr<R> wrapper_; + }; + + const std::shared_ptr<R> value_; +}; + +// Implements the ReturnNull() action. +class ReturnNullAction { + public: + // Allows ReturnNull() to be used in any pointer-returning function. In C++11 + // this is enforced by returning nullptr, and in non-C++11 by asserting a + // pointer type on compile time. + template <typename Result, typename ArgumentTuple> + static Result Perform(const ArgumentTuple&) { + return nullptr; + } +}; + +// Implements the Return() action. +class ReturnVoidAction { + public: + // Allows Return() to be used in any void-returning function. + template <typename Result, typename ArgumentTuple> + static void Perform(const ArgumentTuple&) { + static_assert(std::is_void<Result>::value, "Result should be void."); + } +}; + +// Implements the polymorphic ReturnRef(x) action, which can be used +// in any function that returns a reference to the type of x, +// regardless of the argument types. +template <typename T> +class ReturnRefAction { + public: + // Constructs a ReturnRefAction object from the reference to be returned. + explicit ReturnRefAction(T& ref) : ref_(ref) {} // NOLINT + + // This template type conversion operator allows ReturnRef(x) to be + // used in ANY function that returns a reference to x's type. + template <typename F> + operator Action<F>() const { + typedef typename Function<F>::Result Result; + // Asserts that the function return type is a reference. This + // catches the user error of using ReturnRef(x) when Return(x) + // should be used, and generates some helpful error message. + GTEST_COMPILE_ASSERT_(std::is_reference<Result>::value, + use_Return_instead_of_ReturnRef_to_return_a_value); + return Action<F>(new Impl<F>(ref_)); + } + + private: + // Implements the ReturnRef(x) action for a particular function type F. + template <typename F> + class Impl : public ActionInterface<F> { + public: + typedef typename Function<F>::Result Result; + typedef typename Function<F>::ArgumentTuple ArgumentTuple; + + explicit Impl(T& ref) : ref_(ref) {} // NOLINT + + Result Perform(const ArgumentTuple&) override { return ref_; } + + private: + T& ref_; + }; + + T& ref_; +}; + +// Implements the polymorphic ReturnRefOfCopy(x) action, which can be +// used in any function that returns a reference to the type of x, +// regardless of the argument types. +template <typename T> +class ReturnRefOfCopyAction { + public: + // Constructs a ReturnRefOfCopyAction object from the reference to + // be returned. + explicit ReturnRefOfCopyAction(const T& value) : value_(value) {} // NOLINT + + // This template type conversion operator allows ReturnRefOfCopy(x) to be + // used in ANY function that returns a reference to x's type. + template <typename F> + operator Action<F>() const { + typedef typename Function<F>::Result Result; + // Asserts that the function return type is a reference. This + // catches the user error of using ReturnRefOfCopy(x) when Return(x) + // should be used, and generates some helpful error message. + GTEST_COMPILE_ASSERT_( + std::is_reference<Result>::value, + use_Return_instead_of_ReturnRefOfCopy_to_return_a_value); + return Action<F>(new Impl<F>(value_)); + } + + private: + // Implements the ReturnRefOfCopy(x) action for a particular function type F. + template <typename F> + class Impl : public ActionInterface<F> { + public: + typedef typename Function<F>::Result Result; + typedef typename Function<F>::ArgumentTuple ArgumentTuple; + + explicit Impl(const T& value) : value_(value) {} // NOLINT + + Result Perform(const ArgumentTuple&) override { return value_; } + + private: + T value_; + }; + + const T value_; +}; + +// Implements the polymorphic ReturnRoundRobin(v) action, which can be +// used in any function that returns the element_type of v. +template <typename T> +class ReturnRoundRobinAction { + public: + explicit ReturnRoundRobinAction(std::vector<T> values) { + GTEST_CHECK_(!values.empty()) + << "ReturnRoundRobin requires at least one element."; + state_->values = std::move(values); + } + + template <typename... Args> + T operator()(Args&&...) const { + return state_->Next(); + } + + private: + struct State { + T Next() { + T ret_val = values[i++]; + if (i == values.size()) i = 0; + return ret_val; + } + + std::vector<T> values; + size_t i = 0; + }; + std::shared_ptr<State> state_ = std::make_shared<State>(); +}; + +// Implements the polymorphic DoDefault() action. +class DoDefaultAction { + public: + // This template type conversion operator allows DoDefault() to be + // used in any function. + template <typename F> + operator Action<F>() const { return Action<F>(); } // NOLINT +}; + +// Implements the Assign action to set a given pointer referent to a +// particular value. +template <typename T1, typename T2> +class AssignAction { + public: + AssignAction(T1* ptr, T2 value) : ptr_(ptr), value_(value) {} + + template <typename Result, typename ArgumentTuple> + void Perform(const ArgumentTuple& /* args */) const { + *ptr_ = value_; + } + + private: + T1* const ptr_; + const T2 value_; +}; + +#if !GTEST_OS_WINDOWS_MOBILE + +// Implements the SetErrnoAndReturn action to simulate return from +// various system calls and libc functions. +template <typename T> +class SetErrnoAndReturnAction { + public: + SetErrnoAndReturnAction(int errno_value, T result) + : errno_(errno_value), + result_(result) {} + template <typename Result, typename ArgumentTuple> + Result Perform(const ArgumentTuple& /* args */) const { + errno = errno_; + return result_; + } + + private: + const int errno_; + const T result_; +}; + +#endif // !GTEST_OS_WINDOWS_MOBILE + +// Implements the SetArgumentPointee<N>(x) action for any function +// whose N-th argument (0-based) is a pointer to x's type. +template <size_t N, typename A, typename = void> +struct SetArgumentPointeeAction { + A value; + + template <typename... Args> + void operator()(const Args&... args) const { + *::std::get<N>(std::tie(args...)) = value; + } +}; + +// Implements the Invoke(object_ptr, &Class::Method) action. +template <class Class, typename MethodPtr> +struct InvokeMethodAction { + Class* const obj_ptr; + const MethodPtr method_ptr; + + template <typename... Args> + auto operator()(Args&&... args) const + -> decltype((obj_ptr->*method_ptr)(std::forward<Args>(args)...)) { + return (obj_ptr->*method_ptr)(std::forward<Args>(args)...); + } +}; + +// Implements the InvokeWithoutArgs(f) action. The template argument +// FunctionImpl is the implementation type of f, which can be either a +// function pointer or a functor. InvokeWithoutArgs(f) can be used as an +// Action<F> as long as f's type is compatible with F. +template <typename FunctionImpl> +struct InvokeWithoutArgsAction { + FunctionImpl function_impl; + + // Allows InvokeWithoutArgs(f) to be used as any action whose type is + // compatible with f. + template <typename... Args> + auto operator()(const Args&...) -> decltype(function_impl()) { + return function_impl(); + } +}; + +// Implements the InvokeWithoutArgs(object_ptr, &Class::Method) action. +template <class Class, typename MethodPtr> +struct InvokeMethodWithoutArgsAction { + Class* const obj_ptr; + const MethodPtr method_ptr; + + using ReturnType = + decltype((std::declval<Class*>()->*std::declval<MethodPtr>())()); + + template <typename... Args> + ReturnType operator()(const Args&...) const { + return (obj_ptr->*method_ptr)(); + } +}; + +// Implements the IgnoreResult(action) action. +template <typename A> +class IgnoreResultAction { + public: + explicit IgnoreResultAction(const A& action) : action_(action) {} + + template <typename F> + operator Action<F>() const { + // Assert statement belongs here because this is the best place to verify + // conditions on F. It produces the clearest error messages + // in most compilers. + // Impl really belongs in this scope as a local class but can't + // because MSVC produces duplicate symbols in different translation units + // in this case. Until MS fixes that bug we put Impl into the class scope + // and put the typedef both here (for use in assert statement) and + // in the Impl class. But both definitions must be the same. + typedef typename internal::Function<F>::Result Result; + + // Asserts at compile time that F returns void. + static_assert(std::is_void<Result>::value, "Result type should be void."); + + return Action<F>(new Impl<F>(action_)); + } + + private: + template <typename F> + class Impl : public ActionInterface<F> { + public: + typedef typename internal::Function<F>::Result Result; + typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple; + + explicit Impl(const A& action) : action_(action) {} + + void Perform(const ArgumentTuple& args) override { + // Performs the action and ignores its result. + action_.Perform(args); + } + + private: + // Type OriginalFunction is the same as F except that its return + // type is IgnoredValue. + typedef typename internal::Function<F>::MakeResultIgnoredValue + OriginalFunction; + + const Action<OriginalFunction> action_; + }; + + const A action_; +}; + +template <typename InnerAction, size_t... I> +struct WithArgsAction { + InnerAction action; + + // The inner action could be anything convertible to Action<X>. + // We use the conversion operator to detect the signature of the inner Action. + template <typename R, typename... Args> + operator Action<R(Args...)>() const { // NOLINT + using TupleType = std::tuple<Args...>; + Action<R(typename std::tuple_element<I, TupleType>::type...)> + converted(action); + + return [converted](Args... args) -> R { + return converted.Perform(std::forward_as_tuple( + std::get<I>(std::forward_as_tuple(std::forward<Args>(args)...))...)); + }; + } +}; + +template <typename... Actions> +struct DoAllAction { + private: + template <typename T> + using NonFinalType = + typename std::conditional<std::is_scalar<T>::value, T, const T&>::type; + + template <typename ActionT, size_t... I> + std::vector<ActionT> Convert(IndexSequence<I...>) const { + return {ActionT(std::get<I>(actions))...}; + } + + public: + std::tuple<Actions...> actions; + + template <typename R, typename... Args> + operator Action<R(Args...)>() const { // NOLINT + struct Op { + std::vector<Action<void(NonFinalType<Args>...)>> converted; + Action<R(Args...)> last; + R operator()(Args... args) const { + auto tuple_args = std::forward_as_tuple(std::forward<Args>(args)...); + for (auto& a : converted) { + a.Perform(tuple_args); + } + return last.Perform(std::move(tuple_args)); + } + }; + return Op{Convert<Action<void(NonFinalType<Args>...)>>( + MakeIndexSequence<sizeof...(Actions) - 1>()), + std::get<sizeof...(Actions) - 1>(actions)}; + } +}; + +template <typename T, typename... Params> +struct ReturnNewAction { + T* operator()() const { + return internal::Apply( + [](const Params&... unpacked_params) { + return new T(unpacked_params...); + }, + params); + } + std::tuple<Params...> params; +}; + +template <size_t k> +struct ReturnArgAction { + template <typename... Args> + auto operator()(const Args&... args) const -> + typename std::tuple_element<k, std::tuple<Args...>>::type { + return std::get<k>(std::tie(args...)); + } +}; + +template <size_t k, typename Ptr> +struct SaveArgAction { + Ptr pointer; + + template <typename... Args> + void operator()(const Args&... args) const { + *pointer = std::get<k>(std::tie(args...)); + } +}; + +template <size_t k, typename Ptr> +struct SaveArgPointeeAction { + Ptr pointer; + + template <typename... Args> + void operator()(const Args&... args) const { + *pointer = *std::get<k>(std::tie(args...)); + } +}; + +template <size_t k, typename T> +struct SetArgRefereeAction { + T value; + + template <typename... Args> + void operator()(Args&&... args) const { + using argk_type = + typename ::std::tuple_element<k, std::tuple<Args...>>::type; + static_assert(std::is_lvalue_reference<argk_type>::value, + "Argument must be a reference type."); + std::get<k>(std::tie(args...)) = value; + } +}; + +template <size_t k, typename I1, typename I2> +struct SetArrayArgumentAction { + I1 first; + I2 last; + + template <typename... Args> + void operator()(const Args&... args) const { + auto value = std::get<k>(std::tie(args...)); + for (auto it = first; it != last; ++it, (void)++value) { + *value = *it; + } + } +}; + +template <size_t k> +struct DeleteArgAction { + template <typename... Args> + void operator()(const Args&... args) const { + delete std::get<k>(std::tie(args...)); + } +}; + +template <typename Ptr> +struct ReturnPointeeAction { + Ptr pointer; + template <typename... Args> + auto operator()(const Args&...) const -> decltype(*pointer) { + return *pointer; + } +}; + +#if GTEST_HAS_EXCEPTIONS +template <typename T> +struct ThrowAction { + T exception; + // We use a conversion operator to adapt to any return type. + template <typename R, typename... Args> + operator Action<R(Args...)>() const { // NOLINT + T copy = exception; + return [copy](Args...) -> R { throw copy; }; + } +}; +#endif // GTEST_HAS_EXCEPTIONS + +} // namespace internal + +// An Unused object can be implicitly constructed from ANY value. +// This is handy when defining actions that ignore some or all of the +// mock function arguments. For example, given +// +// MOCK_METHOD3(Foo, double(const string& label, double x, double y)); +// MOCK_METHOD3(Bar, double(int index, double x, double y)); +// +// instead of +// +// double DistanceToOriginWithLabel(const string& label, double x, double y) { +// return sqrt(x*x + y*y); +// } +// double DistanceToOriginWithIndex(int index, double x, double y) { +// return sqrt(x*x + y*y); +// } +// ... +// EXPECT_CALL(mock, Foo("abc", _, _)) +// .WillOnce(Invoke(DistanceToOriginWithLabel)); +// EXPECT_CALL(mock, Bar(5, _, _)) +// .WillOnce(Invoke(DistanceToOriginWithIndex)); +// +// you could write +// +// // We can declare any uninteresting argument as Unused. +// double DistanceToOrigin(Unused, double x, double y) { +// return sqrt(x*x + y*y); +// } +// ... +// EXPECT_CALL(mock, Foo("abc", _, _)).WillOnce(Invoke(DistanceToOrigin)); +// EXPECT_CALL(mock, Bar(5, _, _)).WillOnce(Invoke(DistanceToOrigin)); +typedef internal::IgnoredValue Unused; + +// Creates an action that does actions a1, a2, ..., sequentially in +// each invocation. All but the last action will have a readonly view of the +// arguments. +template <typename... Action> +internal::DoAllAction<typename std::decay<Action>::type...> DoAll( + Action&&... action) { + return {std::forward_as_tuple(std::forward<Action>(action)...)}; +} + +// WithArg<k>(an_action) creates an action that passes the k-th +// (0-based) argument of the mock function to an_action and performs +// it. It adapts an action accepting one argument to one that accepts +// multiple arguments. For convenience, we also provide +// WithArgs<k>(an_action) (defined below) as a synonym. +template <size_t k, typename InnerAction> +internal::WithArgsAction<typename std::decay<InnerAction>::type, k> +WithArg(InnerAction&& action) { + return {std::forward<InnerAction>(action)}; +} + +// WithArgs<N1, N2, ..., Nk>(an_action) creates an action that passes +// the selected arguments of the mock function to an_action and +// performs it. It serves as an adaptor between actions with +// different argument lists. +template <size_t k, size_t... ks, typename InnerAction> +internal::WithArgsAction<typename std::decay<InnerAction>::type, k, ks...> +WithArgs(InnerAction&& action) { + return {std::forward<InnerAction>(action)}; +} + +// WithoutArgs(inner_action) can be used in a mock function with a +// non-empty argument list to perform inner_action, which takes no +// argument. In other words, it adapts an action accepting no +// argument to one that accepts (and ignores) arguments. +template <typename InnerAction> +internal::WithArgsAction<typename std::decay<InnerAction>::type> +WithoutArgs(InnerAction&& action) { + return {std::forward<InnerAction>(action)}; +} + +// Creates an action that returns 'value'. 'value' is passed by value +// instead of const reference - otherwise Return("string literal") +// will trigger a compiler error about using array as initializer. +template <typename R> +internal::ReturnAction<R> Return(R value) { + return internal::ReturnAction<R>(std::move(value)); +} + +// Creates an action that returns NULL. +inline PolymorphicAction<internal::ReturnNullAction> ReturnNull() { + return MakePolymorphicAction(internal::ReturnNullAction()); +} + +// Creates an action that returns from a void function. +inline PolymorphicAction<internal::ReturnVoidAction> Return() { + return MakePolymorphicAction(internal::ReturnVoidAction()); +} + +// Creates an action that returns the reference to a variable. +template <typename R> +inline internal::ReturnRefAction<R> ReturnRef(R& x) { // NOLINT + return internal::ReturnRefAction<R>(x); +} + +// Prevent using ReturnRef on reference to temporary. +template <typename R, R* = nullptr> +internal::ReturnRefAction<R> ReturnRef(R&&) = delete; + +// Creates an action that returns the reference to a copy of the +// argument. The copy is created when the action is constructed and +// lives as long as the action. +template <typename R> +inline internal::ReturnRefOfCopyAction<R> ReturnRefOfCopy(const R& x) { + return internal::ReturnRefOfCopyAction<R>(x); +} + +// Modifies the parent action (a Return() action) to perform a move of the +// argument instead of a copy. +// Return(ByMove()) actions can only be executed once and will assert this +// invariant. +template <typename R> +internal::ByMoveWrapper<R> ByMove(R x) { + return internal::ByMoveWrapper<R>(std::move(x)); +} + +// Creates an action that returns an element of `vals`. Calling this action will +// repeatedly return the next value from `vals` until it reaches the end and +// will restart from the beginning. +template <typename T> +internal::ReturnRoundRobinAction<T> ReturnRoundRobin(std::vector<T> vals) { + return internal::ReturnRoundRobinAction<T>(std::move(vals)); +} + +// Creates an action that returns an element of `vals`. Calling this action will +// repeatedly return the next value from `vals` until it reaches the end and +// will restart from the beginning. +template <typename T> +internal::ReturnRoundRobinAction<T> ReturnRoundRobin( + std::initializer_list<T> vals) { + return internal::ReturnRoundRobinAction<T>(std::vector<T>(vals)); +} + +// Creates an action that does the default action for the give mock function. +inline internal::DoDefaultAction DoDefault() { + return internal::DoDefaultAction(); +} + +// Creates an action that sets the variable pointed by the N-th +// (0-based) function argument to 'value'. +template <size_t N, typename T> +internal::SetArgumentPointeeAction<N, T> SetArgPointee(T value) { + return {std::move(value)}; +} + +// The following version is DEPRECATED. +template <size_t N, typename T> +internal::SetArgumentPointeeAction<N, T> SetArgumentPointee(T value) { + return {std::move(value)}; +} + +// Creates an action that sets a pointer referent to a given value. +template <typename T1, typename T2> +PolymorphicAction<internal::AssignAction<T1, T2> > Assign(T1* ptr, T2 val) { + return MakePolymorphicAction(internal::AssignAction<T1, T2>(ptr, val)); +} + +#if !GTEST_OS_WINDOWS_MOBILE + +// Creates an action that sets errno and returns the appropriate error. +template <typename T> +PolymorphicAction<internal::SetErrnoAndReturnAction<T> > +SetErrnoAndReturn(int errval, T result) { + return MakePolymorphicAction( + internal::SetErrnoAndReturnAction<T>(errval, result)); +} + +#endif // !GTEST_OS_WINDOWS_MOBILE + +// Various overloads for Invoke(). + +// Legacy function. +// Actions can now be implicitly constructed from callables. No need to create +// wrapper objects. +// This function exists for backwards compatibility. +template <typename FunctionImpl> +typename std::decay<FunctionImpl>::type Invoke(FunctionImpl&& function_impl) { + return std::forward<FunctionImpl>(function_impl); +} + +// Creates an action that invokes the given method on the given object +// with the mock function's arguments. +template <class Class, typename MethodPtr> +internal::InvokeMethodAction<Class, MethodPtr> Invoke(Class* obj_ptr, + MethodPtr method_ptr) { + return {obj_ptr, method_ptr}; +} + +// Creates an action that invokes 'function_impl' with no argument. +template <typename FunctionImpl> +internal::InvokeWithoutArgsAction<typename std::decay<FunctionImpl>::type> +InvokeWithoutArgs(FunctionImpl function_impl) { + return {std::move(function_impl)}; +} + +// Creates an action that invokes the given method on the given object +// with no argument. +template <class Class, typename MethodPtr> +internal::InvokeMethodWithoutArgsAction<Class, MethodPtr> InvokeWithoutArgs( + Class* obj_ptr, MethodPtr method_ptr) { + return {obj_ptr, method_ptr}; +} + +// Creates an action that performs an_action and throws away its +// result. In other words, it changes the return type of an_action to +// void. an_action MUST NOT return void, or the code won't compile. +template <typename A> +inline internal::IgnoreResultAction<A> IgnoreResult(const A& an_action) { + return internal::IgnoreResultAction<A>(an_action); +} + +// Creates a reference wrapper for the given L-value. If necessary, +// you can explicitly specify the type of the reference. For example, +// suppose 'derived' is an object of type Derived, ByRef(derived) +// would wrap a Derived&. If you want to wrap a const Base& instead, +// where Base is a base class of Derived, just write: +// +// ByRef<const Base>(derived) +// +// N.B. ByRef is redundant with std::ref, std::cref and std::reference_wrapper. +// However, it may still be used for consistency with ByMove(). +template <typename T> +inline ::std::reference_wrapper<T> ByRef(T& l_value) { // NOLINT + return ::std::reference_wrapper<T>(l_value); +} + +// The ReturnNew<T>(a1, a2, ..., a_k) action returns a pointer to a new +// instance of type T, constructed on the heap with constructor arguments +// a1, a2, ..., and a_k. The caller assumes ownership of the returned value. +template <typename T, typename... Params> +internal::ReturnNewAction<T, typename std::decay<Params>::type...> ReturnNew( + Params&&... params) { + return {std::forward_as_tuple(std::forward<Params>(params)...)}; +} + +// Action ReturnArg<k>() returns the k-th argument of the mock function. +template <size_t k> +internal::ReturnArgAction<k> ReturnArg() { + return {}; +} + +// Action SaveArg<k>(pointer) saves the k-th (0-based) argument of the +// mock function to *pointer. +template <size_t k, typename Ptr> +internal::SaveArgAction<k, Ptr> SaveArg(Ptr pointer) { + return {pointer}; +} + +// Action SaveArgPointee<k>(pointer) saves the value pointed to +// by the k-th (0-based) argument of the mock function to *pointer. +template <size_t k, typename Ptr> +internal::SaveArgPointeeAction<k, Ptr> SaveArgPointee(Ptr pointer) { + return {pointer}; +} + +// Action SetArgReferee<k>(value) assigns 'value' to the variable +// referenced by the k-th (0-based) argument of the mock function. +template <size_t k, typename T> +internal::SetArgRefereeAction<k, typename std::decay<T>::type> SetArgReferee( + T&& value) { + return {std::forward<T>(value)}; +} + +// Action SetArrayArgument<k>(first, last) copies the elements in +// source range [first, last) to the array pointed to by the k-th +// (0-based) argument, which can be either a pointer or an +// iterator. The action does not take ownership of the elements in the +// source range. +template <size_t k, typename I1, typename I2> +internal::SetArrayArgumentAction<k, I1, I2> SetArrayArgument(I1 first, + I2 last) { + return {first, last}; +} + +// Action DeleteArg<k>() deletes the k-th (0-based) argument of the mock +// function. +template <size_t k> +internal::DeleteArgAction<k> DeleteArg() { + return {}; +} + +// This action returns the value pointed to by 'pointer'. +template <typename Ptr> +internal::ReturnPointeeAction<Ptr> ReturnPointee(Ptr pointer) { + return {pointer}; +} + +// Action Throw(exception) can be used in a mock function of any type +// to throw the given exception. Any copyable value can be thrown. +#if GTEST_HAS_EXCEPTIONS +template <typename T> +internal::ThrowAction<typename std::decay<T>::type> Throw(T&& exception) { + return {std::forward<T>(exception)}; +} +#endif // GTEST_HAS_EXCEPTIONS + +namespace internal { + +// A macro from the ACTION* family (defined later in gmock-generated-actions.h) +// defines an action that can be used in a mock function. Typically, +// these actions only care about a subset of the arguments of the mock +// function. For example, if such an action only uses the second +// argument, it can be used in any mock function that takes >= 2 +// arguments where the type of the second argument is compatible. +// +// Therefore, the action implementation must be prepared to take more +// arguments than it needs. The ExcessiveArg type is used to +// represent those excessive arguments. In order to keep the compiler +// error messages tractable, we define it in the testing namespace +// instead of testing::internal. However, this is an INTERNAL TYPE +// and subject to change without notice, so a user MUST NOT USE THIS +// TYPE DIRECTLY. +struct ExcessiveArg {}; + +// Builds an implementation of an Action<> for some particular signature, using +// a class defined by an ACTION* macro. +template <typename F, typename Impl> struct ActionImpl; + +template <typename Impl> +struct ImplBase { + struct Holder { + // Allows each copy of the Action<> to get to the Impl. + explicit operator const Impl&() const { return *ptr; } + std::shared_ptr<Impl> ptr; + }; + using type = typename std::conditional<std::is_constructible<Impl>::value, + Impl, Holder>::type; +}; + +template <typename R, typename... Args, typename Impl> +struct ActionImpl<R(Args...), Impl> : ImplBase<Impl>::type { + using Base = typename ImplBase<Impl>::type; + using function_type = R(Args...); + using args_type = std::tuple<Args...>; + + ActionImpl() = default; // Only defined if appropriate for Base. + explicit ActionImpl(std::shared_ptr<Impl> impl) : Base{std::move(impl)} { } + + R operator()(Args&&... arg) const { + static constexpr size_t kMaxArgs = + sizeof...(Args) <= 10 ? sizeof...(Args) : 10; + return Apply(MakeIndexSequence<kMaxArgs>{}, + MakeIndexSequence<10 - kMaxArgs>{}, + args_type{std::forward<Args>(arg)...}); + } + + template <std::size_t... arg_id, std::size_t... excess_id> + R Apply(IndexSequence<arg_id...>, IndexSequence<excess_id...>, + const args_type& args) const { + // Impl need not be specific to the signature of action being implemented; + // only the implementing function body needs to have all of the specific + // types instantiated. Up to 10 of the args that are provided by the + // args_type get passed, followed by a dummy of unspecified type for the + // remainder up to 10 explicit args. + static constexpr ExcessiveArg kExcessArg{}; + return static_cast<const Impl&>(*this).template gmock_PerformImpl< + /*function_type=*/function_type, /*return_type=*/R, + /*args_type=*/args_type, + /*argN_type=*/typename std::tuple_element<arg_id, args_type>::type...>( + /*args=*/args, std::get<arg_id>(args)..., + ((void)excess_id, kExcessArg)...); + } +}; + +// Stores a default-constructed Impl as part of the Action<>'s +// std::function<>. The Impl should be trivial to copy. +template <typename F, typename Impl> +::testing::Action<F> MakeAction() { + return ::testing::Action<F>(ActionImpl<F, Impl>()); +} + +// Stores just the one given instance of Impl. +template <typename F, typename Impl> +::testing::Action<F> MakeAction(std::shared_ptr<Impl> impl) { + return ::testing::Action<F>(ActionImpl<F, Impl>(std::move(impl))); +} + +#define GMOCK_INTERNAL_ARG_UNUSED(i, data, el) \ + , const arg##i##_type& arg##i GTEST_ATTRIBUTE_UNUSED_ +#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_ \ + const args_type& args GTEST_ATTRIBUTE_UNUSED_ GMOCK_PP_REPEAT( \ + GMOCK_INTERNAL_ARG_UNUSED, , 10) + +#define GMOCK_INTERNAL_ARG(i, data, el) , const arg##i##_type& arg##i +#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_ \ + const args_type& args GMOCK_PP_REPEAT(GMOCK_INTERNAL_ARG, , 10) + +#define GMOCK_INTERNAL_TEMPLATE_ARG(i, data, el) , typename arg##i##_type +#define GMOCK_ACTION_TEMPLATE_ARGS_NAMES_ \ + GMOCK_PP_TAIL(GMOCK_PP_REPEAT(GMOCK_INTERNAL_TEMPLATE_ARG, , 10)) + +#define GMOCK_INTERNAL_TYPENAME_PARAM(i, data, param) , typename param##_type +#define GMOCK_ACTION_TYPENAME_PARAMS_(params) \ + GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_TYPENAME_PARAM, , params)) + +#define GMOCK_INTERNAL_TYPE_PARAM(i, data, param) , param##_type +#define GMOCK_ACTION_TYPE_PARAMS_(params) \ + GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_TYPE_PARAM, , params)) + +#define GMOCK_INTERNAL_TYPE_GVALUE_PARAM(i, data, param) \ + , param##_type gmock_p##i +#define GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params) \ + GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_TYPE_GVALUE_PARAM, , params)) + +#define GMOCK_INTERNAL_GVALUE_PARAM(i, data, param) \ + , std::forward<param##_type>(gmock_p##i) +#define GMOCK_ACTION_GVALUE_PARAMS_(params) \ + GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GVALUE_PARAM, , params)) + +#define GMOCK_INTERNAL_INIT_PARAM(i, data, param) \ + , param(::std::forward<param##_type>(gmock_p##i)) +#define GMOCK_ACTION_INIT_PARAMS_(params) \ + GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_INIT_PARAM, , params)) + +#define GMOCK_INTERNAL_FIELD_PARAM(i, data, param) param##_type param; +#define GMOCK_ACTION_FIELD_PARAMS_(params) \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_FIELD_PARAM, , params) + +#define GMOCK_INTERNAL_ACTION(name, full_name, params) \ + template <GMOCK_ACTION_TYPENAME_PARAMS_(params)> \ + class full_name { \ + public: \ + explicit full_name(GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) \ + : impl_(std::make_shared<gmock_Impl>( \ + GMOCK_ACTION_GVALUE_PARAMS_(params))) { } \ + full_name(const full_name&) = default; \ + full_name(full_name&&) noexcept = default; \ + template <typename F> \ + operator ::testing::Action<F>() const { \ + return ::testing::internal::MakeAction<F>(impl_); \ + } \ + private: \ + class gmock_Impl { \ + public: \ + explicit gmock_Impl(GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) \ + : GMOCK_ACTION_INIT_PARAMS_(params) {} \ + template <typename function_type, typename return_type, \ + typename args_type, GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \ + return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \ + GMOCK_ACTION_FIELD_PARAMS_(params) \ + }; \ + std::shared_ptr<const gmock_Impl> impl_; \ + }; \ + template <GMOCK_ACTION_TYPENAME_PARAMS_(params)> \ + inline full_name<GMOCK_ACTION_TYPE_PARAMS_(params)> name( \ + GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) { \ + return full_name<GMOCK_ACTION_TYPE_PARAMS_(params)>( \ + GMOCK_ACTION_GVALUE_PARAMS_(params)); \ + } \ + template <GMOCK_ACTION_TYPENAME_PARAMS_(params)> \ + template <typename function_type, typename return_type, typename args_type, \ + GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \ + return_type full_name<GMOCK_ACTION_TYPE_PARAMS_(params)>::gmock_Impl:: \ + gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +} // namespace internal + +// Similar to GMOCK_INTERNAL_ACTION, but no bound parameters are stored. +#define ACTION(name) \ + class name##Action { \ + public: \ + explicit name##Action() noexcept {} \ + name##Action(const name##Action&) noexcept {} \ + template <typename F> \ + operator ::testing::Action<F>() const { \ + return ::testing::internal::MakeAction<F, gmock_Impl>(); \ + } \ + private: \ + class gmock_Impl { \ + public: \ + template <typename function_type, typename return_type, \ + typename args_type, GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \ + return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \ + }; \ + }; \ + inline name##Action name() GTEST_MUST_USE_RESULT_; \ + inline name##Action name() { return name##Action(); } \ + template <typename function_type, typename return_type, typename args_type, \ + GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \ + return_type name##Action::gmock_Impl::gmock_PerformImpl( \ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP, (__VA_ARGS__)) + +#define ACTION_P2(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP2, (__VA_ARGS__)) + +#define ACTION_P3(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP3, (__VA_ARGS__)) + +#define ACTION_P4(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP4, (__VA_ARGS__)) + +#define ACTION_P5(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP5, (__VA_ARGS__)) + +#define ACTION_P6(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP6, (__VA_ARGS__)) + +#define ACTION_P7(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP7, (__VA_ARGS__)) + +#define ACTION_P8(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP8, (__VA_ARGS__)) + +#define ACTION_P9(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP9, (__VA_ARGS__)) + +#define ACTION_P10(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP10, (__VA_ARGS__)) + +} // namespace testing + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used cardinalities. More +// cardinalities can be defined by the user implementing the +// CardinalityInterface interface if necessary. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ + +#include <limits.h> +#include <memory> +#include <ostream> // NOLINT + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// To implement a cardinality Foo, define: +// 1. a class FooCardinality that implements the +// CardinalityInterface interface, and +// 2. a factory function that creates a Cardinality object from a +// const FooCardinality*. +// +// The two-level delegation design follows that of Matcher, providing +// consistency for extension developers. It also eases ownership +// management as Cardinality objects can now be copied like plain values. + +// The implementation of a cardinality. +class CardinalityInterface { + public: + virtual ~CardinalityInterface() {} + + // Conservative estimate on the lower/upper bound of the number of + // calls allowed. + virtual int ConservativeLowerBound() const { return 0; } + virtual int ConservativeUpperBound() const { return INT_MAX; } + + // Returns true if and only if call_count calls will satisfy this + // cardinality. + virtual bool IsSatisfiedByCallCount(int call_count) const = 0; + + // Returns true if and only if call_count calls will saturate this + // cardinality. + virtual bool IsSaturatedByCallCount(int call_count) const = 0; + + // Describes self to an ostream. + virtual void DescribeTo(::std::ostream* os) const = 0; +}; + +// A Cardinality is a copyable and IMMUTABLE (except by assignment) +// object that specifies how many times a mock function is expected to +// be called. The implementation of Cardinality is just a std::shared_ptr +// to const CardinalityInterface. Don't inherit from Cardinality! +class GTEST_API_ Cardinality { + public: + // Constructs a null cardinality. Needed for storing Cardinality + // objects in STL containers. + Cardinality() {} + + // Constructs a Cardinality from its implementation. + explicit Cardinality(const CardinalityInterface* impl) : impl_(impl) {} + + // Conservative estimate on the lower/upper bound of the number of + // calls allowed. + int ConservativeLowerBound() const { return impl_->ConservativeLowerBound(); } + int ConservativeUpperBound() const { return impl_->ConservativeUpperBound(); } + + // Returns true if and only if call_count calls will satisfy this + // cardinality. + bool IsSatisfiedByCallCount(int call_count) const { + return impl_->IsSatisfiedByCallCount(call_count); + } + + // Returns true if and only if call_count calls will saturate this + // cardinality. + bool IsSaturatedByCallCount(int call_count) const { + return impl_->IsSaturatedByCallCount(call_count); + } + + // Returns true if and only if call_count calls will over-saturate this + // cardinality, i.e. exceed the maximum number of allowed calls. + bool IsOverSaturatedByCallCount(int call_count) const { + return impl_->IsSaturatedByCallCount(call_count) && + !impl_->IsSatisfiedByCallCount(call_count); + } + + // Describes self to an ostream + void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); } + + // Describes the given actual call count to an ostream. + static void DescribeActualCallCountTo(int actual_call_count, + ::std::ostream* os); + + private: + std::shared_ptr<const CardinalityInterface> impl_; +}; + +// Creates a cardinality that allows at least n calls. +GTEST_API_ Cardinality AtLeast(int n); + +// Creates a cardinality that allows at most n calls. +GTEST_API_ Cardinality AtMost(int n); + +// Creates a cardinality that allows any number of calls. +GTEST_API_ Cardinality AnyNumber(); + +// Creates a cardinality that allows between min and max calls. +GTEST_API_ Cardinality Between(int min, int max); + +// Creates a cardinality that allows exactly n calls. +GTEST_API_ Cardinality Exactly(int n); + +// Creates a cardinality from its implementation. +inline Cardinality MakeCardinality(const CardinalityInterface* c) { + return Cardinality(c); +} + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements MOCK_METHOD. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_ // NOLINT +#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_ // NOLINT + +#include <type_traits> // IWYU pragma: keep +#include <utility> // IWYU pragma: keep + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements the ON_CALL() and EXPECT_CALL() macros. +// +// A user can use the ON_CALL() macro to specify the default action of +// a mock method. The syntax is: +// +// ON_CALL(mock_object, Method(argument-matchers)) +// .With(multi-argument-matcher) +// .WillByDefault(action); +// +// where the .With() clause is optional. +// +// A user can use the EXPECT_CALL() macro to specify an expectation on +// a mock method. The syntax is: +// +// EXPECT_CALL(mock_object, Method(argument-matchers)) +// .With(multi-argument-matchers) +// .Times(cardinality) +// .InSequence(sequences) +// .After(expectations) +// .WillOnce(action) +// .WillRepeatedly(action) +// .RetiresOnSaturation(); +// +// where all clauses are optional, and .InSequence()/.After()/ +// .WillOnce() can appear any number of times. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_ + +#include <functional> +#include <map> +#include <memory> +#include <set> +#include <sstream> +#include <string> +#include <type_traits> +#include <utility> +#include <vector> +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Google Mock - a framework for writing C++ mock classes. +// +// The MATCHER* family of macros can be used in a namespace scope to +// define custom matchers easily. +// +// Basic Usage +// =========== +// +// The syntax +// +// MATCHER(name, description_string) { statements; } +// +// defines a matcher with the given name that executes the statements, +// which must return a bool to indicate if the match succeeds. Inside +// the statements, you can refer to the value being matched by 'arg', +// and refer to its type by 'arg_type'. +// +// The description string documents what the matcher does, and is used +// to generate the failure message when the match fails. Since a +// MATCHER() is usually defined in a header file shared by multiple +// C++ source files, we require the description to be a C-string +// literal to avoid possible side effects. It can be empty, in which +// case we'll use the sequence of words in the matcher name as the +// description. +// +// For example: +// +// MATCHER(IsEven, "") { return (arg % 2) == 0; } +// +// allows you to write +// +// // Expects mock_foo.Bar(n) to be called where n is even. +// EXPECT_CALL(mock_foo, Bar(IsEven())); +// +// or, +// +// // Verifies that the value of some_expression is even. +// EXPECT_THAT(some_expression, IsEven()); +// +// If the above assertion fails, it will print something like: +// +// Value of: some_expression +// Expected: is even +// Actual: 7 +// +// where the description "is even" is automatically calculated from the +// matcher name IsEven. +// +// Argument Type +// ============= +// +// Note that the type of the value being matched (arg_type) is +// determined by the context in which you use the matcher and is +// supplied to you by the compiler, so you don't need to worry about +// declaring it (nor can you). This allows the matcher to be +// polymorphic. For example, IsEven() can be used to match any type +// where the value of "(arg % 2) == 0" can be implicitly converted to +// a bool. In the "Bar(IsEven())" example above, if method Bar() +// takes an int, 'arg_type' will be int; if it takes an unsigned long, +// 'arg_type' will be unsigned long; and so on. +// +// Parameterizing Matchers +// ======================= +// +// Sometimes you'll want to parameterize the matcher. For that you +// can use another macro: +// +// MATCHER_P(name, param_name, description_string) { statements; } +// +// For example: +// +// MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } +// +// will allow you to write: +// +// EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); +// +// which may lead to this message (assuming n is 10): +// +// Value of: Blah("a") +// Expected: has absolute value 10 +// Actual: -9 +// +// Note that both the matcher description and its parameter are +// printed, making the message human-friendly. +// +// In the matcher definition body, you can write 'foo_type' to +// reference the type of a parameter named 'foo'. For example, in the +// body of MATCHER_P(HasAbsoluteValue, value) above, you can write +// 'value_type' to refer to the type of 'value'. +// +// We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P$n to +// support multi-parameter matchers. +// +// Describing Parameterized Matchers +// ================================= +// +// The last argument to MATCHER*() is a string-typed expression. The +// expression can reference all of the matcher's parameters and a +// special bool-typed variable named 'negation'. When 'negation' is +// false, the expression should evaluate to the matcher's description; +// otherwise it should evaluate to the description of the negation of +// the matcher. For example, +// +// using testing::PrintToString; +// +// MATCHER_P2(InClosedRange, low, hi, +// std::string(negation ? "is not" : "is") + " in range [" + +// PrintToString(low) + ", " + PrintToString(hi) + "]") { +// return low <= arg && arg <= hi; +// } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: is in range [4, 6] +// ... +// Expected: is not in range [2, 4] +// +// If you specify "" as the description, the failure message will +// contain the sequence of words in the matcher name followed by the +// parameter values printed as a tuple. For example, +// +// MATCHER_P2(InClosedRange, low, hi, "") { ... } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: in closed range (4, 6) +// ... +// Expected: not (in closed range (2, 4)) +// +// Types of Matcher Parameters +// =========================== +// +// For the purpose of typing, you can view +// +// MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } +// +// as shorthand for +// +// template <typename p1_type, ..., typename pk_type> +// FooMatcherPk<p1_type, ..., pk_type> +// Foo(p1_type p1, ..., pk_type pk) { ... } +// +// When you write Foo(v1, ..., vk), the compiler infers the types of +// the parameters v1, ..., and vk for you. If you are not happy with +// the result of the type inference, you can specify the types by +// explicitly instantiating the template, as in Foo<long, bool>(5, +// false). As said earlier, you don't get to (or need to) specify +// 'arg_type' as that's determined by the context in which the matcher +// is used. You can assign the result of expression Foo(p1, ..., pk) +// to a variable of type FooMatcherPk<p1_type, ..., pk_type>. This +// can be useful when composing matchers. +// +// While you can instantiate a matcher template with reference types, +// passing the parameters by pointer usually makes your code more +// readable. If, however, you still want to pass a parameter by +// reference, be aware that in the failure message generated by the +// matcher you will see the value of the referenced object but not its +// address. +// +// Explaining Match Results +// ======================== +// +// Sometimes the matcher description alone isn't enough to explain why +// the match has failed or succeeded. For example, when expecting a +// long string, it can be very helpful to also print the diff between +// the expected string and the actual one. To achieve that, you can +// optionally stream additional information to a special variable +// named result_listener, whose type is a pointer to class +// MatchResultListener: +// +// MATCHER_P(EqualsLongString, str, "") { +// if (arg == str) return true; +// +// *result_listener << "the difference: " +/// << DiffStrings(str, arg); +// return false; +// } +// +// Overloading Matchers +// ==================== +// +// You can overload matchers with different numbers of parameters: +// +// MATCHER_P(Blah, a, description_string1) { ... } +// MATCHER_P2(Blah, a, b, description_string2) { ... } +// +// Caveats +// ======= +// +// When defining a new matcher, you should also consider implementing +// MatcherInterface or using MakePolymorphicMatcher(). These +// approaches require more work than the MATCHER* macros, but also +// give you more control on the types of the value being matched and +// the matcher parameters, which may leads to better compiler error +// messages when the matcher is used wrong. They also allow +// overloading matchers based on parameter types (as opposed to just +// based on the number of parameters). +// +// MATCHER*() can only be used in a namespace scope as templates cannot be +// declared inside of a local class. +// +// More Information +// ================ +// +// To learn more about using these macros, please search for 'MATCHER' +// on +// https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md +// +// This file also implements some commonly used argument matchers. More +// matchers can be defined by the user implementing the +// MatcherInterface<T> interface if necessary. +// +// See googletest/include/gtest/gtest-matchers.h for the definition of class +// Matcher, class MatcherInterface, and others. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ + +#include <algorithm> +#include <cmath> +#include <initializer_list> +#include <iterator> +#include <limits> +#include <memory> +#include <ostream> // NOLINT +#include <sstream> +#include <string> +#include <type_traits> +#include <utility> +#include <vector> + + +// MSVC warning C5046 is new as of VS2017 version 15.8. +#if defined(_MSC_VER) && _MSC_VER >= 1915 +#define GMOCK_MAYBE_5046_ 5046 +#else +#define GMOCK_MAYBE_5046_ +#endif + +GTEST_DISABLE_MSC_WARNINGS_PUSH_( + 4251 GMOCK_MAYBE_5046_ /* class A needs to have dll-interface to be used by + clients of class B */ + /* Symbol involving type with internal linkage not defined */) + +namespace testing { + +// To implement a matcher Foo for type T, define: +// 1. a class FooMatcherImpl that implements the +// MatcherInterface<T> interface, and +// 2. a factory function that creates a Matcher<T> object from a +// FooMatcherImpl*. +// +// The two-level delegation design makes it possible to allow a user +// to write "v" instead of "Eq(v)" where a Matcher is expected, which +// is impossible if we pass matchers by pointers. It also eases +// ownership management as Matcher objects can now be copied like +// plain values. + +// A match result listener that stores the explanation in a string. +class StringMatchResultListener : public MatchResultListener { + public: + StringMatchResultListener() : MatchResultListener(&ss_) {} + + // Returns the explanation accumulated so far. + std::string str() const { return ss_.str(); } + + // Clears the explanation accumulated so far. + void Clear() { ss_.str(""); } + + private: + ::std::stringstream ss_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StringMatchResultListener); +}; + +// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION +// and MUST NOT BE USED IN USER CODE!!! +namespace internal { + +// The MatcherCastImpl class template is a helper for implementing +// MatcherCast(). We need this helper in order to partially +// specialize the implementation of MatcherCast() (C++ allows +// class/struct templates to be partially specialized, but not +// function templates.). + +// This general version is used when MatcherCast()'s argument is a +// polymorphic matcher (i.e. something that can be converted to a +// Matcher but is not one yet; for example, Eq(value)) or a value (for +// example, "hello"). +template <typename T, typename M> +class MatcherCastImpl { + public: + static Matcher<T> Cast(const M& polymorphic_matcher_or_value) { + // M can be a polymorphic matcher, in which case we want to use + // its conversion operator to create Matcher<T>. Or it can be a value + // that should be passed to the Matcher<T>'s constructor. + // + // We can't call Matcher<T>(polymorphic_matcher_or_value) when M is a + // polymorphic matcher because it'll be ambiguous if T has an implicit + // constructor from M (this usually happens when T has an implicit + // constructor from any type). + // + // It won't work to unconditionally implicit_cast + // polymorphic_matcher_or_value to Matcher<T> because it won't trigger + // a user-defined conversion from M to T if one exists (assuming M is + // a value). + return CastImpl(polymorphic_matcher_or_value, + std::is_convertible<M, Matcher<T>>{}, + std::is_convertible<M, T>{}); + } + + private: + template <bool Ignore> + static Matcher<T> CastImpl(const M& polymorphic_matcher_or_value, + std::true_type /* convertible_to_matcher */, + std::integral_constant<bool, Ignore>) { + // M is implicitly convertible to Matcher<T>, which means that either + // M is a polymorphic matcher or Matcher<T> has an implicit constructor + // from M. In both cases using the implicit conversion will produce a + // matcher. + // + // Even if T has an implicit constructor from M, it won't be called because + // creating Matcher<T> would require a chain of two user-defined conversions + // (first to create T from M and then to create Matcher<T> from T). + return polymorphic_matcher_or_value; + } + + // M can't be implicitly converted to Matcher<T>, so M isn't a polymorphic + // matcher. It's a value of a type implicitly convertible to T. Use direct + // initialization to create a matcher. + static Matcher<T> CastImpl(const M& value, + std::false_type /* convertible_to_matcher */, + std::true_type /* convertible_to_T */) { + return Matcher<T>(ImplicitCast_<T>(value)); + } + + // M can't be implicitly converted to either Matcher<T> or T. Attempt to use + // polymorphic matcher Eq(value) in this case. + // + // Note that we first attempt to perform an implicit cast on the value and + // only fall back to the polymorphic Eq() matcher afterwards because the + // latter calls bool operator==(const Lhs& lhs, const Rhs& rhs) in the end + // which might be undefined even when Rhs is implicitly convertible to Lhs + // (e.g. std::pair<const int, int> vs. std::pair<int, int>). + // + // We don't define this method inline as we need the declaration of Eq(). + static Matcher<T> CastImpl(const M& value, + std::false_type /* convertible_to_matcher */, + std::false_type /* convertible_to_T */); +}; + +// This more specialized version is used when MatcherCast()'s argument +// is already a Matcher. This only compiles when type T can be +// statically converted to type U. +template <typename T, typename U> +class MatcherCastImpl<T, Matcher<U> > { + public: + static Matcher<T> Cast(const Matcher<U>& source_matcher) { + return Matcher<T>(new Impl(source_matcher)); + } + + private: + class Impl : public MatcherInterface<T> { + public: + explicit Impl(const Matcher<U>& source_matcher) + : source_matcher_(source_matcher) {} + + // We delegate the matching logic to the source matcher. + bool MatchAndExplain(T x, MatchResultListener* listener) const override { + using FromType = typename std::remove_cv<typename std::remove_pointer< + typename std::remove_reference<T>::type>::type>::type; + using ToType = typename std::remove_cv<typename std::remove_pointer< + typename std::remove_reference<U>::type>::type>::type; + // Do not allow implicitly converting base*/& to derived*/&. + static_assert( + // Do not trigger if only one of them is a pointer. That implies a + // regular conversion and not a down_cast. + (std::is_pointer<typename std::remove_reference<T>::type>::value != + std::is_pointer<typename std::remove_reference<U>::type>::value) || + std::is_same<FromType, ToType>::value || + !std::is_base_of<FromType, ToType>::value, + "Can't implicitly convert from <base> to <derived>"); + + // Do the cast to `U` explicitly if necessary. + // Otherwise, let implicit conversions do the trick. + using CastType = + typename std::conditional<std::is_convertible<T&, const U&>::value, + T&, U>::type; + + return source_matcher_.MatchAndExplain(static_cast<CastType>(x), + listener); + } + + void DescribeTo(::std::ostream* os) const override { + source_matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + source_matcher_.DescribeNegationTo(os); + } + + private: + const Matcher<U> source_matcher_; + }; +}; + +// This even more specialized version is used for efficiently casting +// a matcher to its own type. +template <typename T> +class MatcherCastImpl<T, Matcher<T> > { + public: + static Matcher<T> Cast(const Matcher<T>& matcher) { return matcher; } +}; + +// Template specialization for parameterless Matcher. +template <typename Derived> +class MatcherBaseImpl { + public: + MatcherBaseImpl() = default; + + template <typename T> + operator ::testing::Matcher<T>() const { // NOLINT(runtime/explicit) + return ::testing::Matcher<T>(new + typename Derived::template gmock_Impl<T>()); + } +}; + +// Template specialization for Matcher with parameters. +template <template <typename...> class Derived, typename... Ts> +class MatcherBaseImpl<Derived<Ts...>> { + public: + // Mark the constructor explicit for single argument T to avoid implicit + // conversions. + template <typename E = std::enable_if<sizeof...(Ts) == 1>, + typename E::type* = nullptr> + explicit MatcherBaseImpl(Ts... params) + : params_(std::forward<Ts>(params)...) {} + template <typename E = std::enable_if<sizeof...(Ts) != 1>, + typename = typename E::type> + MatcherBaseImpl(Ts... params) // NOLINT + : params_(std::forward<Ts>(params)...) {} + + template <typename F> + operator ::testing::Matcher<F>() const { // NOLINT(runtime/explicit) + return Apply<F>(MakeIndexSequence<sizeof...(Ts)>{}); + } + + private: + template <typename F, std::size_t... tuple_ids> + ::testing::Matcher<F> Apply(IndexSequence<tuple_ids...>) const { + return ::testing::Matcher<F>( + new typename Derived<Ts...>::template gmock_Impl<F>( + std::get<tuple_ids>(params_)...)); + } + + const std::tuple<Ts...> params_; +}; + +} // namespace internal + +// In order to be safe and clear, casting between different matcher +// types is done explicitly via MatcherCast<T>(m), which takes a +// matcher m and returns a Matcher<T>. It compiles only when T can be +// statically converted to the argument type of m. +template <typename T, typename M> +inline Matcher<T> MatcherCast(const M& matcher) { + return internal::MatcherCastImpl<T, M>::Cast(matcher); +} + +// This overload handles polymorphic matchers and values only since +// monomorphic matchers are handled by the next one. +template <typename T, typename M> +inline Matcher<T> SafeMatcherCast(const M& polymorphic_matcher_or_value) { + return MatcherCast<T>(polymorphic_matcher_or_value); +} + +// This overload handles monomorphic matchers. +// +// In general, if type T can be implicitly converted to type U, we can +// safely convert a Matcher<U> to a Matcher<T> (i.e. Matcher is +// contravariant): just keep a copy of the original Matcher<U>, convert the +// argument from type T to U, and then pass it to the underlying Matcher<U>. +// The only exception is when U is a reference and T is not, as the +// underlying Matcher<U> may be interested in the argument's address, which +// is not preserved in the conversion from T to U. +template <typename T, typename U> +inline Matcher<T> SafeMatcherCast(const Matcher<U>& matcher) { + // Enforce that T can be implicitly converted to U. + static_assert(std::is_convertible<const T&, const U&>::value, + "T must be implicitly convertible to U"); + // Enforce that we are not converting a non-reference type T to a reference + // type U. + GTEST_COMPILE_ASSERT_( + std::is_reference<T>::value || !std::is_reference<U>::value, + cannot_convert_non_reference_arg_to_reference); + // In case both T and U are arithmetic types, enforce that the + // conversion is not lossy. + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT; + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(U) RawU; + constexpr bool kTIsOther = GMOCK_KIND_OF_(RawT) == internal::kOther; + constexpr bool kUIsOther = GMOCK_KIND_OF_(RawU) == internal::kOther; + GTEST_COMPILE_ASSERT_( + kTIsOther || kUIsOther || + (internal::LosslessArithmeticConvertible<RawT, RawU>::value), + conversion_of_arithmetic_types_must_be_lossless); + return MatcherCast<T>(matcher); +} + +// A<T>() returns a matcher that matches any value of type T. +template <typename T> +Matcher<T> A(); + +// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION +// and MUST NOT BE USED IN USER CODE!!! +namespace internal { + +// If the explanation is not empty, prints it to the ostream. +inline void PrintIfNotEmpty(const std::string& explanation, + ::std::ostream* os) { + if (explanation != "" && os != nullptr) { + *os << ", " << explanation; + } +} + +// Returns true if the given type name is easy to read by a human. +// This is used to decide whether printing the type of a value might +// be helpful. +inline bool IsReadableTypeName(const std::string& type_name) { + // We consider a type name readable if it's short or doesn't contain + // a template or function type. + return (type_name.length() <= 20 || + type_name.find_first_of("<(") == std::string::npos); +} + +// Matches the value against the given matcher, prints the value and explains +// the match result to the listener. Returns the match result. +// 'listener' must not be NULL. +// Value cannot be passed by const reference, because some matchers take a +// non-const argument. +template <typename Value, typename T> +bool MatchPrintAndExplain(Value& value, const Matcher<T>& matcher, + MatchResultListener* listener) { + if (!listener->IsInterested()) { + // If the listener is not interested, we do not need to construct the + // inner explanation. + return matcher.Matches(value); + } + + StringMatchResultListener inner_listener; + const bool match = matcher.MatchAndExplain(value, &inner_listener); + + UniversalPrint(value, listener->stream()); +#if GTEST_HAS_RTTI + const std::string& type_name = GetTypeName<Value>(); + if (IsReadableTypeName(type_name)) + *listener->stream() << " (of type " << type_name << ")"; +#endif + PrintIfNotEmpty(inner_listener.str(), listener->stream()); + + return match; +} + +// An internal helper class for doing compile-time loop on a tuple's +// fields. +template <size_t N> +class TuplePrefix { + public: + // TuplePrefix<N>::Matches(matcher_tuple, value_tuple) returns true + // if and only if the first N fields of matcher_tuple matches + // the first N fields of value_tuple, respectively. + template <typename MatcherTuple, typename ValueTuple> + static bool Matches(const MatcherTuple& matcher_tuple, + const ValueTuple& value_tuple) { + return TuplePrefix<N - 1>::Matches(matcher_tuple, value_tuple) && + std::get<N - 1>(matcher_tuple).Matches(std::get<N - 1>(value_tuple)); + } + + // TuplePrefix<N>::ExplainMatchFailuresTo(matchers, values, os) + // describes failures in matching the first N fields of matchers + // against the first N fields of values. If there is no failure, + // nothing will be streamed to os. + template <typename MatcherTuple, typename ValueTuple> + static void ExplainMatchFailuresTo(const MatcherTuple& matchers, + const ValueTuple& values, + ::std::ostream* os) { + // First, describes failures in the first N - 1 fields. + TuplePrefix<N - 1>::ExplainMatchFailuresTo(matchers, values, os); + + // Then describes the failure (if any) in the (N - 1)-th (0-based) + // field. + typename std::tuple_element<N - 1, MatcherTuple>::type matcher = + std::get<N - 1>(matchers); + typedef typename std::tuple_element<N - 1, ValueTuple>::type Value; + const Value& value = std::get<N - 1>(values); + StringMatchResultListener listener; + if (!matcher.MatchAndExplain(value, &listener)) { + *os << " Expected arg #" << N - 1 << ": "; + std::get<N - 1>(matchers).DescribeTo(os); + *os << "\n Actual: "; + // We remove the reference in type Value to prevent the + // universal printer from printing the address of value, which + // isn't interesting to the user most of the time. The + // matcher's MatchAndExplain() method handles the case when + // the address is interesting. + internal::UniversalPrint(value, os); + PrintIfNotEmpty(listener.str(), os); + *os << "\n"; + } + } +}; + +// The base case. +template <> +class TuplePrefix<0> { + public: + template <typename MatcherTuple, typename ValueTuple> + static bool Matches(const MatcherTuple& /* matcher_tuple */, + const ValueTuple& /* value_tuple */) { + return true; + } + + template <typename MatcherTuple, typename ValueTuple> + static void ExplainMatchFailuresTo(const MatcherTuple& /* matchers */, + const ValueTuple& /* values */, + ::std::ostream* /* os */) {} +}; + +// TupleMatches(matcher_tuple, value_tuple) returns true if and only if +// all matchers in matcher_tuple match the corresponding fields in +// value_tuple. It is a compiler error if matcher_tuple and +// value_tuple have different number of fields or incompatible field +// types. +template <typename MatcherTuple, typename ValueTuple> +bool TupleMatches(const MatcherTuple& matcher_tuple, + const ValueTuple& value_tuple) { + // Makes sure that matcher_tuple and value_tuple have the same + // number of fields. + GTEST_COMPILE_ASSERT_(std::tuple_size<MatcherTuple>::value == + std::tuple_size<ValueTuple>::value, + matcher_and_value_have_different_numbers_of_fields); + return TuplePrefix<std::tuple_size<ValueTuple>::value>::Matches(matcher_tuple, + value_tuple); +} + +// Describes failures in matching matchers against values. If there +// is no failure, nothing will be streamed to os. +template <typename MatcherTuple, typename ValueTuple> +void ExplainMatchFailureTupleTo(const MatcherTuple& matchers, + const ValueTuple& values, + ::std::ostream* os) { + TuplePrefix<std::tuple_size<MatcherTuple>::value>::ExplainMatchFailuresTo( + matchers, values, os); +} + +// TransformTupleValues and its helper. +// +// TransformTupleValuesHelper hides the internal machinery that +// TransformTupleValues uses to implement a tuple traversal. +template <typename Tuple, typename Func, typename OutIter> +class TransformTupleValuesHelper { + private: + typedef ::std::tuple_size<Tuple> TupleSize; + + public: + // For each member of tuple 't', taken in order, evaluates '*out++ = f(t)'. + // Returns the final value of 'out' in case the caller needs it. + static OutIter Run(Func f, const Tuple& t, OutIter out) { + return IterateOverTuple<Tuple, TupleSize::value>()(f, t, out); + } + + private: + template <typename Tup, size_t kRemainingSize> + struct IterateOverTuple { + OutIter operator() (Func f, const Tup& t, OutIter out) const { + *out++ = f(::std::get<TupleSize::value - kRemainingSize>(t)); + return IterateOverTuple<Tup, kRemainingSize - 1>()(f, t, out); + } + }; + template <typename Tup> + struct IterateOverTuple<Tup, 0> { + OutIter operator() (Func /* f */, const Tup& /* t */, OutIter out) const { + return out; + } + }; +}; + +// Successively invokes 'f(element)' on each element of the tuple 't', +// appending each result to the 'out' iterator. Returns the final value +// of 'out'. +template <typename Tuple, typename Func, typename OutIter> +OutIter TransformTupleValues(Func f, const Tuple& t, OutIter out) { + return TransformTupleValuesHelper<Tuple, Func, OutIter>::Run(f, t, out); +} + +// Implements _, a matcher that matches any value of any +// type. This is a polymorphic matcher, so we need a template type +// conversion operator to make it appearing as a Matcher<T> for any +// type T. +class AnythingMatcher { + public: + using is_gtest_matcher = void; + + template <typename T> + bool MatchAndExplain(const T& /* x */, std::ostream* /* listener */) const { + return true; + } + void DescribeTo(std::ostream* os) const { *os << "is anything"; } + void DescribeNegationTo(::std::ostream* os) const { + // This is mostly for completeness' sake, as it's not very useful + // to write Not(A<bool>()). However we cannot completely rule out + // such a possibility, and it doesn't hurt to be prepared. + *os << "never matches"; + } +}; + +// Implements the polymorphic IsNull() matcher, which matches any raw or smart +// pointer that is NULL. +class IsNullMatcher { + public: + template <typename Pointer> + bool MatchAndExplain(const Pointer& p, + MatchResultListener* /* listener */) const { + return p == nullptr; + } + + void DescribeTo(::std::ostream* os) const { *os << "is NULL"; } + void DescribeNegationTo(::std::ostream* os) const { + *os << "isn't NULL"; + } +}; + +// Implements the polymorphic NotNull() matcher, which matches any raw or smart +// pointer that is not NULL. +class NotNullMatcher { + public: + template <typename Pointer> + bool MatchAndExplain(const Pointer& p, + MatchResultListener* /* listener */) const { + return p != nullptr; + } + + void DescribeTo(::std::ostream* os) const { *os << "isn't NULL"; } + void DescribeNegationTo(::std::ostream* os) const { + *os << "is NULL"; + } +}; + +// Ref(variable) matches any argument that is a reference to +// 'variable'. This matcher is polymorphic as it can match any +// super type of the type of 'variable'. +// +// The RefMatcher template class implements Ref(variable). It can +// only be instantiated with a reference type. This prevents a user +// from mistakenly using Ref(x) to match a non-reference function +// argument. For example, the following will righteously cause a +// compiler error: +// +// int n; +// Matcher<int> m1 = Ref(n); // This won't compile. +// Matcher<int&> m2 = Ref(n); // This will compile. +template <typename T> +class RefMatcher; + +template <typename T> +class RefMatcher<T&> { + // Google Mock is a generic framework and thus needs to support + // mocking any function types, including those that take non-const + // reference arguments. Therefore the template parameter T (and + // Super below) can be instantiated to either a const type or a + // non-const type. + public: + // RefMatcher() takes a T& instead of const T&, as we want the + // compiler to catch using Ref(const_value) as a matcher for a + // non-const reference. + explicit RefMatcher(T& x) : object_(x) {} // NOLINT + + template <typename Super> + operator Matcher<Super&>() const { + // By passing object_ (type T&) to Impl(), which expects a Super&, + // we make sure that Super is a super type of T. In particular, + // this catches using Ref(const_value) as a matcher for a + // non-const reference, as you cannot implicitly convert a const + // reference to a non-const reference. + return MakeMatcher(new Impl<Super>(object_)); + } + + private: + template <typename Super> + class Impl : public MatcherInterface<Super&> { + public: + explicit Impl(Super& x) : object_(x) {} // NOLINT + + // MatchAndExplain() takes a Super& (as opposed to const Super&) + // in order to match the interface MatcherInterface<Super&>. + bool MatchAndExplain(Super& x, + MatchResultListener* listener) const override { + *listener << "which is located @" << static_cast<const void*>(&x); + return &x == &object_; + } + + void DescribeTo(::std::ostream* os) const override { + *os << "references the variable "; + UniversalPrinter<Super&>::Print(object_, os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "does not reference the variable "; + UniversalPrinter<Super&>::Print(object_, os); + } + + private: + const Super& object_; + }; + + T& object_; +}; + +// Polymorphic helper functions for narrow and wide string matchers. +inline bool CaseInsensitiveCStringEquals(const char* lhs, const char* rhs) { + return String::CaseInsensitiveCStringEquals(lhs, rhs); +} + +inline bool CaseInsensitiveCStringEquals(const wchar_t* lhs, + const wchar_t* rhs) { + return String::CaseInsensitiveWideCStringEquals(lhs, rhs); +} + +// String comparison for narrow or wide strings that can have embedded NUL +// characters. +template <typename StringType> +bool CaseInsensitiveStringEquals(const StringType& s1, + const StringType& s2) { + // Are the heads equal? + if (!CaseInsensitiveCStringEquals(s1.c_str(), s2.c_str())) { + return false; + } + + // Skip the equal heads. + const typename StringType::value_type nul = 0; + const size_t i1 = s1.find(nul), i2 = s2.find(nul); + + // Are we at the end of either s1 or s2? + if (i1 == StringType::npos || i2 == StringType::npos) { + return i1 == i2; + } + + // Are the tails equal? + return CaseInsensitiveStringEquals(s1.substr(i1 + 1), s2.substr(i2 + 1)); +} + +// String matchers. + +// Implements equality-based string matchers like StrEq, StrCaseNe, and etc. +template <typename StringType> +class StrEqualityMatcher { + public: + StrEqualityMatcher(StringType str, bool expect_eq, bool case_sensitive) + : string_(std::move(str)), + expect_eq_(expect_eq), + case_sensitive_(case_sensitive) {} + +#if GTEST_INTERNAL_HAS_STRING_VIEW + bool MatchAndExplain(const internal::StringView& s, + MatchResultListener* listener) const { + // This should fail to compile if StringView is used with wide + // strings. + const StringType& str = std::string(s); + return MatchAndExplain(str, listener); + } +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template <typename CharType> + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + if (s == nullptr) { + return !expect_eq_; + } + return MatchAndExplain(StringType(s), listener); + } + + // Matches anything that can convert to StringType. + // + // This is a template, not just a plain function with const StringType&, + // because StringView has some interfering non-explicit constructors. + template <typename MatcheeStringType> + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const StringType s2(s); + const bool eq = case_sensitive_ ? s2 == string_ : + CaseInsensitiveStringEquals(s2, string_); + return expect_eq_ == eq; + } + + void DescribeTo(::std::ostream* os) const { + DescribeToHelper(expect_eq_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + DescribeToHelper(!expect_eq_, os); + } + + private: + void DescribeToHelper(bool expect_eq, ::std::ostream* os) const { + *os << (expect_eq ? "is " : "isn't "); + *os << "equal to "; + if (!case_sensitive_) { + *os << "(ignoring case) "; + } + UniversalPrint(string_, os); + } + + const StringType string_; + const bool expect_eq_; + const bool case_sensitive_; +}; + +// Implements the polymorphic HasSubstr(substring) matcher, which +// can be used as a Matcher<T> as long as T can be converted to a +// string. +template <typename StringType> +class HasSubstrMatcher { + public: + explicit HasSubstrMatcher(const StringType& substring) + : substring_(substring) {} + +#if GTEST_INTERNAL_HAS_STRING_VIEW + bool MatchAndExplain(const internal::StringView& s, + MatchResultListener* listener) const { + // This should fail to compile if StringView is used with wide + // strings. + const StringType& str = std::string(s); + return MatchAndExplain(str, listener); + } +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template <typename CharType> + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != nullptr && MatchAndExplain(StringType(s), listener); + } + + // Matches anything that can convert to StringType. + // + // This is a template, not just a plain function with const StringType&, + // because StringView has some interfering non-explicit constructors. + template <typename MatcheeStringType> + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + return StringType(s).find(substring_) != StringType::npos; + } + + // Describes what this matcher matches. + void DescribeTo(::std::ostream* os) const { + *os << "has substring "; + UniversalPrint(substring_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "has no substring "; + UniversalPrint(substring_, os); + } + + private: + const StringType substring_; +}; + +// Implements the polymorphic StartsWith(substring) matcher, which +// can be used as a Matcher<T> as long as T can be converted to a +// string. +template <typename StringType> +class StartsWithMatcher { + public: + explicit StartsWithMatcher(const StringType& prefix) : prefix_(prefix) { + } + +#if GTEST_INTERNAL_HAS_STRING_VIEW + bool MatchAndExplain(const internal::StringView& s, + MatchResultListener* listener) const { + // This should fail to compile if StringView is used with wide + // strings. + const StringType& str = std::string(s); + return MatchAndExplain(str, listener); + } +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template <typename CharType> + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != nullptr && MatchAndExplain(StringType(s), listener); + } + + // Matches anything that can convert to StringType. + // + // This is a template, not just a plain function with const StringType&, + // because StringView has some interfering non-explicit constructors. + template <typename MatcheeStringType> + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const StringType& s2(s); + return s2.length() >= prefix_.length() && + s2.substr(0, prefix_.length()) == prefix_; + } + + void DescribeTo(::std::ostream* os) const { + *os << "starts with "; + UniversalPrint(prefix_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't start with "; + UniversalPrint(prefix_, os); + } + + private: + const StringType prefix_; +}; + +// Implements the polymorphic EndsWith(substring) matcher, which +// can be used as a Matcher<T> as long as T can be converted to a +// string. +template <typename StringType> +class EndsWithMatcher { + public: + explicit EndsWithMatcher(const StringType& suffix) : suffix_(suffix) {} + +#if GTEST_INTERNAL_HAS_STRING_VIEW + bool MatchAndExplain(const internal::StringView& s, + MatchResultListener* listener) const { + // This should fail to compile if StringView is used with wide + // strings. + const StringType& str = std::string(s); + return MatchAndExplain(str, listener); + } +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template <typename CharType> + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != nullptr && MatchAndExplain(StringType(s), listener); + } + + // Matches anything that can convert to StringType. + // + // This is a template, not just a plain function with const StringType&, + // because StringView has some interfering non-explicit constructors. + template <typename MatcheeStringType> + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const StringType& s2(s); + return s2.length() >= suffix_.length() && + s2.substr(s2.length() - suffix_.length()) == suffix_; + } + + void DescribeTo(::std::ostream* os) const { + *os << "ends with "; + UniversalPrint(suffix_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't end with "; + UniversalPrint(suffix_, os); + } + + private: + const StringType suffix_; +}; + +// Implements a matcher that compares the two fields of a 2-tuple +// using one of the ==, <=, <, etc, operators. The two fields being +// compared don't have to have the same type. +// +// The matcher defined here is polymorphic (for example, Eq() can be +// used to match a std::tuple<int, short>, a std::tuple<const long&, double>, +// etc). Therefore we use a template type conversion operator in the +// implementation. +template <typename D, typename Op> +class PairMatchBase { + public: + template <typename T1, typename T2> + operator Matcher<::std::tuple<T1, T2>>() const { + return Matcher<::std::tuple<T1, T2>>(new Impl<const ::std::tuple<T1, T2>&>); + } + template <typename T1, typename T2> + operator Matcher<const ::std::tuple<T1, T2>&>() const { + return MakeMatcher(new Impl<const ::std::tuple<T1, T2>&>); + } + + private: + static ::std::ostream& GetDesc(::std::ostream& os) { // NOLINT + return os << D::Desc(); + } + + template <typename Tuple> + class Impl : public MatcherInterface<Tuple> { + public: + bool MatchAndExplain(Tuple args, + MatchResultListener* /* listener */) const override { + return Op()(::std::get<0>(args), ::std::get<1>(args)); + } + void DescribeTo(::std::ostream* os) const override { + *os << "are " << GetDesc; + } + void DescribeNegationTo(::std::ostream* os) const override { + *os << "aren't " << GetDesc; + } + }; +}; + +class Eq2Matcher : public PairMatchBase<Eq2Matcher, AnyEq> { + public: + static const char* Desc() { return "an equal pair"; } +}; +class Ne2Matcher : public PairMatchBase<Ne2Matcher, AnyNe> { + public: + static const char* Desc() { return "an unequal pair"; } +}; +class Lt2Matcher : public PairMatchBase<Lt2Matcher, AnyLt> { + public: + static const char* Desc() { return "a pair where the first < the second"; } +}; +class Gt2Matcher : public PairMatchBase<Gt2Matcher, AnyGt> { + public: + static const char* Desc() { return "a pair where the first > the second"; } +}; +class Le2Matcher : public PairMatchBase<Le2Matcher, AnyLe> { + public: + static const char* Desc() { return "a pair where the first <= the second"; } +}; +class Ge2Matcher : public PairMatchBase<Ge2Matcher, AnyGe> { + public: + static const char* Desc() { return "a pair where the first >= the second"; } +}; + +// Implements the Not(...) matcher for a particular argument type T. +// We do not nest it inside the NotMatcher class template, as that +// will prevent different instantiations of NotMatcher from sharing +// the same NotMatcherImpl<T> class. +template <typename T> +class NotMatcherImpl : public MatcherInterface<const T&> { + public: + explicit NotMatcherImpl(const Matcher<T>& matcher) + : matcher_(matcher) {} + + bool MatchAndExplain(const T& x, + MatchResultListener* listener) const override { + return !matcher_.MatchAndExplain(x, listener); + } + + void DescribeTo(::std::ostream* os) const override { + matcher_.DescribeNegationTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + matcher_.DescribeTo(os); + } + + private: + const Matcher<T> matcher_; +}; + +// Implements the Not(m) matcher, which matches a value that doesn't +// match matcher m. +template <typename InnerMatcher> +class NotMatcher { + public: + explicit NotMatcher(InnerMatcher matcher) : matcher_(matcher) {} + + // This template type conversion operator allows Not(m) to be used + // to match any type m can match. + template <typename T> + operator Matcher<T>() const { + return Matcher<T>(new NotMatcherImpl<T>(SafeMatcherCast<T>(matcher_))); + } + + private: + InnerMatcher matcher_; +}; + +// Implements the AllOf(m1, m2) matcher for a particular argument type +// T. We do not nest it inside the BothOfMatcher class template, as +// that will prevent different instantiations of BothOfMatcher from +// sharing the same BothOfMatcherImpl<T> class. +template <typename T> +class AllOfMatcherImpl : public MatcherInterface<const T&> { + public: + explicit AllOfMatcherImpl(std::vector<Matcher<T> > matchers) + : matchers_(std::move(matchers)) {} + + void DescribeTo(::std::ostream* os) const override { + *os << "("; + for (size_t i = 0; i < matchers_.size(); ++i) { + if (i != 0) *os << ") and ("; + matchers_[i].DescribeTo(os); + } + *os << ")"; + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "("; + for (size_t i = 0; i < matchers_.size(); ++i) { + if (i != 0) *os << ") or ("; + matchers_[i].DescribeNegationTo(os); + } + *os << ")"; + } + + bool MatchAndExplain(const T& x, + MatchResultListener* listener) const override { + // If either matcher1_ or matcher2_ doesn't match x, we only need + // to explain why one of them fails. + std::string all_match_result; + + for (size_t i = 0; i < matchers_.size(); ++i) { + StringMatchResultListener slistener; + if (matchers_[i].MatchAndExplain(x, &slistener)) { + if (all_match_result.empty()) { + all_match_result = slistener.str(); + } else { + std::string result = slistener.str(); + if (!result.empty()) { + all_match_result += ", and "; + all_match_result += result; + } + } + } else { + *listener << slistener.str(); + return false; + } + } + + // Otherwise we need to explain why *both* of them match. + *listener << all_match_result; + return true; + } + + private: + const std::vector<Matcher<T> > matchers_; +}; + +// VariadicMatcher is used for the variadic implementation of +// AllOf(m_1, m_2, ...) and AnyOf(m_1, m_2, ...). +// CombiningMatcher<T> is used to recursively combine the provided matchers +// (of type Args...). +template <template <typename T> class CombiningMatcher, typename... Args> +class VariadicMatcher { + public: + VariadicMatcher(const Args&... matchers) // NOLINT + : matchers_(matchers...) { + static_assert(sizeof...(Args) > 0, "Must have at least one matcher."); + } + + VariadicMatcher(const VariadicMatcher&) = default; + VariadicMatcher& operator=(const VariadicMatcher&) = delete; + + // This template type conversion operator allows an + // VariadicMatcher<Matcher1, Matcher2...> object to match any type that + // all of the provided matchers (Matcher1, Matcher2, ...) can match. + template <typename T> + operator Matcher<T>() const { + std::vector<Matcher<T> > values; + CreateVariadicMatcher<T>(&values, std::integral_constant<size_t, 0>()); + return Matcher<T>(new CombiningMatcher<T>(std::move(values))); + } + + private: + template <typename T, size_t I> + void CreateVariadicMatcher(std::vector<Matcher<T> >* values, + std::integral_constant<size_t, I>) const { + values->push_back(SafeMatcherCast<T>(std::get<I>(matchers_))); + CreateVariadicMatcher<T>(values, std::integral_constant<size_t, I + 1>()); + } + + template <typename T> + void CreateVariadicMatcher( + std::vector<Matcher<T> >*, + std::integral_constant<size_t, sizeof...(Args)>) const {} + + std::tuple<Args...> matchers_; +}; + +template <typename... Args> +using AllOfMatcher = VariadicMatcher<AllOfMatcherImpl, Args...>; + +// Implements the AnyOf(m1, m2) matcher for a particular argument type +// T. We do not nest it inside the AnyOfMatcher class template, as +// that will prevent different instantiations of AnyOfMatcher from +// sharing the same EitherOfMatcherImpl<T> class. +template <typename T> +class AnyOfMatcherImpl : public MatcherInterface<const T&> { + public: + explicit AnyOfMatcherImpl(std::vector<Matcher<T> > matchers) + : matchers_(std::move(matchers)) {} + + void DescribeTo(::std::ostream* os) const override { + *os << "("; + for (size_t i = 0; i < matchers_.size(); ++i) { + if (i != 0) *os << ") or ("; + matchers_[i].DescribeTo(os); + } + *os << ")"; + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "("; + for (size_t i = 0; i < matchers_.size(); ++i) { + if (i != 0) *os << ") and ("; + matchers_[i].DescribeNegationTo(os); + } + *os << ")"; + } + + bool MatchAndExplain(const T& x, + MatchResultListener* listener) const override { + std::string no_match_result; + + // If either matcher1_ or matcher2_ matches x, we just need to + // explain why *one* of them matches. + for (size_t i = 0; i < matchers_.size(); ++i) { + StringMatchResultListener slistener; + if (matchers_[i].MatchAndExplain(x, &slistener)) { + *listener << slistener.str(); + return true; + } else { + if (no_match_result.empty()) { + no_match_result = slistener.str(); + } else { + std::string result = slistener.str(); + if (!result.empty()) { + no_match_result += ", and "; + no_match_result += result; + } + } + } + } + + // Otherwise we need to explain why *both* of them fail. + *listener << no_match_result; + return false; + } + + private: + const std::vector<Matcher<T> > matchers_; +}; + +// AnyOfMatcher is used for the variadic implementation of AnyOf(m_1, m_2, ...). +template <typename... Args> +using AnyOfMatcher = VariadicMatcher<AnyOfMatcherImpl, Args...>; + +// Wrapper for implementation of Any/AllOfArray(). +template <template <class> class MatcherImpl, typename T> +class SomeOfArrayMatcher { + public: + // Constructs the matcher from a sequence of element values or + // element matchers. + template <typename Iter> + SomeOfArrayMatcher(Iter first, Iter last) : matchers_(first, last) {} + + template <typename U> + operator Matcher<U>() const { // NOLINT + using RawU = typename std::decay<U>::type; + std::vector<Matcher<RawU>> matchers; + for (const auto& matcher : matchers_) { + matchers.push_back(MatcherCast<RawU>(matcher)); + } + return Matcher<U>(new MatcherImpl<RawU>(std::move(matchers))); + } + + private: + const ::std::vector<T> matchers_; +}; + +template <typename T> +using AllOfArrayMatcher = SomeOfArrayMatcher<AllOfMatcherImpl, T>; + +template <typename T> +using AnyOfArrayMatcher = SomeOfArrayMatcher<AnyOfMatcherImpl, T>; + +// Used for implementing Truly(pred), which turns a predicate into a +// matcher. +template <typename Predicate> +class TrulyMatcher { + public: + explicit TrulyMatcher(Predicate pred) : predicate_(pred) {} + + // This method template allows Truly(pred) to be used as a matcher + // for type T where T is the argument type of predicate 'pred'. The + // argument is passed by reference as the predicate may be + // interested in the address of the argument. + template <typename T> + bool MatchAndExplain(T& x, // NOLINT + MatchResultListener* listener) const { + // Without the if-statement, MSVC sometimes warns about converting + // a value to bool (warning 4800). + // + // We cannot write 'return !!predicate_(x);' as that doesn't work + // when predicate_(x) returns a class convertible to bool but + // having no operator!(). + if (predicate_(x)) + return true; + *listener << "didn't satisfy the given predicate"; + return false; + } + + void DescribeTo(::std::ostream* os) const { + *os << "satisfies the given predicate"; + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't satisfy the given predicate"; + } + + private: + Predicate predicate_; +}; + +// Used for implementing Matches(matcher), which turns a matcher into +// a predicate. +template <typename M> +class MatcherAsPredicate { + public: + explicit MatcherAsPredicate(M matcher) : matcher_(matcher) {} + + // This template operator() allows Matches(m) to be used as a + // predicate on type T where m is a matcher on type T. + // + // The argument x is passed by reference instead of by value, as + // some matcher may be interested in its address (e.g. as in + // Matches(Ref(n))(x)). + template <typename T> + bool operator()(const T& x) const { + // We let matcher_ commit to a particular type here instead of + // when the MatcherAsPredicate object was constructed. This + // allows us to write Matches(m) where m is a polymorphic matcher + // (e.g. Eq(5)). + // + // If we write Matcher<T>(matcher_).Matches(x) here, it won't + // compile when matcher_ has type Matcher<const T&>; if we write + // Matcher<const T&>(matcher_).Matches(x) here, it won't compile + // when matcher_ has type Matcher<T>; if we just write + // matcher_.Matches(x), it won't compile when matcher_ is + // polymorphic, e.g. Eq(5). + // + // MatcherCast<const T&>() is necessary for making the code work + // in all of the above situations. + return MatcherCast<const T&>(matcher_).Matches(x); + } + + private: + M matcher_; +}; + +// For implementing ASSERT_THAT() and EXPECT_THAT(). The template +// argument M must be a type that can be converted to a matcher. +template <typename M> +class PredicateFormatterFromMatcher { + public: + explicit PredicateFormatterFromMatcher(M m) : matcher_(std::move(m)) {} + + // This template () operator allows a PredicateFormatterFromMatcher + // object to act as a predicate-formatter suitable for using with + // Google Test's EXPECT_PRED_FORMAT1() macro. + template <typename T> + AssertionResult operator()(const char* value_text, const T& x) const { + // We convert matcher_ to a Matcher<const T&> *now* instead of + // when the PredicateFormatterFromMatcher object was constructed, + // as matcher_ may be polymorphic (e.g. NotNull()) and we won't + // know which type to instantiate it to until we actually see the + // type of x here. + // + // We write SafeMatcherCast<const T&>(matcher_) instead of + // Matcher<const T&>(matcher_), as the latter won't compile when + // matcher_ has type Matcher<T> (e.g. An<int>()). + // We don't write MatcherCast<const T&> either, as that allows + // potentially unsafe downcasting of the matcher argument. + const Matcher<const T&> matcher = SafeMatcherCast<const T&>(matcher_); + + // The expected path here is that the matcher should match (i.e. that most + // tests pass) so optimize for this case. + if (matcher.Matches(x)) { + return AssertionSuccess(); + } + + ::std::stringstream ss; + ss << "Value of: " << value_text << "\n" + << "Expected: "; + matcher.DescribeTo(&ss); + + // Rerun the matcher to "PrintAndExplain" the failure. + StringMatchResultListener listener; + if (MatchPrintAndExplain(x, matcher, &listener)) { + ss << "\n The matcher failed on the initial attempt; but passed when " + "rerun to generate the explanation."; + } + ss << "\n Actual: " << listener.str(); + return AssertionFailure() << ss.str(); + } + + private: + const M matcher_; +}; + +// A helper function for converting a matcher to a predicate-formatter +// without the user needing to explicitly write the type. This is +// used for implementing ASSERT_THAT() and EXPECT_THAT(). +// Implementation detail: 'matcher' is received by-value to force decaying. +template <typename M> +inline PredicateFormatterFromMatcher<M> +MakePredicateFormatterFromMatcher(M matcher) { + return PredicateFormatterFromMatcher<M>(std::move(matcher)); +} + +// Implements the polymorphic IsNan() matcher, which matches any floating type +// value that is Nan. +class IsNanMatcher { + public: + template <typename FloatType> + bool MatchAndExplain(const FloatType& f, + MatchResultListener* /* listener */) const { + return (::std::isnan)(f); + } + + void DescribeTo(::std::ostream* os) const { *os << "is NaN"; } + void DescribeNegationTo(::std::ostream* os) const { + *os << "isn't NaN"; + } +}; + +// Implements the polymorphic floating point equality matcher, which matches +// two float values using ULP-based approximation or, optionally, a +// user-specified epsilon. The template is meant to be instantiated with +// FloatType being either float or double. +template <typename FloatType> +class FloatingEqMatcher { + public: + // Constructor for FloatingEqMatcher. + // The matcher's input will be compared with expected. The matcher treats two + // NANs as equal if nan_eq_nan is true. Otherwise, under IEEE standards, + // equality comparisons between NANs will always return false. We specify a + // negative max_abs_error_ term to indicate that ULP-based approximation will + // be used for comparison. + FloatingEqMatcher(FloatType expected, bool nan_eq_nan) : + expected_(expected), nan_eq_nan_(nan_eq_nan), max_abs_error_(-1) { + } + + // Constructor that supports a user-specified max_abs_error that will be used + // for comparison instead of ULP-based approximation. The max absolute + // should be non-negative. + FloatingEqMatcher(FloatType expected, bool nan_eq_nan, + FloatType max_abs_error) + : expected_(expected), + nan_eq_nan_(nan_eq_nan), + max_abs_error_(max_abs_error) { + GTEST_CHECK_(max_abs_error >= 0) + << ", where max_abs_error is" << max_abs_error; + } + + // Implements floating point equality matcher as a Matcher<T>. + template <typename T> + class Impl : public MatcherInterface<T> { + public: + Impl(FloatType expected, bool nan_eq_nan, FloatType max_abs_error) + : expected_(expected), + nan_eq_nan_(nan_eq_nan), + max_abs_error_(max_abs_error) {} + + bool MatchAndExplain(T value, + MatchResultListener* listener) const override { + const FloatingPoint<FloatType> actual(value), expected(expected_); + + // Compares NaNs first, if nan_eq_nan_ is true. + if (actual.is_nan() || expected.is_nan()) { + if (actual.is_nan() && expected.is_nan()) { + return nan_eq_nan_; + } + // One is nan; the other is not nan. + return false; + } + if (HasMaxAbsError()) { + // We perform an equality check so that inf will match inf, regardless + // of error bounds. If the result of value - expected_ would result in + // overflow or if either value is inf, the default result is infinity, + // which should only match if max_abs_error_ is also infinity. + if (value == expected_) { + return true; + } + + const FloatType diff = value - expected_; + if (::std::fabs(diff) <= max_abs_error_) { + return true; + } + + if (listener->IsInterested()) { + *listener << "which is " << diff << " from " << expected_; + } + return false; + } else { + return actual.AlmostEquals(expected); + } + } + + void DescribeTo(::std::ostream* os) const override { + // os->precision() returns the previously set precision, which we + // store to restore the ostream to its original configuration + // after outputting. + const ::std::streamsize old_precision = os->precision( + ::std::numeric_limits<FloatType>::digits10 + 2); + if (FloatingPoint<FloatType>(expected_).is_nan()) { + if (nan_eq_nan_) { + *os << "is NaN"; + } else { + *os << "never matches"; + } + } else { + *os << "is approximately " << expected_; + if (HasMaxAbsError()) { + *os << " (absolute error <= " << max_abs_error_ << ")"; + } + } + os->precision(old_precision); + } + + void DescribeNegationTo(::std::ostream* os) const override { + // As before, get original precision. + const ::std::streamsize old_precision = os->precision( + ::std::numeric_limits<FloatType>::digits10 + 2); + if (FloatingPoint<FloatType>(expected_).is_nan()) { + if (nan_eq_nan_) { + *os << "isn't NaN"; + } else { + *os << "is anything"; + } + } else { + *os << "isn't approximately " << expected_; + if (HasMaxAbsError()) { + *os << " (absolute error > " << max_abs_error_ << ")"; + } + } + // Restore original precision. + os->precision(old_precision); + } + + private: + bool HasMaxAbsError() const { + return max_abs_error_ >= 0; + } + + const FloatType expected_; + const bool nan_eq_nan_; + // max_abs_error will be used for value comparison when >= 0. + const FloatType max_abs_error_; + }; + + // The following 3 type conversion operators allow FloatEq(expected) and + // NanSensitiveFloatEq(expected) to be used as a Matcher<float>, a + // Matcher<const float&>, or a Matcher<float&>, but nothing else. + operator Matcher<FloatType>() const { + return MakeMatcher( + new Impl<FloatType>(expected_, nan_eq_nan_, max_abs_error_)); + } + + operator Matcher<const FloatType&>() const { + return MakeMatcher( + new Impl<const FloatType&>(expected_, nan_eq_nan_, max_abs_error_)); + } + + operator Matcher<FloatType&>() const { + return MakeMatcher( + new Impl<FloatType&>(expected_, nan_eq_nan_, max_abs_error_)); + } + + private: + const FloatType expected_; + const bool nan_eq_nan_; + // max_abs_error will be used for value comparison when >= 0. + const FloatType max_abs_error_; +}; + +// A 2-tuple ("binary") wrapper around FloatingEqMatcher: +// FloatingEq2Matcher() matches (x, y) by matching FloatingEqMatcher(x, false) +// against y, and FloatingEq2Matcher(e) matches FloatingEqMatcher(x, false, e) +// against y. The former implements "Eq", the latter "Near". At present, there +// is no version that compares NaNs as equal. +template <typename FloatType> +class FloatingEq2Matcher { + public: + FloatingEq2Matcher() { Init(-1, false); } + + explicit FloatingEq2Matcher(bool nan_eq_nan) { Init(-1, nan_eq_nan); } + + explicit FloatingEq2Matcher(FloatType max_abs_error) { + Init(max_abs_error, false); + } + + FloatingEq2Matcher(FloatType max_abs_error, bool nan_eq_nan) { + Init(max_abs_error, nan_eq_nan); + } + + template <typename T1, typename T2> + operator Matcher<::std::tuple<T1, T2>>() const { + return MakeMatcher( + new Impl<::std::tuple<T1, T2>>(max_abs_error_, nan_eq_nan_)); + } + template <typename T1, typename T2> + operator Matcher<const ::std::tuple<T1, T2>&>() const { + return MakeMatcher( + new Impl<const ::std::tuple<T1, T2>&>(max_abs_error_, nan_eq_nan_)); + } + + private: + static ::std::ostream& GetDesc(::std::ostream& os) { // NOLINT + return os << "an almost-equal pair"; + } + + template <typename Tuple> + class Impl : public MatcherInterface<Tuple> { + public: + Impl(FloatType max_abs_error, bool nan_eq_nan) : + max_abs_error_(max_abs_error), + nan_eq_nan_(nan_eq_nan) {} + + bool MatchAndExplain(Tuple args, + MatchResultListener* listener) const override { + if (max_abs_error_ == -1) { + FloatingEqMatcher<FloatType> fm(::std::get<0>(args), nan_eq_nan_); + return static_cast<Matcher<FloatType>>(fm).MatchAndExplain( + ::std::get<1>(args), listener); + } else { + FloatingEqMatcher<FloatType> fm(::std::get<0>(args), nan_eq_nan_, + max_abs_error_); + return static_cast<Matcher<FloatType>>(fm).MatchAndExplain( + ::std::get<1>(args), listener); + } + } + void DescribeTo(::std::ostream* os) const override { + *os << "are " << GetDesc; + } + void DescribeNegationTo(::std::ostream* os) const override { + *os << "aren't " << GetDesc; + } + + private: + FloatType max_abs_error_; + const bool nan_eq_nan_; + }; + + void Init(FloatType max_abs_error_val, bool nan_eq_nan_val) { + max_abs_error_ = max_abs_error_val; + nan_eq_nan_ = nan_eq_nan_val; + } + FloatType max_abs_error_; + bool nan_eq_nan_; +}; + +// Implements the Pointee(m) matcher for matching a pointer whose +// pointee matches matcher m. The pointer can be either raw or smart. +template <typename InnerMatcher> +class PointeeMatcher { + public: + explicit PointeeMatcher(const InnerMatcher& matcher) : matcher_(matcher) {} + + // This type conversion operator template allows Pointee(m) to be + // used as a matcher for any pointer type whose pointee type is + // compatible with the inner matcher, where type Pointer can be + // either a raw pointer or a smart pointer. + // + // The reason we do this instead of relying on + // MakePolymorphicMatcher() is that the latter is not flexible + // enough for implementing the DescribeTo() method of Pointee(). + template <typename Pointer> + operator Matcher<Pointer>() const { + return Matcher<Pointer>(new Impl<const Pointer&>(matcher_)); + } + + private: + // The monomorphic implementation that works for a particular pointer type. + template <typename Pointer> + class Impl : public MatcherInterface<Pointer> { + public: + using Pointee = + typename std::pointer_traits<GTEST_REMOVE_REFERENCE_AND_CONST_( + Pointer)>::element_type; + + explicit Impl(const InnerMatcher& matcher) + : matcher_(MatcherCast<const Pointee&>(matcher)) {} + + void DescribeTo(::std::ostream* os) const override { + *os << "points to a value that "; + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "does not point to a value that "; + matcher_.DescribeTo(os); + } + + bool MatchAndExplain(Pointer pointer, + MatchResultListener* listener) const override { + if (GetRawPointer(pointer) == nullptr) return false; + + *listener << "which points to "; + return MatchPrintAndExplain(*pointer, matcher_, listener); + } + + private: + const Matcher<const Pointee&> matcher_; + }; + + const InnerMatcher matcher_; +}; + +// Implements the Pointer(m) matcher +// Implements the Pointer(m) matcher for matching a pointer that matches matcher +// m. The pointer can be either raw or smart, and will match `m` against the +// raw pointer. +template <typename InnerMatcher> +class PointerMatcher { + public: + explicit PointerMatcher(const InnerMatcher& matcher) : matcher_(matcher) {} + + // This type conversion operator template allows Pointer(m) to be + // used as a matcher for any pointer type whose pointer type is + // compatible with the inner matcher, where type PointerType can be + // either a raw pointer or a smart pointer. + // + // The reason we do this instead of relying on + // MakePolymorphicMatcher() is that the latter is not flexible + // enough for implementing the DescribeTo() method of Pointer(). + template <typename PointerType> + operator Matcher<PointerType>() const { // NOLINT + return Matcher<PointerType>(new Impl<const PointerType&>(matcher_)); + } + + private: + // The monomorphic implementation that works for a particular pointer type. + template <typename PointerType> + class Impl : public MatcherInterface<PointerType> { + public: + using Pointer = + const typename std::pointer_traits<GTEST_REMOVE_REFERENCE_AND_CONST_( + PointerType)>::element_type*; + + explicit Impl(const InnerMatcher& matcher) + : matcher_(MatcherCast<Pointer>(matcher)) {} + + void DescribeTo(::std::ostream* os) const override { + *os << "is a pointer that "; + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "is not a pointer that "; + matcher_.DescribeTo(os); + } + + bool MatchAndExplain(PointerType pointer, + MatchResultListener* listener) const override { + *listener << "which is a pointer that "; + Pointer p = GetRawPointer(pointer); + return MatchPrintAndExplain(p, matcher_, listener); + } + + private: + Matcher<Pointer> matcher_; + }; + + const InnerMatcher matcher_; +}; + +#if GTEST_HAS_RTTI +// Implements the WhenDynamicCastTo<T>(m) matcher that matches a pointer or +// reference that matches inner_matcher when dynamic_cast<T> is applied. +// The result of dynamic_cast<To> is forwarded to the inner matcher. +// If To is a pointer and the cast fails, the inner matcher will receive NULL. +// If To is a reference and the cast fails, this matcher returns false +// immediately. +template <typename To> +class WhenDynamicCastToMatcherBase { + public: + explicit WhenDynamicCastToMatcherBase(const Matcher<To>& matcher) + : matcher_(matcher) {} + + void DescribeTo(::std::ostream* os) const { + GetCastTypeDescription(os); + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const { + GetCastTypeDescription(os); + matcher_.DescribeNegationTo(os); + } + + protected: + const Matcher<To> matcher_; + + static std::string GetToName() { + return GetTypeName<To>(); + } + + private: + static void GetCastTypeDescription(::std::ostream* os) { + *os << "when dynamic_cast to " << GetToName() << ", "; + } +}; + +// Primary template. +// To is a pointer. Cast and forward the result. +template <typename To> +class WhenDynamicCastToMatcher : public WhenDynamicCastToMatcherBase<To> { + public: + explicit WhenDynamicCastToMatcher(const Matcher<To>& matcher) + : WhenDynamicCastToMatcherBase<To>(matcher) {} + + template <typename From> + bool MatchAndExplain(From from, MatchResultListener* listener) const { + To to = dynamic_cast<To>(from); + return MatchPrintAndExplain(to, this->matcher_, listener); + } +}; + +// Specialize for references. +// In this case we return false if the dynamic_cast fails. +template <typename To> +class WhenDynamicCastToMatcher<To&> : public WhenDynamicCastToMatcherBase<To&> { + public: + explicit WhenDynamicCastToMatcher(const Matcher<To&>& matcher) + : WhenDynamicCastToMatcherBase<To&>(matcher) {} + + template <typename From> + bool MatchAndExplain(From& from, MatchResultListener* listener) const { + // We don't want an std::bad_cast here, so do the cast with pointers. + To* to = dynamic_cast<To*>(&from); + if (to == nullptr) { + *listener << "which cannot be dynamic_cast to " << this->GetToName(); + return false; + } + return MatchPrintAndExplain(*to, this->matcher_, listener); + } +}; +#endif // GTEST_HAS_RTTI + +// Implements the Field() matcher for matching a field (i.e. member +// variable) of an object. +template <typename Class, typename FieldType> +class FieldMatcher { + public: + FieldMatcher(FieldType Class::*field, + const Matcher<const FieldType&>& matcher) + : field_(field), matcher_(matcher), whose_field_("whose given field ") {} + + FieldMatcher(const std::string& field_name, FieldType Class::*field, + const Matcher<const FieldType&>& matcher) + : field_(field), + matcher_(matcher), + whose_field_("whose field `" + field_name + "` ") {} + + void DescribeTo(::std::ostream* os) const { + *os << "is an object " << whose_field_; + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "is an object " << whose_field_; + matcher_.DescribeNegationTo(os); + } + + template <typename T> + bool MatchAndExplain(const T& value, MatchResultListener* listener) const { + // FIXME: The dispatch on std::is_pointer was introduced as a workaround for + // a compiler bug, and can now be removed. + return MatchAndExplainImpl( + typename std::is_pointer<typename std::remove_const<T>::type>::type(), + value, listener); + } + + private: + bool MatchAndExplainImpl(std::false_type /* is_not_pointer */, + const Class& obj, + MatchResultListener* listener) const { + *listener << whose_field_ << "is "; + return MatchPrintAndExplain(obj.*field_, matcher_, listener); + } + + bool MatchAndExplainImpl(std::true_type /* is_pointer */, const Class* p, + MatchResultListener* listener) const { + if (p == nullptr) return false; + + *listener << "which points to an object "; + // Since *p has a field, it must be a class/struct/union type and + // thus cannot be a pointer. Therefore we pass false_type() as + // the first argument. + return MatchAndExplainImpl(std::false_type(), *p, listener); + } + + const FieldType Class::*field_; + const Matcher<const FieldType&> matcher_; + + // Contains either "whose given field " if the name of the field is unknown + // or "whose field `name_of_field` " if the name is known. + const std::string whose_field_; +}; + +// Implements the Property() matcher for matching a property +// (i.e. return value of a getter method) of an object. +// +// Property is a const-qualified member function of Class returning +// PropertyType. +template <typename Class, typename PropertyType, typename Property> +class PropertyMatcher { + public: + typedef const PropertyType& RefToConstProperty; + + PropertyMatcher(Property property, const Matcher<RefToConstProperty>& matcher) + : property_(property), + matcher_(matcher), + whose_property_("whose given property ") {} + + PropertyMatcher(const std::string& property_name, Property property, + const Matcher<RefToConstProperty>& matcher) + : property_(property), + matcher_(matcher), + whose_property_("whose property `" + property_name + "` ") {} + + void DescribeTo(::std::ostream* os) const { + *os << "is an object " << whose_property_; + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "is an object " << whose_property_; + matcher_.DescribeNegationTo(os); + } + + template <typename T> + bool MatchAndExplain(const T&value, MatchResultListener* listener) const { + return MatchAndExplainImpl( + typename std::is_pointer<typename std::remove_const<T>::type>::type(), + value, listener); + } + + private: + bool MatchAndExplainImpl(std::false_type /* is_not_pointer */, + const Class& obj, + MatchResultListener* listener) const { + *listener << whose_property_ << "is "; + // Cannot pass the return value (for example, int) to MatchPrintAndExplain, + // which takes a non-const reference as argument. + RefToConstProperty result = (obj.*property_)(); + return MatchPrintAndExplain(result, matcher_, listener); + } + + bool MatchAndExplainImpl(std::true_type /* is_pointer */, const Class* p, + MatchResultListener* listener) const { + if (p == nullptr) return false; + + *listener << "which points to an object "; + // Since *p has a property method, it must be a class/struct/union + // type and thus cannot be a pointer. Therefore we pass + // false_type() as the first argument. + return MatchAndExplainImpl(std::false_type(), *p, listener); + } + + Property property_; + const Matcher<RefToConstProperty> matcher_; + + // Contains either "whose given property " if the name of the property is + // unknown or "whose property `name_of_property` " if the name is known. + const std::string whose_property_; +}; + +// Type traits specifying various features of different functors for ResultOf. +// The default template specifies features for functor objects. +template <typename Functor> +struct CallableTraits { + typedef Functor StorageType; + + static void CheckIsValid(Functor /* functor */) {} + + template <typename T> + static auto Invoke(Functor f, const T& arg) -> decltype(f(arg)) { + return f(arg); + } +}; + +// Specialization for function pointers. +template <typename ArgType, typename ResType> +struct CallableTraits<ResType(*)(ArgType)> { + typedef ResType ResultType; + typedef ResType(*StorageType)(ArgType); + + static void CheckIsValid(ResType(*f)(ArgType)) { + GTEST_CHECK_(f != nullptr) + << "NULL function pointer is passed into ResultOf()."; + } + template <typename T> + static ResType Invoke(ResType(*f)(ArgType), T arg) { + return (*f)(arg); + } +}; + +// Implements the ResultOf() matcher for matching a return value of a +// unary function of an object. +template <typename Callable, typename InnerMatcher> +class ResultOfMatcher { + public: + ResultOfMatcher(Callable callable, InnerMatcher matcher) + : callable_(std::move(callable)), matcher_(std::move(matcher)) { + CallableTraits<Callable>::CheckIsValid(callable_); + } + + template <typename T> + operator Matcher<T>() const { + return Matcher<T>(new Impl<const T&>(callable_, matcher_)); + } + + private: + typedef typename CallableTraits<Callable>::StorageType CallableStorageType; + + template <typename T> + class Impl : public MatcherInterface<T> { + using ResultType = decltype(CallableTraits<Callable>::template Invoke<T>( + std::declval<CallableStorageType>(), std::declval<T>())); + + public: + template <typename M> + Impl(const CallableStorageType& callable, const M& matcher) + : callable_(callable), matcher_(MatcherCast<ResultType>(matcher)) {} + + void DescribeTo(::std::ostream* os) const override { + *os << "is mapped by the given callable to a value that "; + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "is mapped by the given callable to a value that "; + matcher_.DescribeNegationTo(os); + } + + bool MatchAndExplain(T obj, MatchResultListener* listener) const override { + *listener << "which is mapped by the given callable to "; + // Cannot pass the return value directly to MatchPrintAndExplain, which + // takes a non-const reference as argument. + // Also, specifying template argument explicitly is needed because T could + // be a non-const reference (e.g. Matcher<Uncopyable&>). + ResultType result = + CallableTraits<Callable>::template Invoke<T>(callable_, obj); + return MatchPrintAndExplain(result, matcher_, listener); + } + + private: + // Functors often define operator() as non-const method even though + // they are actually stateless. But we need to use them even when + // 'this' is a const pointer. It's the user's responsibility not to + // use stateful callables with ResultOf(), which doesn't guarantee + // how many times the callable will be invoked. + mutable CallableStorageType callable_; + const Matcher<ResultType> matcher_; + }; // class Impl + + const CallableStorageType callable_; + const InnerMatcher matcher_; +}; + +// Implements a matcher that checks the size of an STL-style container. +template <typename SizeMatcher> +class SizeIsMatcher { + public: + explicit SizeIsMatcher(const SizeMatcher& size_matcher) + : size_matcher_(size_matcher) { + } + + template <typename Container> + operator Matcher<Container>() const { + return Matcher<Container>(new Impl<const Container&>(size_matcher_)); + } + + template <typename Container> + class Impl : public MatcherInterface<Container> { + public: + using SizeType = decltype(std::declval<Container>().size()); + explicit Impl(const SizeMatcher& size_matcher) + : size_matcher_(MatcherCast<SizeType>(size_matcher)) {} + + void DescribeTo(::std::ostream* os) const override { + *os << "size "; + size_matcher_.DescribeTo(os); + } + void DescribeNegationTo(::std::ostream* os) const override { + *os << "size "; + size_matcher_.DescribeNegationTo(os); + } + + bool MatchAndExplain(Container container, + MatchResultListener* listener) const override { + SizeType size = container.size(); + StringMatchResultListener size_listener; + const bool result = size_matcher_.MatchAndExplain(size, &size_listener); + *listener + << "whose size " << size << (result ? " matches" : " doesn't match"); + PrintIfNotEmpty(size_listener.str(), listener->stream()); + return result; + } + + private: + const Matcher<SizeType> size_matcher_; + }; + + private: + const SizeMatcher size_matcher_; +}; + +// Implements a matcher that checks the begin()..end() distance of an STL-style +// container. +template <typename DistanceMatcher> +class BeginEndDistanceIsMatcher { + public: + explicit BeginEndDistanceIsMatcher(const DistanceMatcher& distance_matcher) + : distance_matcher_(distance_matcher) {} + + template <typename Container> + operator Matcher<Container>() const { + return Matcher<Container>(new Impl<const Container&>(distance_matcher_)); + } + + template <typename Container> + class Impl : public MatcherInterface<Container> { + public: + typedef internal::StlContainerView< + GTEST_REMOVE_REFERENCE_AND_CONST_(Container)> ContainerView; + typedef typename std::iterator_traits< + typename ContainerView::type::const_iterator>::difference_type + DistanceType; + explicit Impl(const DistanceMatcher& distance_matcher) + : distance_matcher_(MatcherCast<DistanceType>(distance_matcher)) {} + + void DescribeTo(::std::ostream* os) const override { + *os << "distance between begin() and end() "; + distance_matcher_.DescribeTo(os); + } + void DescribeNegationTo(::std::ostream* os) const override { + *os << "distance between begin() and end() "; + distance_matcher_.DescribeNegationTo(os); + } + + bool MatchAndExplain(Container container, + MatchResultListener* listener) const override { + using std::begin; + using std::end; + DistanceType distance = std::distance(begin(container), end(container)); + StringMatchResultListener distance_listener; + const bool result = + distance_matcher_.MatchAndExplain(distance, &distance_listener); + *listener << "whose distance between begin() and end() " << distance + << (result ? " matches" : " doesn't match"); + PrintIfNotEmpty(distance_listener.str(), listener->stream()); + return result; + } + + private: + const Matcher<DistanceType> distance_matcher_; + }; + + private: + const DistanceMatcher distance_matcher_; +}; + +// Implements an equality matcher for any STL-style container whose elements +// support ==. This matcher is like Eq(), but its failure explanations provide +// more detailed information that is useful when the container is used as a set. +// The failure message reports elements that are in one of the operands but not +// the other. The failure messages do not report duplicate or out-of-order +// elements in the containers (which don't properly matter to sets, but can +// occur if the containers are vectors or lists, for example). +// +// Uses the container's const_iterator, value_type, operator ==, +// begin(), and end(). +template <typename Container> +class ContainerEqMatcher { + public: + typedef internal::StlContainerView<Container> View; + typedef typename View::type StlContainer; + typedef typename View::const_reference StlContainerReference; + + static_assert(!std::is_const<Container>::value, + "Container type must not be const"); + static_assert(!std::is_reference<Container>::value, + "Container type must not be a reference"); + + // We make a copy of expected in case the elements in it are modified + // after this matcher is created. + explicit ContainerEqMatcher(const Container& expected) + : expected_(View::Copy(expected)) {} + + void DescribeTo(::std::ostream* os) const { + *os << "equals "; + UniversalPrint(expected_, os); + } + void DescribeNegationTo(::std::ostream* os) const { + *os << "does not equal "; + UniversalPrint(expected_, os); + } + + template <typename LhsContainer> + bool MatchAndExplain(const LhsContainer& lhs, + MatchResultListener* listener) const { + typedef internal::StlContainerView< + typename std::remove_const<LhsContainer>::type> + LhsView; + typedef typename LhsView::type LhsStlContainer; + StlContainerReference lhs_stl_container = LhsView::ConstReference(lhs); + if (lhs_stl_container == expected_) + return true; + + ::std::ostream* const os = listener->stream(); + if (os != nullptr) { + // Something is different. Check for extra values first. + bool printed_header = false; + for (typename LhsStlContainer::const_iterator it = + lhs_stl_container.begin(); + it != lhs_stl_container.end(); ++it) { + if (internal::ArrayAwareFind(expected_.begin(), expected_.end(), *it) == + expected_.end()) { + if (printed_header) { + *os << ", "; + } else { + *os << "which has these unexpected elements: "; + printed_header = true; + } + UniversalPrint(*it, os); + } + } + + // Now check for missing values. + bool printed_header2 = false; + for (typename StlContainer::const_iterator it = expected_.begin(); + it != expected_.end(); ++it) { + if (internal::ArrayAwareFind( + lhs_stl_container.begin(), lhs_stl_container.end(), *it) == + lhs_stl_container.end()) { + if (printed_header2) { + *os << ", "; + } else { + *os << (printed_header ? ",\nand" : "which") + << " doesn't have these expected elements: "; + printed_header2 = true; + } + UniversalPrint(*it, os); + } + } + } + + return false; + } + + private: + const StlContainer expected_; +}; + +// A comparator functor that uses the < operator to compare two values. +struct LessComparator { + template <typename T, typename U> + bool operator()(const T& lhs, const U& rhs) const { return lhs < rhs; } +}; + +// Implements WhenSortedBy(comparator, container_matcher). +template <typename Comparator, typename ContainerMatcher> +class WhenSortedByMatcher { + public: + WhenSortedByMatcher(const Comparator& comparator, + const ContainerMatcher& matcher) + : comparator_(comparator), matcher_(matcher) {} + + template <typename LhsContainer> + operator Matcher<LhsContainer>() const { + return MakeMatcher(new Impl<LhsContainer>(comparator_, matcher_)); + } + + template <typename LhsContainer> + class Impl : public MatcherInterface<LhsContainer> { + public: + typedef internal::StlContainerView< + GTEST_REMOVE_REFERENCE_AND_CONST_(LhsContainer)> LhsView; + typedef typename LhsView::type LhsStlContainer; + typedef typename LhsView::const_reference LhsStlContainerReference; + // Transforms std::pair<const Key, Value> into std::pair<Key, Value> + // so that we can match associative containers. + typedef typename RemoveConstFromKey< + typename LhsStlContainer::value_type>::type LhsValue; + + Impl(const Comparator& comparator, const ContainerMatcher& matcher) + : comparator_(comparator), matcher_(matcher) {} + + void DescribeTo(::std::ostream* os) const override { + *os << "(when sorted) "; + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "(when sorted) "; + matcher_.DescribeNegationTo(os); + } + + bool MatchAndExplain(LhsContainer lhs, + MatchResultListener* listener) const override { + LhsStlContainerReference lhs_stl_container = LhsView::ConstReference(lhs); + ::std::vector<LhsValue> sorted_container(lhs_stl_container.begin(), + lhs_stl_container.end()); + ::std::sort( + sorted_container.begin(), sorted_container.end(), comparator_); + + if (!listener->IsInterested()) { + // If the listener is not interested, we do not need to + // construct the inner explanation. + return matcher_.Matches(sorted_container); + } + + *listener << "which is "; + UniversalPrint(sorted_container, listener->stream()); + *listener << " when sorted"; + + StringMatchResultListener inner_listener; + const bool match = matcher_.MatchAndExplain(sorted_container, + &inner_listener); + PrintIfNotEmpty(inner_listener.str(), listener->stream()); + return match; + } + + private: + const Comparator comparator_; + const Matcher<const ::std::vector<LhsValue>&> matcher_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Impl); + }; + + private: + const Comparator comparator_; + const ContainerMatcher matcher_; +}; + +// Implements Pointwise(tuple_matcher, rhs_container). tuple_matcher +// must be able to be safely cast to Matcher<std::tuple<const T1&, const +// T2&> >, where T1 and T2 are the types of elements in the LHS +// container and the RHS container respectively. +template <typename TupleMatcher, typename RhsContainer> +class PointwiseMatcher { + GTEST_COMPILE_ASSERT_( + !IsHashTable<GTEST_REMOVE_REFERENCE_AND_CONST_(RhsContainer)>::value, + use_UnorderedPointwise_with_hash_tables); + + public: + typedef internal::StlContainerView<RhsContainer> RhsView; + typedef typename RhsView::type RhsStlContainer; + typedef typename RhsStlContainer::value_type RhsValue; + + static_assert(!std::is_const<RhsContainer>::value, + "RhsContainer type must not be const"); + static_assert(!std::is_reference<RhsContainer>::value, + "RhsContainer type must not be a reference"); + + // Like ContainerEq, we make a copy of rhs in case the elements in + // it are modified after this matcher is created. + PointwiseMatcher(const TupleMatcher& tuple_matcher, const RhsContainer& rhs) + : tuple_matcher_(tuple_matcher), rhs_(RhsView::Copy(rhs)) {} + + template <typename LhsContainer> + operator Matcher<LhsContainer>() const { + GTEST_COMPILE_ASSERT_( + !IsHashTable<GTEST_REMOVE_REFERENCE_AND_CONST_(LhsContainer)>::value, + use_UnorderedPointwise_with_hash_tables); + + return Matcher<LhsContainer>( + new Impl<const LhsContainer&>(tuple_matcher_, rhs_)); + } + + template <typename LhsContainer> + class Impl : public MatcherInterface<LhsContainer> { + public: + typedef internal::StlContainerView< + GTEST_REMOVE_REFERENCE_AND_CONST_(LhsContainer)> LhsView; + typedef typename LhsView::type LhsStlContainer; + typedef typename LhsView::const_reference LhsStlContainerReference; + typedef typename LhsStlContainer::value_type LhsValue; + // We pass the LHS value and the RHS value to the inner matcher by + // reference, as they may be expensive to copy. We must use tuple + // instead of pair here, as a pair cannot hold references (C++ 98, + // 20.2.2 [lib.pairs]). + typedef ::std::tuple<const LhsValue&, const RhsValue&> InnerMatcherArg; + + Impl(const TupleMatcher& tuple_matcher, const RhsStlContainer& rhs) + // mono_tuple_matcher_ holds a monomorphic version of the tuple matcher. + : mono_tuple_matcher_(SafeMatcherCast<InnerMatcherArg>(tuple_matcher)), + rhs_(rhs) {} + + void DescribeTo(::std::ostream* os) const override { + *os << "contains " << rhs_.size() + << " values, where each value and its corresponding value in "; + UniversalPrinter<RhsStlContainer>::Print(rhs_, os); + *os << " "; + mono_tuple_matcher_.DescribeTo(os); + } + void DescribeNegationTo(::std::ostream* os) const override { + *os << "doesn't contain exactly " << rhs_.size() + << " values, or contains a value x at some index i" + << " where x and the i-th value of "; + UniversalPrint(rhs_, os); + *os << " "; + mono_tuple_matcher_.DescribeNegationTo(os); + } + + bool MatchAndExplain(LhsContainer lhs, + MatchResultListener* listener) const override { + LhsStlContainerReference lhs_stl_container = LhsView::ConstReference(lhs); + const size_t actual_size = lhs_stl_container.size(); + if (actual_size != rhs_.size()) { + *listener << "which contains " << actual_size << " values"; + return false; + } + + typename LhsStlContainer::const_iterator left = lhs_stl_container.begin(); + typename RhsStlContainer::const_iterator right = rhs_.begin(); + for (size_t i = 0; i != actual_size; ++i, ++left, ++right) { + if (listener->IsInterested()) { + StringMatchResultListener inner_listener; + // Create InnerMatcherArg as a temporarily object to avoid it outlives + // *left and *right. Dereference or the conversion to `const T&` may + // return temp objects, e.g for vector<bool>. + if (!mono_tuple_matcher_.MatchAndExplain( + InnerMatcherArg(ImplicitCast_<const LhsValue&>(*left), + ImplicitCast_<const RhsValue&>(*right)), + &inner_listener)) { + *listener << "where the value pair ("; + UniversalPrint(*left, listener->stream()); + *listener << ", "; + UniversalPrint(*right, listener->stream()); + *listener << ") at index #" << i << " don't match"; + PrintIfNotEmpty(inner_listener.str(), listener->stream()); + return false; + } + } else { + if (!mono_tuple_matcher_.Matches( + InnerMatcherArg(ImplicitCast_<const LhsValue&>(*left), + ImplicitCast_<const RhsValue&>(*right)))) + return false; + } + } + + return true; + } + + private: + const Matcher<InnerMatcherArg> mono_tuple_matcher_; + const RhsStlContainer rhs_; + }; + + private: + const TupleMatcher tuple_matcher_; + const RhsStlContainer rhs_; +}; + +// Holds the logic common to ContainsMatcherImpl and EachMatcherImpl. +template <typename Container> +class QuantifierMatcherImpl : public MatcherInterface<Container> { + public: + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef StlContainerView<RawContainer> View; + typedef typename View::type StlContainer; + typedef typename View::const_reference StlContainerReference; + typedef typename StlContainer::value_type Element; + + template <typename InnerMatcher> + explicit QuantifierMatcherImpl(InnerMatcher inner_matcher) + : inner_matcher_( + testing::SafeMatcherCast<const Element&>(inner_matcher)) {} + + // Checks whether: + // * All elements in the container match, if all_elements_should_match. + // * Any element in the container matches, if !all_elements_should_match. + bool MatchAndExplainImpl(bool all_elements_should_match, + Container container, + MatchResultListener* listener) const { + StlContainerReference stl_container = View::ConstReference(container); + size_t i = 0; + for (typename StlContainer::const_iterator it = stl_container.begin(); + it != stl_container.end(); ++it, ++i) { + StringMatchResultListener inner_listener; + const bool matches = inner_matcher_.MatchAndExplain(*it, &inner_listener); + + if (matches != all_elements_should_match) { + *listener << "whose element #" << i + << (matches ? " matches" : " doesn't match"); + PrintIfNotEmpty(inner_listener.str(), listener->stream()); + return !all_elements_should_match; + } + } + return all_elements_should_match; + } + + protected: + const Matcher<const Element&> inner_matcher_; +}; + +// Implements Contains(element_matcher) for the given argument type Container. +// Symmetric to EachMatcherImpl. +template <typename Container> +class ContainsMatcherImpl : public QuantifierMatcherImpl<Container> { + public: + template <typename InnerMatcher> + explicit ContainsMatcherImpl(InnerMatcher inner_matcher) + : QuantifierMatcherImpl<Container>(inner_matcher) {} + + // Describes what this matcher does. + void DescribeTo(::std::ostream* os) const override { + *os << "contains at least one element that "; + this->inner_matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "doesn't contain any element that "; + this->inner_matcher_.DescribeTo(os); + } + + bool MatchAndExplain(Container container, + MatchResultListener* listener) const override { + return this->MatchAndExplainImpl(false, container, listener); + } +}; + +// Implements Each(element_matcher) for the given argument type Container. +// Symmetric to ContainsMatcherImpl. +template <typename Container> +class EachMatcherImpl : public QuantifierMatcherImpl<Container> { + public: + template <typename InnerMatcher> + explicit EachMatcherImpl(InnerMatcher inner_matcher) + : QuantifierMatcherImpl<Container>(inner_matcher) {} + + // Describes what this matcher does. + void DescribeTo(::std::ostream* os) const override { + *os << "only contains elements that "; + this->inner_matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "contains some element that "; + this->inner_matcher_.DescribeNegationTo(os); + } + + bool MatchAndExplain(Container container, + MatchResultListener* listener) const override { + return this->MatchAndExplainImpl(true, container, listener); + } +}; + +// Implements polymorphic Contains(element_matcher). +template <typename M> +class ContainsMatcher { + public: + explicit ContainsMatcher(M m) : inner_matcher_(m) {} + + template <typename Container> + operator Matcher<Container>() const { + return Matcher<Container>( + new ContainsMatcherImpl<const Container&>(inner_matcher_)); + } + + private: + const M inner_matcher_; +}; + +// Implements polymorphic Each(element_matcher). +template <typename M> +class EachMatcher { + public: + explicit EachMatcher(M m) : inner_matcher_(m) {} + + template <typename Container> + operator Matcher<Container>() const { + return Matcher<Container>( + new EachMatcherImpl<const Container&>(inner_matcher_)); + } + + private: + const M inner_matcher_; +}; + +struct Rank1 {}; +struct Rank0 : Rank1 {}; + +namespace pair_getters { +using std::get; +template <typename T> +auto First(T& x, Rank1) -> decltype(get<0>(x)) { // NOLINT + return get<0>(x); +} +template <typename T> +auto First(T& x, Rank0) -> decltype((x.first)) { // NOLINT + return x.first; +} + +template <typename T> +auto Second(T& x, Rank1) -> decltype(get<1>(x)) { // NOLINT + return get<1>(x); +} +template <typename T> +auto Second(T& x, Rank0) -> decltype((x.second)) { // NOLINT + return x.second; +} +} // namespace pair_getters + +// Implements Key(inner_matcher) for the given argument pair type. +// Key(inner_matcher) matches an std::pair whose 'first' field matches +// inner_matcher. For example, Contains(Key(Ge(5))) can be used to match an +// std::map that contains at least one element whose key is >= 5. +template <typename PairType> +class KeyMatcherImpl : public MatcherInterface<PairType> { + public: + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(PairType) RawPairType; + typedef typename RawPairType::first_type KeyType; + + template <typename InnerMatcher> + explicit KeyMatcherImpl(InnerMatcher inner_matcher) + : inner_matcher_( + testing::SafeMatcherCast<const KeyType&>(inner_matcher)) { + } + + // Returns true if and only if 'key_value.first' (the key) matches the inner + // matcher. + bool MatchAndExplain(PairType key_value, + MatchResultListener* listener) const override { + StringMatchResultListener inner_listener; + const bool match = inner_matcher_.MatchAndExplain( + pair_getters::First(key_value, Rank0()), &inner_listener); + const std::string explanation = inner_listener.str(); + if (explanation != "") { + *listener << "whose first field is a value " << explanation; + } + return match; + } + + // Describes what this matcher does. + void DescribeTo(::std::ostream* os) const override { + *os << "has a key that "; + inner_matcher_.DescribeTo(os); + } + + // Describes what the negation of this matcher does. + void DescribeNegationTo(::std::ostream* os) const override { + *os << "doesn't have a key that "; + inner_matcher_.DescribeTo(os); + } + + private: + const Matcher<const KeyType&> inner_matcher_; +}; + +// Implements polymorphic Key(matcher_for_key). +template <typename M> +class KeyMatcher { + public: + explicit KeyMatcher(M m) : matcher_for_key_(m) {} + + template <typename PairType> + operator Matcher<PairType>() const { + return Matcher<PairType>( + new KeyMatcherImpl<const PairType&>(matcher_for_key_)); + } + + private: + const M matcher_for_key_; +}; + +// Implements polymorphic Address(matcher_for_address). +template <typename InnerMatcher> +class AddressMatcher { + public: + explicit AddressMatcher(InnerMatcher m) : matcher_(m) {} + + template <typename Type> + operator Matcher<Type>() const { // NOLINT + return Matcher<Type>(new Impl<const Type&>(matcher_)); + } + + private: + // The monomorphic implementation that works for a particular object type. + template <typename Type> + class Impl : public MatcherInterface<Type> { + public: + using Address = const GTEST_REMOVE_REFERENCE_AND_CONST_(Type) *; + explicit Impl(const InnerMatcher& matcher) + : matcher_(MatcherCast<Address>(matcher)) {} + + void DescribeTo(::std::ostream* os) const override { + *os << "has address that "; + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "does not have address that "; + matcher_.DescribeTo(os); + } + + bool MatchAndExplain(Type object, + MatchResultListener* listener) const override { + *listener << "which has address "; + Address address = std::addressof(object); + return MatchPrintAndExplain(address, matcher_, listener); + } + + private: + const Matcher<Address> matcher_; + }; + const InnerMatcher matcher_; +}; + +// Implements Pair(first_matcher, second_matcher) for the given argument pair +// type with its two matchers. See Pair() function below. +template <typename PairType> +class PairMatcherImpl : public MatcherInterface<PairType> { + public: + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(PairType) RawPairType; + typedef typename RawPairType::first_type FirstType; + typedef typename RawPairType::second_type SecondType; + + template <typename FirstMatcher, typename SecondMatcher> + PairMatcherImpl(FirstMatcher first_matcher, SecondMatcher second_matcher) + : first_matcher_( + testing::SafeMatcherCast<const FirstType&>(first_matcher)), + second_matcher_( + testing::SafeMatcherCast<const SecondType&>(second_matcher)) { + } + + // Describes what this matcher does. + void DescribeTo(::std::ostream* os) const override { + *os << "has a first field that "; + first_matcher_.DescribeTo(os); + *os << ", and has a second field that "; + second_matcher_.DescribeTo(os); + } + + // Describes what the negation of this matcher does. + void DescribeNegationTo(::std::ostream* os) const override { + *os << "has a first field that "; + first_matcher_.DescribeNegationTo(os); + *os << ", or has a second field that "; + second_matcher_.DescribeNegationTo(os); + } + + // Returns true if and only if 'a_pair.first' matches first_matcher and + // 'a_pair.second' matches second_matcher. + bool MatchAndExplain(PairType a_pair, + MatchResultListener* listener) const override { + if (!listener->IsInterested()) { + // If the listener is not interested, we don't need to construct the + // explanation. + return first_matcher_.Matches(pair_getters::First(a_pair, Rank0())) && + second_matcher_.Matches(pair_getters::Second(a_pair, Rank0())); + } + StringMatchResultListener first_inner_listener; + if (!first_matcher_.MatchAndExplain(pair_getters::First(a_pair, Rank0()), + &first_inner_listener)) { + *listener << "whose first field does not match"; + PrintIfNotEmpty(first_inner_listener.str(), listener->stream()); + return false; + } + StringMatchResultListener second_inner_listener; + if (!second_matcher_.MatchAndExplain(pair_getters::Second(a_pair, Rank0()), + &second_inner_listener)) { + *listener << "whose second field does not match"; + PrintIfNotEmpty(second_inner_listener.str(), listener->stream()); + return false; + } + ExplainSuccess(first_inner_listener.str(), second_inner_listener.str(), + listener); + return true; + } + + private: + void ExplainSuccess(const std::string& first_explanation, + const std::string& second_explanation, + MatchResultListener* listener) const { + *listener << "whose both fields match"; + if (first_explanation != "") { + *listener << ", where the first field is a value " << first_explanation; + } + if (second_explanation != "") { + *listener << ", "; + if (first_explanation != "") { + *listener << "and "; + } else { + *listener << "where "; + } + *listener << "the second field is a value " << second_explanation; + } + } + + const Matcher<const FirstType&> first_matcher_; + const Matcher<const SecondType&> second_matcher_; +}; + +// Implements polymorphic Pair(first_matcher, second_matcher). +template <typename FirstMatcher, typename SecondMatcher> +class PairMatcher { + public: + PairMatcher(FirstMatcher first_matcher, SecondMatcher second_matcher) + : first_matcher_(first_matcher), second_matcher_(second_matcher) {} + + template <typename PairType> + operator Matcher<PairType> () const { + return Matcher<PairType>( + new PairMatcherImpl<const PairType&>(first_matcher_, second_matcher_)); + } + + private: + const FirstMatcher first_matcher_; + const SecondMatcher second_matcher_; +}; + +template <typename T, size_t... I> +auto UnpackStructImpl(const T& t, IndexSequence<I...>, int) + -> decltype(std::tie(get<I>(t)...)) { + static_assert(std::tuple_size<T>::value == sizeof...(I), + "Number of arguments doesn't match the number of fields."); + return std::tie(get<I>(t)...); +} + +#if defined(__cpp_structured_bindings) && __cpp_structured_bindings >= 201606 +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<1>, char) { + const auto& [a] = t; + return std::tie(a); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<2>, char) { + const auto& [a, b] = t; + return std::tie(a, b); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<3>, char) { + const auto& [a, b, c] = t; + return std::tie(a, b, c); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<4>, char) { + const auto& [a, b, c, d] = t; + return std::tie(a, b, c, d); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<5>, char) { + const auto& [a, b, c, d, e] = t; + return std::tie(a, b, c, d, e); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<6>, char) { + const auto& [a, b, c, d, e, f] = t; + return std::tie(a, b, c, d, e, f); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<7>, char) { + const auto& [a, b, c, d, e, f, g] = t; + return std::tie(a, b, c, d, e, f, g); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<8>, char) { + const auto& [a, b, c, d, e, f, g, h] = t; + return std::tie(a, b, c, d, e, f, g, h); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<9>, char) { + const auto& [a, b, c, d, e, f, g, h, i] = t; + return std::tie(a, b, c, d, e, f, g, h, i); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<10>, char) { + const auto& [a, b, c, d, e, f, g, h, i, j] = t; + return std::tie(a, b, c, d, e, f, g, h, i, j); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<11>, char) { + const auto& [a, b, c, d, e, f, g, h, i, j, k] = t; + return std::tie(a, b, c, d, e, f, g, h, i, j, k); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<12>, char) { + const auto& [a, b, c, d, e, f, g, h, i, j, k, l] = t; + return std::tie(a, b, c, d, e, f, g, h, i, j, k, l); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<13>, char) { + const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m] = t; + return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<14>, char) { + const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n] = t; + return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<15>, char) { + const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o] = t; + return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<16>, char) { + const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p] = t; + return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p); +} +#endif // defined(__cpp_structured_bindings) + +template <size_t I, typename T> +auto UnpackStruct(const T& t) + -> decltype((UnpackStructImpl)(t, MakeIndexSequence<I>{}, 0)) { + return (UnpackStructImpl)(t, MakeIndexSequence<I>{}, 0); +} + +// Helper function to do comma folding in C++11. +// The array ensures left-to-right order of evaluation. +// Usage: VariadicExpand({expr...}); +template <typename T, size_t N> +void VariadicExpand(const T (&)[N]) {} + +template <typename Struct, typename StructSize> +class FieldsAreMatcherImpl; + +template <typename Struct, size_t... I> +class FieldsAreMatcherImpl<Struct, IndexSequence<I...>> + : public MatcherInterface<Struct> { + using UnpackedType = + decltype(UnpackStruct<sizeof...(I)>(std::declval<const Struct&>())); + using MatchersType = std::tuple< + Matcher<const typename std::tuple_element<I, UnpackedType>::type&>...>; + + public: + template <typename Inner> + explicit FieldsAreMatcherImpl(const Inner& matchers) + : matchers_(testing::SafeMatcherCast< + const typename std::tuple_element<I, UnpackedType>::type&>( + std::get<I>(matchers))...) {} + + void DescribeTo(::std::ostream* os) const override { + const char* separator = ""; + VariadicExpand( + {(*os << separator << "has field #" << I << " that ", + std::get<I>(matchers_).DescribeTo(os), separator = ", and ")...}); + } + + void DescribeNegationTo(::std::ostream* os) const override { + const char* separator = ""; + VariadicExpand({(*os << separator << "has field #" << I << " that ", + std::get<I>(matchers_).DescribeNegationTo(os), + separator = ", or ")...}); + } + + bool MatchAndExplain(Struct t, MatchResultListener* listener) const override { + return MatchInternal((UnpackStruct<sizeof...(I)>)(t), listener); + } + + private: + bool MatchInternal(UnpackedType tuple, MatchResultListener* listener) const { + if (!listener->IsInterested()) { + // If the listener is not interested, we don't need to construct the + // explanation. + bool good = true; + VariadicExpand({good = good && std::get<I>(matchers_).Matches( + std::get<I>(tuple))...}); + return good; + } + + size_t failed_pos = ~size_t{}; + + std::vector<StringMatchResultListener> inner_listener(sizeof...(I)); + + VariadicExpand( + {failed_pos == ~size_t{} && !std::get<I>(matchers_).MatchAndExplain( + std::get<I>(tuple), &inner_listener[I]) + ? failed_pos = I + : 0 ...}); + if (failed_pos != ~size_t{}) { + *listener << "whose field #" << failed_pos << " does not match"; + PrintIfNotEmpty(inner_listener[failed_pos].str(), listener->stream()); + return false; + } + + *listener << "whose all elements match"; + const char* separator = ", where"; + for (size_t index = 0; index < sizeof...(I); ++index) { + const std::string str = inner_listener[index].str(); + if (!str.empty()) { + *listener << separator << " field #" << index << " is a value " << str; + separator = ", and"; + } + } + + return true; + } + + MatchersType matchers_; +}; + +template <typename... Inner> +class FieldsAreMatcher { + public: + explicit FieldsAreMatcher(Inner... inner) : matchers_(std::move(inner)...) {} + + template <typename Struct> + operator Matcher<Struct>() const { // NOLINT + return Matcher<Struct>( + new FieldsAreMatcherImpl<const Struct&, IndexSequenceFor<Inner...>>( + matchers_)); + } + + private: + std::tuple<Inner...> matchers_; +}; + +// Implements ElementsAre() and ElementsAreArray(). +template <typename Container> +class ElementsAreMatcherImpl : public MatcherInterface<Container> { + public: + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef internal::StlContainerView<RawContainer> View; + typedef typename View::type StlContainer; + typedef typename View::const_reference StlContainerReference; + typedef typename StlContainer::value_type Element; + + // Constructs the matcher from a sequence of element values or + // element matchers. + template <typename InputIter> + ElementsAreMatcherImpl(InputIter first, InputIter last) { + while (first != last) { + matchers_.push_back(MatcherCast<const Element&>(*first++)); + } + } + + // Describes what this matcher does. + void DescribeTo(::std::ostream* os) const override { + if (count() == 0) { + *os << "is empty"; + } else if (count() == 1) { + *os << "has 1 element that "; + matchers_[0].DescribeTo(os); + } else { + *os << "has " << Elements(count()) << " where\n"; + for (size_t i = 0; i != count(); ++i) { + *os << "element #" << i << " "; + matchers_[i].DescribeTo(os); + if (i + 1 < count()) { + *os << ",\n"; + } + } + } + } + + // Describes what the negation of this matcher does. + void DescribeNegationTo(::std::ostream* os) const override { + if (count() == 0) { + *os << "isn't empty"; + return; + } + + *os << "doesn't have " << Elements(count()) << ", or\n"; + for (size_t i = 0; i != count(); ++i) { + *os << "element #" << i << " "; + matchers_[i].DescribeNegationTo(os); + if (i + 1 < count()) { + *os << ", or\n"; + } + } + } + + bool MatchAndExplain(Container container, + MatchResultListener* listener) const override { + // To work with stream-like "containers", we must only walk + // through the elements in one pass. + + const bool listener_interested = listener->IsInterested(); + + // explanations[i] is the explanation of the element at index i. + ::std::vector<std::string> explanations(count()); + StlContainerReference stl_container = View::ConstReference(container); + typename StlContainer::const_iterator it = stl_container.begin(); + size_t exam_pos = 0; + bool mismatch_found = false; // Have we found a mismatched element yet? + + // Go through the elements and matchers in pairs, until we reach + // the end of either the elements or the matchers, or until we find a + // mismatch. + for (; it != stl_container.end() && exam_pos != count(); ++it, ++exam_pos) { + bool match; // Does the current element match the current matcher? + if (listener_interested) { + StringMatchResultListener s; + match = matchers_[exam_pos].MatchAndExplain(*it, &s); + explanations[exam_pos] = s.str(); + } else { + match = matchers_[exam_pos].Matches(*it); + } + + if (!match) { + mismatch_found = true; + break; + } + } + // If mismatch_found is true, 'exam_pos' is the index of the mismatch. + + // Find how many elements the actual container has. We avoid + // calling size() s.t. this code works for stream-like "containers" + // that don't define size(). + size_t actual_count = exam_pos; + for (; it != stl_container.end(); ++it) { + ++actual_count; + } + + if (actual_count != count()) { + // The element count doesn't match. If the container is empty, + // there's no need to explain anything as Google Mock already + // prints the empty container. Otherwise we just need to show + // how many elements there actually are. + if (listener_interested && (actual_count != 0)) { + *listener << "which has " << Elements(actual_count); + } + return false; + } + + if (mismatch_found) { + // The element count matches, but the exam_pos-th element doesn't match. + if (listener_interested) { + *listener << "whose element #" << exam_pos << " doesn't match"; + PrintIfNotEmpty(explanations[exam_pos], listener->stream()); + } + return false; + } + + // Every element matches its expectation. We need to explain why + // (the obvious ones can be skipped). + if (listener_interested) { + bool reason_printed = false; + for (size_t i = 0; i != count(); ++i) { + const std::string& s = explanations[i]; + if (!s.empty()) { + if (reason_printed) { + *listener << ",\nand "; + } + *listener << "whose element #" << i << " matches, " << s; + reason_printed = true; + } + } + } + return true; + } + + private: + static Message Elements(size_t count) { + return Message() << count << (count == 1 ? " element" : " elements"); + } + + size_t count() const { return matchers_.size(); } + + ::std::vector<Matcher<const Element&> > matchers_; +}; + +// Connectivity matrix of (elements X matchers), in element-major order. +// Initially, there are no edges. +// Use NextGraph() to iterate over all possible edge configurations. +// Use Randomize() to generate a random edge configuration. +class GTEST_API_ MatchMatrix { + public: + MatchMatrix(size_t num_elements, size_t num_matchers) + : num_elements_(num_elements), + num_matchers_(num_matchers), + matched_(num_elements_* num_matchers_, 0) { + } + + size_t LhsSize() const { return num_elements_; } + size_t RhsSize() const { return num_matchers_; } + bool HasEdge(size_t ilhs, size_t irhs) const { + return matched_[SpaceIndex(ilhs, irhs)] == 1; + } + void SetEdge(size_t ilhs, size_t irhs, bool b) { + matched_[SpaceIndex(ilhs, irhs)] = b ? 1 : 0; + } + + // Treating the connectivity matrix as a (LhsSize()*RhsSize())-bit number, + // adds 1 to that number; returns false if incrementing the graph left it + // empty. + bool NextGraph(); + + void Randomize(); + + std::string DebugString() const; + + private: + size_t SpaceIndex(size_t ilhs, size_t irhs) const { + return ilhs * num_matchers_ + irhs; + } + + size_t num_elements_; + size_t num_matchers_; + + // Each element is a char interpreted as bool. They are stored as a + // flattened array in lhs-major order, use 'SpaceIndex()' to translate + // a (ilhs, irhs) matrix coordinate into an offset. + ::std::vector<char> matched_; +}; + +typedef ::std::pair<size_t, size_t> ElementMatcherPair; +typedef ::std::vector<ElementMatcherPair> ElementMatcherPairs; + +// Returns a maximum bipartite matching for the specified graph 'g'. +// The matching is represented as a vector of {element, matcher} pairs. +GTEST_API_ ElementMatcherPairs +FindMaxBipartiteMatching(const MatchMatrix& g); + +struct UnorderedMatcherRequire { + enum Flags { + Superset = 1 << 0, + Subset = 1 << 1, + ExactMatch = Superset | Subset, + }; +}; + +// Untyped base class for implementing UnorderedElementsAre. By +// putting logic that's not specific to the element type here, we +// reduce binary bloat and increase compilation speed. +class GTEST_API_ UnorderedElementsAreMatcherImplBase { + protected: + explicit UnorderedElementsAreMatcherImplBase( + UnorderedMatcherRequire::Flags matcher_flags) + : match_flags_(matcher_flags) {} + + // A vector of matcher describers, one for each element matcher. + // Does not own the describers (and thus can be used only when the + // element matchers are alive). + typedef ::std::vector<const MatcherDescriberInterface*> MatcherDescriberVec; + + // Describes this UnorderedElementsAre matcher. + void DescribeToImpl(::std::ostream* os) const; + + // Describes the negation of this UnorderedElementsAre matcher. + void DescribeNegationToImpl(::std::ostream* os) const; + + bool VerifyMatchMatrix(const ::std::vector<std::string>& element_printouts, + const MatchMatrix& matrix, + MatchResultListener* listener) const; + + bool FindPairing(const MatchMatrix& matrix, + MatchResultListener* listener) const; + + MatcherDescriberVec& matcher_describers() { + return matcher_describers_; + } + + static Message Elements(size_t n) { + return Message() << n << " element" << (n == 1 ? "" : "s"); + } + + UnorderedMatcherRequire::Flags match_flags() const { return match_flags_; } + + private: + UnorderedMatcherRequire::Flags match_flags_; + MatcherDescriberVec matcher_describers_; +}; + +// Implements UnorderedElementsAre, UnorderedElementsAreArray, IsSubsetOf, and +// IsSupersetOf. +template <typename Container> +class UnorderedElementsAreMatcherImpl + : public MatcherInterface<Container>, + public UnorderedElementsAreMatcherImplBase { + public: + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef internal::StlContainerView<RawContainer> View; + typedef typename View::type StlContainer; + typedef typename View::const_reference StlContainerReference; + typedef typename StlContainer::const_iterator StlContainerConstIterator; + typedef typename StlContainer::value_type Element; + + template <typename InputIter> + UnorderedElementsAreMatcherImpl(UnorderedMatcherRequire::Flags matcher_flags, + InputIter first, InputIter last) + : UnorderedElementsAreMatcherImplBase(matcher_flags) { + for (; first != last; ++first) { + matchers_.push_back(MatcherCast<const Element&>(*first)); + } + for (const auto& m : matchers_) { + matcher_describers().push_back(m.GetDescriber()); + } + } + + // Describes what this matcher does. + void DescribeTo(::std::ostream* os) const override { + return UnorderedElementsAreMatcherImplBase::DescribeToImpl(os); + } + + // Describes what the negation of this matcher does. + void DescribeNegationTo(::std::ostream* os) const override { + return UnorderedElementsAreMatcherImplBase::DescribeNegationToImpl(os); + } + + bool MatchAndExplain(Container container, + MatchResultListener* listener) const override { + StlContainerReference stl_container = View::ConstReference(container); + ::std::vector<std::string> element_printouts; + MatchMatrix matrix = + AnalyzeElements(stl_container.begin(), stl_container.end(), + &element_printouts, listener); + + if (matrix.LhsSize() == 0 && matrix.RhsSize() == 0) { + return true; + } + + if (match_flags() == UnorderedMatcherRequire::ExactMatch) { + if (matrix.LhsSize() != matrix.RhsSize()) { + // The element count doesn't match. If the container is empty, + // there's no need to explain anything as Google Mock already + // prints the empty container. Otherwise we just need to show + // how many elements there actually are. + if (matrix.LhsSize() != 0 && listener->IsInterested()) { + *listener << "which has " << Elements(matrix.LhsSize()); + } + return false; + } + } + + return VerifyMatchMatrix(element_printouts, matrix, listener) && + FindPairing(matrix, listener); + } + + private: + template <typename ElementIter> + MatchMatrix AnalyzeElements(ElementIter elem_first, ElementIter elem_last, + ::std::vector<std::string>* element_printouts, + MatchResultListener* listener) const { + element_printouts->clear(); + ::std::vector<char> did_match; + size_t num_elements = 0; + DummyMatchResultListener dummy; + for (; elem_first != elem_last; ++num_elements, ++elem_first) { + if (listener->IsInterested()) { + element_printouts->push_back(PrintToString(*elem_first)); + } + for (size_t irhs = 0; irhs != matchers_.size(); ++irhs) { + did_match.push_back( + matchers_[irhs].MatchAndExplain(*elem_first, &dummy)); + } + } + + MatchMatrix matrix(num_elements, matchers_.size()); + ::std::vector<char>::const_iterator did_match_iter = did_match.begin(); + for (size_t ilhs = 0; ilhs != num_elements; ++ilhs) { + for (size_t irhs = 0; irhs != matchers_.size(); ++irhs) { + matrix.SetEdge(ilhs, irhs, *did_match_iter++ != 0); + } + } + return matrix; + } + + ::std::vector<Matcher<const Element&> > matchers_; +}; + +// Functor for use in TransformTuple. +// Performs MatcherCast<Target> on an input argument of any type. +template <typename Target> +struct CastAndAppendTransform { + template <typename Arg> + Matcher<Target> operator()(const Arg& a) const { + return MatcherCast<Target>(a); + } +}; + +// Implements UnorderedElementsAre. +template <typename MatcherTuple> +class UnorderedElementsAreMatcher { + public: + explicit UnorderedElementsAreMatcher(const MatcherTuple& args) + : matchers_(args) {} + + template <typename Container> + operator Matcher<Container>() const { + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView<RawContainer>::type View; + typedef typename View::value_type Element; + typedef ::std::vector<Matcher<const Element&> > MatcherVec; + MatcherVec matchers; + matchers.reserve(::std::tuple_size<MatcherTuple>::value); + TransformTupleValues(CastAndAppendTransform<const Element&>(), matchers_, + ::std::back_inserter(matchers)); + return Matcher<Container>( + new UnorderedElementsAreMatcherImpl<const Container&>( + UnorderedMatcherRequire::ExactMatch, matchers.begin(), + matchers.end())); + } + + private: + const MatcherTuple matchers_; +}; + +// Implements ElementsAre. +template <typename MatcherTuple> +class ElementsAreMatcher { + public: + explicit ElementsAreMatcher(const MatcherTuple& args) : matchers_(args) {} + + template <typename Container> + operator Matcher<Container>() const { + GTEST_COMPILE_ASSERT_( + !IsHashTable<GTEST_REMOVE_REFERENCE_AND_CONST_(Container)>::value || + ::std::tuple_size<MatcherTuple>::value < 2, + use_UnorderedElementsAre_with_hash_tables); + + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView<RawContainer>::type View; + typedef typename View::value_type Element; + typedef ::std::vector<Matcher<const Element&> > MatcherVec; + MatcherVec matchers; + matchers.reserve(::std::tuple_size<MatcherTuple>::value); + TransformTupleValues(CastAndAppendTransform<const Element&>(), matchers_, + ::std::back_inserter(matchers)); + return Matcher<Container>(new ElementsAreMatcherImpl<const Container&>( + matchers.begin(), matchers.end())); + } + + private: + const MatcherTuple matchers_; +}; + +// Implements UnorderedElementsAreArray(), IsSubsetOf(), and IsSupersetOf(). +template <typename T> +class UnorderedElementsAreArrayMatcher { + public: + template <typename Iter> + UnorderedElementsAreArrayMatcher(UnorderedMatcherRequire::Flags match_flags, + Iter first, Iter last) + : match_flags_(match_flags), matchers_(first, last) {} + + template <typename Container> + operator Matcher<Container>() const { + return Matcher<Container>( + new UnorderedElementsAreMatcherImpl<const Container&>( + match_flags_, matchers_.begin(), matchers_.end())); + } + + private: + UnorderedMatcherRequire::Flags match_flags_; + ::std::vector<T> matchers_; +}; + +// Implements ElementsAreArray(). +template <typename T> +class ElementsAreArrayMatcher { + public: + template <typename Iter> + ElementsAreArrayMatcher(Iter first, Iter last) : matchers_(first, last) {} + + template <typename Container> + operator Matcher<Container>() const { + GTEST_COMPILE_ASSERT_( + !IsHashTable<GTEST_REMOVE_REFERENCE_AND_CONST_(Container)>::value, + use_UnorderedElementsAreArray_with_hash_tables); + + return Matcher<Container>(new ElementsAreMatcherImpl<const Container&>( + matchers_.begin(), matchers_.end())); + } + + private: + const ::std::vector<T> matchers_; +}; + +// Given a 2-tuple matcher tm of type Tuple2Matcher and a value second +// of type Second, BoundSecondMatcher<Tuple2Matcher, Second>(tm, +// second) is a polymorphic matcher that matches a value x if and only if +// tm matches tuple (x, second). Useful for implementing +// UnorderedPointwise() in terms of UnorderedElementsAreArray(). +// +// BoundSecondMatcher is copyable and assignable, as we need to put +// instances of this class in a vector when implementing +// UnorderedPointwise(). +template <typename Tuple2Matcher, typename Second> +class BoundSecondMatcher { + public: + BoundSecondMatcher(const Tuple2Matcher& tm, const Second& second) + : tuple2_matcher_(tm), second_value_(second) {} + + BoundSecondMatcher(const BoundSecondMatcher& other) = default; + + template <typename T> + operator Matcher<T>() const { + return MakeMatcher(new Impl<T>(tuple2_matcher_, second_value_)); + } + + // We have to define this for UnorderedPointwise() to compile in + // C++98 mode, as it puts BoundSecondMatcher instances in a vector, + // which requires the elements to be assignable in C++98. The + // compiler cannot generate the operator= for us, as Tuple2Matcher + // and Second may not be assignable. + // + // However, this should never be called, so the implementation just + // need to assert. + void operator=(const BoundSecondMatcher& /*rhs*/) { + GTEST_LOG_(FATAL) << "BoundSecondMatcher should never be assigned."; + } + + private: + template <typename T> + class Impl : public MatcherInterface<T> { + public: + typedef ::std::tuple<T, Second> ArgTuple; + + Impl(const Tuple2Matcher& tm, const Second& second) + : mono_tuple2_matcher_(SafeMatcherCast<const ArgTuple&>(tm)), + second_value_(second) {} + + void DescribeTo(::std::ostream* os) const override { + *os << "and "; + UniversalPrint(second_value_, os); + *os << " "; + mono_tuple2_matcher_.DescribeTo(os); + } + + bool MatchAndExplain(T x, MatchResultListener* listener) const override { + return mono_tuple2_matcher_.MatchAndExplain(ArgTuple(x, second_value_), + listener); + } + + private: + const Matcher<const ArgTuple&> mono_tuple2_matcher_; + const Second second_value_; + }; + + const Tuple2Matcher tuple2_matcher_; + const Second second_value_; +}; + +// Given a 2-tuple matcher tm and a value second, +// MatcherBindSecond(tm, second) returns a matcher that matches a +// value x if and only if tm matches tuple (x, second). Useful for +// implementing UnorderedPointwise() in terms of UnorderedElementsAreArray(). +template <typename Tuple2Matcher, typename Second> +BoundSecondMatcher<Tuple2Matcher, Second> MatcherBindSecond( + const Tuple2Matcher& tm, const Second& second) { + return BoundSecondMatcher<Tuple2Matcher, Second>(tm, second); +} + +// Returns the description for a matcher defined using the MATCHER*() +// macro where the user-supplied description string is "", if +// 'negation' is false; otherwise returns the description of the +// negation of the matcher. 'param_values' contains a list of strings +// that are the print-out of the matcher's parameters. +GTEST_API_ std::string FormatMatcherDescription(bool negation, + const char* matcher_name, + const Strings& param_values); + +// Implements a matcher that checks the value of a optional<> type variable. +template <typename ValueMatcher> +class OptionalMatcher { + public: + explicit OptionalMatcher(const ValueMatcher& value_matcher) + : value_matcher_(value_matcher) {} + + template <typename Optional> + operator Matcher<Optional>() const { + return Matcher<Optional>(new Impl<const Optional&>(value_matcher_)); + } + + template <typename Optional> + class Impl : public MatcherInterface<Optional> { + public: + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Optional) OptionalView; + typedef typename OptionalView::value_type ValueType; + explicit Impl(const ValueMatcher& value_matcher) + : value_matcher_(MatcherCast<ValueType>(value_matcher)) {} + + void DescribeTo(::std::ostream* os) const override { + *os << "value "; + value_matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "value "; + value_matcher_.DescribeNegationTo(os); + } + + bool MatchAndExplain(Optional optional, + MatchResultListener* listener) const override { + if (!optional) { + *listener << "which is not engaged"; + return false; + } + const ValueType& value = *optional; + StringMatchResultListener value_listener; + const bool match = value_matcher_.MatchAndExplain(value, &value_listener); + *listener << "whose value " << PrintToString(value) + << (match ? " matches" : " doesn't match"); + PrintIfNotEmpty(value_listener.str(), listener->stream()); + return match; + } + + private: + const Matcher<ValueType> value_matcher_; + }; + + private: + const ValueMatcher value_matcher_; +}; + +namespace variant_matcher { +// Overloads to allow VariantMatcher to do proper ADL lookup. +template <typename T> +void holds_alternative() {} +template <typename T> +void get() {} + +// Implements a matcher that checks the value of a variant<> type variable. +template <typename T> +class VariantMatcher { + public: + explicit VariantMatcher(::testing::Matcher<const T&> matcher) + : matcher_(std::move(matcher)) {} + + template <typename Variant> + bool MatchAndExplain(const Variant& value, + ::testing::MatchResultListener* listener) const { + using std::get; + if (!listener->IsInterested()) { + return holds_alternative<T>(value) && matcher_.Matches(get<T>(value)); + } + + if (!holds_alternative<T>(value)) { + *listener << "whose value is not of type '" << GetTypeName() << "'"; + return false; + } + + const T& elem = get<T>(value); + StringMatchResultListener elem_listener; + const bool match = matcher_.MatchAndExplain(elem, &elem_listener); + *listener << "whose value " << PrintToString(elem) + << (match ? " matches" : " doesn't match"); + PrintIfNotEmpty(elem_listener.str(), listener->stream()); + return match; + } + + void DescribeTo(std::ostream* os) const { + *os << "is a variant<> with value of type '" << GetTypeName() + << "' and the value "; + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(std::ostream* os) const { + *os << "is a variant<> with value of type other than '" << GetTypeName() + << "' or the value "; + matcher_.DescribeNegationTo(os); + } + + private: + static std::string GetTypeName() { +#if GTEST_HAS_RTTI + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_( + return internal::GetTypeName<T>()); +#endif + return "the element type"; + } + + const ::testing::Matcher<const T&> matcher_; +}; + +} // namespace variant_matcher + +namespace any_cast_matcher { + +// Overloads to allow AnyCastMatcher to do proper ADL lookup. +template <typename T> +void any_cast() {} + +// Implements a matcher that any_casts the value. +template <typename T> +class AnyCastMatcher { + public: + explicit AnyCastMatcher(const ::testing::Matcher<const T&>& matcher) + : matcher_(matcher) {} + + template <typename AnyType> + bool MatchAndExplain(const AnyType& value, + ::testing::MatchResultListener* listener) const { + if (!listener->IsInterested()) { + const T* ptr = any_cast<T>(&value); + return ptr != nullptr && matcher_.Matches(*ptr); + } + + const T* elem = any_cast<T>(&value); + if (elem == nullptr) { + *listener << "whose value is not of type '" << GetTypeName() << "'"; + return false; + } + + StringMatchResultListener elem_listener; + const bool match = matcher_.MatchAndExplain(*elem, &elem_listener); + *listener << "whose value " << PrintToString(*elem) + << (match ? " matches" : " doesn't match"); + PrintIfNotEmpty(elem_listener.str(), listener->stream()); + return match; + } + + void DescribeTo(std::ostream* os) const { + *os << "is an 'any' type with value of type '" << GetTypeName() + << "' and the value "; + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(std::ostream* os) const { + *os << "is an 'any' type with value of type other than '" << GetTypeName() + << "' or the value "; + matcher_.DescribeNegationTo(os); + } + + private: + static std::string GetTypeName() { +#if GTEST_HAS_RTTI + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_( + return internal::GetTypeName<T>()); +#endif + return "the element type"; + } + + const ::testing::Matcher<const T&> matcher_; +}; + +} // namespace any_cast_matcher + +// Implements the Args() matcher. +template <class ArgsTuple, size_t... k> +class ArgsMatcherImpl : public MatcherInterface<ArgsTuple> { + public: + using RawArgsTuple = typename std::decay<ArgsTuple>::type; + using SelectedArgs = + std::tuple<typename std::tuple_element<k, RawArgsTuple>::type...>; + using MonomorphicInnerMatcher = Matcher<const SelectedArgs&>; + + template <typename InnerMatcher> + explicit ArgsMatcherImpl(const InnerMatcher& inner_matcher) + : inner_matcher_(SafeMatcherCast<const SelectedArgs&>(inner_matcher)) {} + + bool MatchAndExplain(ArgsTuple args, + MatchResultListener* listener) const override { + // Workaround spurious C4100 on MSVC<=15.7 when k is empty. + (void)args; + const SelectedArgs& selected_args = + std::forward_as_tuple(std::get<k>(args)...); + if (!listener->IsInterested()) return inner_matcher_.Matches(selected_args); + + PrintIndices(listener->stream()); + *listener << "are " << PrintToString(selected_args); + + StringMatchResultListener inner_listener; + const bool match = + inner_matcher_.MatchAndExplain(selected_args, &inner_listener); + PrintIfNotEmpty(inner_listener.str(), listener->stream()); + return match; + } + + void DescribeTo(::std::ostream* os) const override { + *os << "are a tuple "; + PrintIndices(os); + inner_matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "are a tuple "; + PrintIndices(os); + inner_matcher_.DescribeNegationTo(os); + } + + private: + // Prints the indices of the selected fields. + static void PrintIndices(::std::ostream* os) { + *os << "whose fields ("; + const char* sep = ""; + // Workaround spurious C4189 on MSVC<=15.7 when k is empty. + (void)sep; + const char* dummy[] = {"", (*os << sep << "#" << k, sep = ", ")...}; + (void)dummy; + *os << ") "; + } + + MonomorphicInnerMatcher inner_matcher_; +}; + +template <class InnerMatcher, size_t... k> +class ArgsMatcher { + public: + explicit ArgsMatcher(InnerMatcher inner_matcher) + : inner_matcher_(std::move(inner_matcher)) {} + + template <typename ArgsTuple> + operator Matcher<ArgsTuple>() const { // NOLINT + return MakeMatcher(new ArgsMatcherImpl<ArgsTuple, k...>(inner_matcher_)); + } + + private: + InnerMatcher inner_matcher_; +}; + +} // namespace internal + +// ElementsAreArray(iterator_first, iterator_last) +// ElementsAreArray(pointer, count) +// ElementsAreArray(array) +// ElementsAreArray(container) +// ElementsAreArray({ e1, e2, ..., en }) +// +// The ElementsAreArray() functions are like ElementsAre(...), except +// that they are given a homogeneous sequence rather than taking each +// element as a function argument. The sequence can be specified as an +// array, a pointer and count, a vector, an initializer list, or an +// STL iterator range. In each of these cases, the underlying sequence +// can be either a sequence of values or a sequence of matchers. +// +// All forms of ElementsAreArray() make a copy of the input matcher sequence. + +template <typename Iter> +inline internal::ElementsAreArrayMatcher< + typename ::std::iterator_traits<Iter>::value_type> +ElementsAreArray(Iter first, Iter last) { + typedef typename ::std::iterator_traits<Iter>::value_type T; + return internal::ElementsAreArrayMatcher<T>(first, last); +} + +template <typename T> +inline internal::ElementsAreArrayMatcher<T> ElementsAreArray( + const T* pointer, size_t count) { + return ElementsAreArray(pointer, pointer + count); +} + +template <typename T, size_t N> +inline internal::ElementsAreArrayMatcher<T> ElementsAreArray( + const T (&array)[N]) { + return ElementsAreArray(array, N); +} + +template <typename Container> +inline internal::ElementsAreArrayMatcher<typename Container::value_type> +ElementsAreArray(const Container& container) { + return ElementsAreArray(container.begin(), container.end()); +} + +template <typename T> +inline internal::ElementsAreArrayMatcher<T> +ElementsAreArray(::std::initializer_list<T> xs) { + return ElementsAreArray(xs.begin(), xs.end()); +} + +// UnorderedElementsAreArray(iterator_first, iterator_last) +// UnorderedElementsAreArray(pointer, count) +// UnorderedElementsAreArray(array) +// UnorderedElementsAreArray(container) +// UnorderedElementsAreArray({ e1, e2, ..., en }) +// +// UnorderedElementsAreArray() verifies that a bijective mapping onto a +// collection of matchers exists. +// +// The matchers can be specified as an array, a pointer and count, a container, +// an initializer list, or an STL iterator range. In each of these cases, the +// underlying matchers can be either values or matchers. + +template <typename Iter> +inline internal::UnorderedElementsAreArrayMatcher< + typename ::std::iterator_traits<Iter>::value_type> +UnorderedElementsAreArray(Iter first, Iter last) { + typedef typename ::std::iterator_traits<Iter>::value_type T; + return internal::UnorderedElementsAreArrayMatcher<T>( + internal::UnorderedMatcherRequire::ExactMatch, first, last); +} + +template <typename T> +inline internal::UnorderedElementsAreArrayMatcher<T> +UnorderedElementsAreArray(const T* pointer, size_t count) { + return UnorderedElementsAreArray(pointer, pointer + count); +} + +template <typename T, size_t N> +inline internal::UnorderedElementsAreArrayMatcher<T> +UnorderedElementsAreArray(const T (&array)[N]) { + return UnorderedElementsAreArray(array, N); +} + +template <typename Container> +inline internal::UnorderedElementsAreArrayMatcher< + typename Container::value_type> +UnorderedElementsAreArray(const Container& container) { + return UnorderedElementsAreArray(container.begin(), container.end()); +} + +template <typename T> +inline internal::UnorderedElementsAreArrayMatcher<T> +UnorderedElementsAreArray(::std::initializer_list<T> xs) { + return UnorderedElementsAreArray(xs.begin(), xs.end()); +} + +// _ is a matcher that matches anything of any type. +// +// This definition is fine as: +// +// 1. The C++ standard permits using the name _ in a namespace that +// is not the global namespace or ::std. +// 2. The AnythingMatcher class has no data member or constructor, +// so it's OK to create global variables of this type. +// 3. c-style has approved of using _ in this case. +const internal::AnythingMatcher _ = {}; +// Creates a matcher that matches any value of the given type T. +template <typename T> +inline Matcher<T> A() { + return _; +} + +// Creates a matcher that matches any value of the given type T. +template <typename T> +inline Matcher<T> An() { + return _; +} + +template <typename T, typename M> +Matcher<T> internal::MatcherCastImpl<T, M>::CastImpl( + const M& value, std::false_type /* convertible_to_matcher */, + std::false_type /* convertible_to_T */) { + return Eq(value); +} + +// Creates a polymorphic matcher that matches any NULL pointer. +inline PolymorphicMatcher<internal::IsNullMatcher > IsNull() { + return MakePolymorphicMatcher(internal::IsNullMatcher()); +} + +// Creates a polymorphic matcher that matches any non-NULL pointer. +// This is convenient as Not(NULL) doesn't compile (the compiler +// thinks that that expression is comparing a pointer with an integer). +inline PolymorphicMatcher<internal::NotNullMatcher > NotNull() { + return MakePolymorphicMatcher(internal::NotNullMatcher()); +} + +// Creates a polymorphic matcher that matches any argument that +// references variable x. +template <typename T> +inline internal::RefMatcher<T&> Ref(T& x) { // NOLINT + return internal::RefMatcher<T&>(x); +} + +// Creates a polymorphic matcher that matches any NaN floating point. +inline PolymorphicMatcher<internal::IsNanMatcher> IsNan() { + return MakePolymorphicMatcher(internal::IsNanMatcher()); +} + +// Creates a matcher that matches any double argument approximately +// equal to rhs, where two NANs are considered unequal. +inline internal::FloatingEqMatcher<double> DoubleEq(double rhs) { + return internal::FloatingEqMatcher<double>(rhs, false); +} + +// Creates a matcher that matches any double argument approximately +// equal to rhs, including NaN values when rhs is NaN. +inline internal::FloatingEqMatcher<double> NanSensitiveDoubleEq(double rhs) { + return internal::FloatingEqMatcher<double>(rhs, true); +} + +// Creates a matcher that matches any double argument approximately equal to +// rhs, up to the specified max absolute error bound, where two NANs are +// considered unequal. The max absolute error bound must be non-negative. +inline internal::FloatingEqMatcher<double> DoubleNear( + double rhs, double max_abs_error) { + return internal::FloatingEqMatcher<double>(rhs, false, max_abs_error); +} + +// Creates a matcher that matches any double argument approximately equal to +// rhs, up to the specified max absolute error bound, including NaN values when +// rhs is NaN. The max absolute error bound must be non-negative. +inline internal::FloatingEqMatcher<double> NanSensitiveDoubleNear( + double rhs, double max_abs_error) { + return internal::FloatingEqMatcher<double>(rhs, true, max_abs_error); +} + +// Creates a matcher that matches any float argument approximately +// equal to rhs, where two NANs are considered unequal. +inline internal::FloatingEqMatcher<float> FloatEq(float rhs) { + return internal::FloatingEqMatcher<float>(rhs, false); +} + +// Creates a matcher that matches any float argument approximately +// equal to rhs, including NaN values when rhs is NaN. +inline internal::FloatingEqMatcher<float> NanSensitiveFloatEq(float rhs) { + return internal::FloatingEqMatcher<float>(rhs, true); +} + +// Creates a matcher that matches any float argument approximately equal to +// rhs, up to the specified max absolute error bound, where two NANs are +// considered unequal. The max absolute error bound must be non-negative. +inline internal::FloatingEqMatcher<float> FloatNear( + float rhs, float max_abs_error) { + return internal::FloatingEqMatcher<float>(rhs, false, max_abs_error); +} + +// Creates a matcher that matches any float argument approximately equal to +// rhs, up to the specified max absolute error bound, including NaN values when +// rhs is NaN. The max absolute error bound must be non-negative. +inline internal::FloatingEqMatcher<float> NanSensitiveFloatNear( + float rhs, float max_abs_error) { + return internal::FloatingEqMatcher<float>(rhs, true, max_abs_error); +} + +// Creates a matcher that matches a pointer (raw or smart) that points +// to a value that matches inner_matcher. +template <typename InnerMatcher> +inline internal::PointeeMatcher<InnerMatcher> Pointee( + const InnerMatcher& inner_matcher) { + return internal::PointeeMatcher<InnerMatcher>(inner_matcher); +} + +#if GTEST_HAS_RTTI +// Creates a matcher that matches a pointer or reference that matches +// inner_matcher when dynamic_cast<To> is applied. +// The result of dynamic_cast<To> is forwarded to the inner matcher. +// If To is a pointer and the cast fails, the inner matcher will receive NULL. +// If To is a reference and the cast fails, this matcher returns false +// immediately. +template <typename To> +inline PolymorphicMatcher<internal::WhenDynamicCastToMatcher<To> > +WhenDynamicCastTo(const Matcher<To>& inner_matcher) { + return MakePolymorphicMatcher( + internal::WhenDynamicCastToMatcher<To>(inner_matcher)); +} +#endif // GTEST_HAS_RTTI + +// Creates a matcher that matches an object whose given field matches +// 'matcher'. For example, +// Field(&Foo::number, Ge(5)) +// matches a Foo object x if and only if x.number >= 5. +template <typename Class, typename FieldType, typename FieldMatcher> +inline PolymorphicMatcher< + internal::FieldMatcher<Class, FieldType> > Field( + FieldType Class::*field, const FieldMatcher& matcher) { + return MakePolymorphicMatcher( + internal::FieldMatcher<Class, FieldType>( + field, MatcherCast<const FieldType&>(matcher))); + // The call to MatcherCast() is required for supporting inner + // matchers of compatible types. For example, it allows + // Field(&Foo::bar, m) + // to compile where bar is an int32 and m is a matcher for int64. +} + +// Same as Field() but also takes the name of the field to provide better error +// messages. +template <typename Class, typename FieldType, typename FieldMatcher> +inline PolymorphicMatcher<internal::FieldMatcher<Class, FieldType> > Field( + const std::string& field_name, FieldType Class::*field, + const FieldMatcher& matcher) { + return MakePolymorphicMatcher(internal::FieldMatcher<Class, FieldType>( + field_name, field, MatcherCast<const FieldType&>(matcher))); +} + +// Creates a matcher that matches an object whose given property +// matches 'matcher'. For example, +// Property(&Foo::str, StartsWith("hi")) +// matches a Foo object x if and only if x.str() starts with "hi". +template <typename Class, typename PropertyType, typename PropertyMatcher> +inline PolymorphicMatcher<internal::PropertyMatcher< + Class, PropertyType, PropertyType (Class::*)() const> > +Property(PropertyType (Class::*property)() const, + const PropertyMatcher& matcher) { + return MakePolymorphicMatcher( + internal::PropertyMatcher<Class, PropertyType, + PropertyType (Class::*)() const>( + property, MatcherCast<const PropertyType&>(matcher))); + // The call to MatcherCast() is required for supporting inner + // matchers of compatible types. For example, it allows + // Property(&Foo::bar, m) + // to compile where bar() returns an int32 and m is a matcher for int64. +} + +// Same as Property() above, but also takes the name of the property to provide +// better error messages. +template <typename Class, typename PropertyType, typename PropertyMatcher> +inline PolymorphicMatcher<internal::PropertyMatcher< + Class, PropertyType, PropertyType (Class::*)() const> > +Property(const std::string& property_name, + PropertyType (Class::*property)() const, + const PropertyMatcher& matcher) { + return MakePolymorphicMatcher( + internal::PropertyMatcher<Class, PropertyType, + PropertyType (Class::*)() const>( + property_name, property, MatcherCast<const PropertyType&>(matcher))); +} + +// The same as above but for reference-qualified member functions. +template <typename Class, typename PropertyType, typename PropertyMatcher> +inline PolymorphicMatcher<internal::PropertyMatcher< + Class, PropertyType, PropertyType (Class::*)() const &> > +Property(PropertyType (Class::*property)() const &, + const PropertyMatcher& matcher) { + return MakePolymorphicMatcher( + internal::PropertyMatcher<Class, PropertyType, + PropertyType (Class::*)() const&>( + property, MatcherCast<const PropertyType&>(matcher))); +} + +// Three-argument form for reference-qualified member functions. +template <typename Class, typename PropertyType, typename PropertyMatcher> +inline PolymorphicMatcher<internal::PropertyMatcher< + Class, PropertyType, PropertyType (Class::*)() const &> > +Property(const std::string& property_name, + PropertyType (Class::*property)() const &, + const PropertyMatcher& matcher) { + return MakePolymorphicMatcher( + internal::PropertyMatcher<Class, PropertyType, + PropertyType (Class::*)() const&>( + property_name, property, MatcherCast<const PropertyType&>(matcher))); +} + +// Creates a matcher that matches an object if and only if the result of +// applying a callable to x matches 'matcher'. For example, +// ResultOf(f, StartsWith("hi")) +// matches a Foo object x if and only if f(x) starts with "hi". +// `callable` parameter can be a function, function pointer, or a functor. It is +// required to keep no state affecting the results of the calls on it and make +// no assumptions about how many calls will be made. Any state it keeps must be +// protected from the concurrent access. +template <typename Callable, typename InnerMatcher> +internal::ResultOfMatcher<Callable, InnerMatcher> ResultOf( + Callable callable, InnerMatcher matcher) { + return internal::ResultOfMatcher<Callable, InnerMatcher>( + std::move(callable), std::move(matcher)); +} + +// String matchers. + +// Matches a string equal to str. +template <typename T = std::string> +PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrEq( + const internal::StringLike<T>& str) { + return MakePolymorphicMatcher( + internal::StrEqualityMatcher<std::string>(std::string(str), true, true)); +} + +// Matches a string not equal to str. +template <typename T = std::string> +PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrNe( + const internal::StringLike<T>& str) { + return MakePolymorphicMatcher( + internal::StrEqualityMatcher<std::string>(std::string(str), false, true)); +} + +// Matches a string equal to str, ignoring case. +template <typename T = std::string> +PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrCaseEq( + const internal::StringLike<T>& str) { + return MakePolymorphicMatcher( + internal::StrEqualityMatcher<std::string>(std::string(str), true, false)); +} + +// Matches a string not equal to str, ignoring case. +template <typename T = std::string> +PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrCaseNe( + const internal::StringLike<T>& str) { + return MakePolymorphicMatcher(internal::StrEqualityMatcher<std::string>( + std::string(str), false, false)); +} + +// Creates a matcher that matches any string, std::string, or C string +// that contains the given substring. +template <typename T = std::string> +PolymorphicMatcher<internal::HasSubstrMatcher<std::string> > HasSubstr( + const internal::StringLike<T>& substring) { + return MakePolymorphicMatcher( + internal::HasSubstrMatcher<std::string>(std::string(substring))); +} + +// Matches a string that starts with 'prefix' (case-sensitive). +template <typename T = std::string> +PolymorphicMatcher<internal::StartsWithMatcher<std::string> > StartsWith( + const internal::StringLike<T>& prefix) { + return MakePolymorphicMatcher( + internal::StartsWithMatcher<std::string>(std::string(prefix))); +} + +// Matches a string that ends with 'suffix' (case-sensitive). +template <typename T = std::string> +PolymorphicMatcher<internal::EndsWithMatcher<std::string> > EndsWith( + const internal::StringLike<T>& suffix) { + return MakePolymorphicMatcher( + internal::EndsWithMatcher<std::string>(std::string(suffix))); +} + +#if GTEST_HAS_STD_WSTRING +// Wide string matchers. + +// Matches a string equal to str. +inline PolymorphicMatcher<internal::StrEqualityMatcher<std::wstring> > StrEq( + const std::wstring& str) { + return MakePolymorphicMatcher( + internal::StrEqualityMatcher<std::wstring>(str, true, true)); +} + +// Matches a string not equal to str. +inline PolymorphicMatcher<internal::StrEqualityMatcher<std::wstring> > StrNe( + const std::wstring& str) { + return MakePolymorphicMatcher( + internal::StrEqualityMatcher<std::wstring>(str, false, true)); +} + +// Matches a string equal to str, ignoring case. +inline PolymorphicMatcher<internal::StrEqualityMatcher<std::wstring> > +StrCaseEq(const std::wstring& str) { + return MakePolymorphicMatcher( + internal::StrEqualityMatcher<std::wstring>(str, true, false)); +} + +// Matches a string not equal to str, ignoring case. +inline PolymorphicMatcher<internal::StrEqualityMatcher<std::wstring> > +StrCaseNe(const std::wstring& str) { + return MakePolymorphicMatcher( + internal::StrEqualityMatcher<std::wstring>(str, false, false)); +} + +// Creates a matcher that matches any ::wstring, std::wstring, or C wide string +// that contains the given substring. +inline PolymorphicMatcher<internal::HasSubstrMatcher<std::wstring> > HasSubstr( + const std::wstring& substring) { + return MakePolymorphicMatcher( + internal::HasSubstrMatcher<std::wstring>(substring)); +} + +// Matches a string that starts with 'prefix' (case-sensitive). +inline PolymorphicMatcher<internal::StartsWithMatcher<std::wstring> > +StartsWith(const std::wstring& prefix) { + return MakePolymorphicMatcher( + internal::StartsWithMatcher<std::wstring>(prefix)); +} + +// Matches a string that ends with 'suffix' (case-sensitive). +inline PolymorphicMatcher<internal::EndsWithMatcher<std::wstring> > EndsWith( + const std::wstring& suffix) { + return MakePolymorphicMatcher( + internal::EndsWithMatcher<std::wstring>(suffix)); +} + +#endif // GTEST_HAS_STD_WSTRING + +// Creates a polymorphic matcher that matches a 2-tuple where the +// first field == the second field. +inline internal::Eq2Matcher Eq() { return internal::Eq2Matcher(); } + +// Creates a polymorphic matcher that matches a 2-tuple where the +// first field >= the second field. +inline internal::Ge2Matcher Ge() { return internal::Ge2Matcher(); } + +// Creates a polymorphic matcher that matches a 2-tuple where the +// first field > the second field. +inline internal::Gt2Matcher Gt() { return internal::Gt2Matcher(); } + +// Creates a polymorphic matcher that matches a 2-tuple where the +// first field <= the second field. +inline internal::Le2Matcher Le() { return internal::Le2Matcher(); } + +// Creates a polymorphic matcher that matches a 2-tuple where the +// first field < the second field. +inline internal::Lt2Matcher Lt() { return internal::Lt2Matcher(); } + +// Creates a polymorphic matcher that matches a 2-tuple where the +// first field != the second field. +inline internal::Ne2Matcher Ne() { return internal::Ne2Matcher(); } + +// Creates a polymorphic matcher that matches a 2-tuple where +// FloatEq(first field) matches the second field. +inline internal::FloatingEq2Matcher<float> FloatEq() { + return internal::FloatingEq2Matcher<float>(); +} + +// Creates a polymorphic matcher that matches a 2-tuple where +// DoubleEq(first field) matches the second field. +inline internal::FloatingEq2Matcher<double> DoubleEq() { + return internal::FloatingEq2Matcher<double>(); +} + +// Creates a polymorphic matcher that matches a 2-tuple where +// FloatEq(first field) matches the second field with NaN equality. +inline internal::FloatingEq2Matcher<float> NanSensitiveFloatEq() { + return internal::FloatingEq2Matcher<float>(true); +} + +// Creates a polymorphic matcher that matches a 2-tuple where +// DoubleEq(first field) matches the second field with NaN equality. +inline internal::FloatingEq2Matcher<double> NanSensitiveDoubleEq() { + return internal::FloatingEq2Matcher<double>(true); +} + +// Creates a polymorphic matcher that matches a 2-tuple where +// FloatNear(first field, max_abs_error) matches the second field. +inline internal::FloatingEq2Matcher<float> FloatNear(float max_abs_error) { + return internal::FloatingEq2Matcher<float>(max_abs_error); +} + +// Creates a polymorphic matcher that matches a 2-tuple where +// DoubleNear(first field, max_abs_error) matches the second field. +inline internal::FloatingEq2Matcher<double> DoubleNear(double max_abs_error) { + return internal::FloatingEq2Matcher<double>(max_abs_error); +} + +// Creates a polymorphic matcher that matches a 2-tuple where +// FloatNear(first field, max_abs_error) matches the second field with NaN +// equality. +inline internal::FloatingEq2Matcher<float> NanSensitiveFloatNear( + float max_abs_error) { + return internal::FloatingEq2Matcher<float>(max_abs_error, true); +} + +// Creates a polymorphic matcher that matches a 2-tuple where +// DoubleNear(first field, max_abs_error) matches the second field with NaN +// equality. +inline internal::FloatingEq2Matcher<double> NanSensitiveDoubleNear( + double max_abs_error) { + return internal::FloatingEq2Matcher<double>(max_abs_error, true); +} + +// Creates a matcher that matches any value of type T that m doesn't +// match. +template <typename InnerMatcher> +inline internal::NotMatcher<InnerMatcher> Not(InnerMatcher m) { + return internal::NotMatcher<InnerMatcher>(m); +} + +// Returns a matcher that matches anything that satisfies the given +// predicate. The predicate can be any unary function or functor +// whose return type can be implicitly converted to bool. +template <typename Predicate> +inline PolymorphicMatcher<internal::TrulyMatcher<Predicate> > +Truly(Predicate pred) { + return MakePolymorphicMatcher(internal::TrulyMatcher<Predicate>(pred)); +} + +// Returns a matcher that matches the container size. The container must +// support both size() and size_type which all STL-like containers provide. +// Note that the parameter 'size' can be a value of type size_type as well as +// matcher. For instance: +// EXPECT_THAT(container, SizeIs(2)); // Checks container has 2 elements. +// EXPECT_THAT(container, SizeIs(Le(2)); // Checks container has at most 2. +template <typename SizeMatcher> +inline internal::SizeIsMatcher<SizeMatcher> +SizeIs(const SizeMatcher& size_matcher) { + return internal::SizeIsMatcher<SizeMatcher>(size_matcher); +} + +// Returns a matcher that matches the distance between the container's begin() +// iterator and its end() iterator, i.e. the size of the container. This matcher +// can be used instead of SizeIs with containers such as std::forward_list which +// do not implement size(). The container must provide const_iterator (with +// valid iterator_traits), begin() and end(). +template <typename DistanceMatcher> +inline internal::BeginEndDistanceIsMatcher<DistanceMatcher> +BeginEndDistanceIs(const DistanceMatcher& distance_matcher) { + return internal::BeginEndDistanceIsMatcher<DistanceMatcher>(distance_matcher); +} + +// Returns a matcher that matches an equal container. +// This matcher behaves like Eq(), but in the event of mismatch lists the +// values that are included in one container but not the other. (Duplicate +// values and order differences are not explained.) +template <typename Container> +inline PolymorphicMatcher<internal::ContainerEqMatcher< + typename std::remove_const<Container>::type>> +ContainerEq(const Container& rhs) { + return MakePolymorphicMatcher(internal::ContainerEqMatcher<Container>(rhs)); +} + +// Returns a matcher that matches a container that, when sorted using +// the given comparator, matches container_matcher. +template <typename Comparator, typename ContainerMatcher> +inline internal::WhenSortedByMatcher<Comparator, ContainerMatcher> +WhenSortedBy(const Comparator& comparator, + const ContainerMatcher& container_matcher) { + return internal::WhenSortedByMatcher<Comparator, ContainerMatcher>( + comparator, container_matcher); +} + +// Returns a matcher that matches a container that, when sorted using +// the < operator, matches container_matcher. +template <typename ContainerMatcher> +inline internal::WhenSortedByMatcher<internal::LessComparator, ContainerMatcher> +WhenSorted(const ContainerMatcher& container_matcher) { + return + internal::WhenSortedByMatcher<internal::LessComparator, ContainerMatcher>( + internal::LessComparator(), container_matcher); +} + +// Matches an STL-style container or a native array that contains the +// same number of elements as in rhs, where its i-th element and rhs's +// i-th element (as a pair) satisfy the given pair matcher, for all i. +// TupleMatcher must be able to be safely cast to Matcher<std::tuple<const +// T1&, const T2&> >, where T1 and T2 are the types of elements in the +// LHS container and the RHS container respectively. +template <typename TupleMatcher, typename Container> +inline internal::PointwiseMatcher<TupleMatcher, + typename std::remove_const<Container>::type> +Pointwise(const TupleMatcher& tuple_matcher, const Container& rhs) { + return internal::PointwiseMatcher<TupleMatcher, Container>(tuple_matcher, + rhs); +} + + +// Supports the Pointwise(m, {a, b, c}) syntax. +template <typename TupleMatcher, typename T> +inline internal::PointwiseMatcher<TupleMatcher, std::vector<T> > Pointwise( + const TupleMatcher& tuple_matcher, std::initializer_list<T> rhs) { + return Pointwise(tuple_matcher, std::vector<T>(rhs)); +} + + +// UnorderedPointwise(pair_matcher, rhs) matches an STL-style +// container or a native array that contains the same number of +// elements as in rhs, where in some permutation of the container, its +// i-th element and rhs's i-th element (as a pair) satisfy the given +// pair matcher, for all i. Tuple2Matcher must be able to be safely +// cast to Matcher<std::tuple<const T1&, const T2&> >, where T1 and T2 are +// the types of elements in the LHS container and the RHS container +// respectively. +// +// This is like Pointwise(pair_matcher, rhs), except that the element +// order doesn't matter. +template <typename Tuple2Matcher, typename RhsContainer> +inline internal::UnorderedElementsAreArrayMatcher< + typename internal::BoundSecondMatcher< + Tuple2Matcher, + typename internal::StlContainerView< + typename std::remove_const<RhsContainer>::type>::type::value_type>> +UnorderedPointwise(const Tuple2Matcher& tuple2_matcher, + const RhsContainer& rhs_container) { + // RhsView allows the same code to handle RhsContainer being a + // STL-style container and it being a native C-style array. + typedef typename internal::StlContainerView<RhsContainer> RhsView; + typedef typename RhsView::type RhsStlContainer; + typedef typename RhsStlContainer::value_type Second; + const RhsStlContainer& rhs_stl_container = + RhsView::ConstReference(rhs_container); + + // Create a matcher for each element in rhs_container. + ::std::vector<internal::BoundSecondMatcher<Tuple2Matcher, Second> > matchers; + for (typename RhsStlContainer::const_iterator it = rhs_stl_container.begin(); + it != rhs_stl_container.end(); ++it) { + matchers.push_back( + internal::MatcherBindSecond(tuple2_matcher, *it)); + } + + // Delegate the work to UnorderedElementsAreArray(). + return UnorderedElementsAreArray(matchers); +} + + +// Supports the UnorderedPointwise(m, {a, b, c}) syntax. +template <typename Tuple2Matcher, typename T> +inline internal::UnorderedElementsAreArrayMatcher< + typename internal::BoundSecondMatcher<Tuple2Matcher, T> > +UnorderedPointwise(const Tuple2Matcher& tuple2_matcher, + std::initializer_list<T> rhs) { + return UnorderedPointwise(tuple2_matcher, std::vector<T>(rhs)); +} + + +// Matches an STL-style container or a native array that contains at +// least one element matching the given value or matcher. +// +// Examples: +// ::std::set<int> page_ids; +// page_ids.insert(3); +// page_ids.insert(1); +// EXPECT_THAT(page_ids, Contains(1)); +// EXPECT_THAT(page_ids, Contains(Gt(2))); +// EXPECT_THAT(page_ids, Not(Contains(4))); +// +// ::std::map<int, size_t> page_lengths; +// page_lengths[1] = 100; +// EXPECT_THAT(page_lengths, +// Contains(::std::pair<const int, size_t>(1, 100))); +// +// const char* user_ids[] = { "joe", "mike", "tom" }; +// EXPECT_THAT(user_ids, Contains(Eq(::std::string("tom")))); +template <typename M> +inline internal::ContainsMatcher<M> Contains(M matcher) { + return internal::ContainsMatcher<M>(matcher); +} + +// IsSupersetOf(iterator_first, iterator_last) +// IsSupersetOf(pointer, count) +// IsSupersetOf(array) +// IsSupersetOf(container) +// IsSupersetOf({e1, e2, ..., en}) +// +// IsSupersetOf() verifies that a surjective partial mapping onto a collection +// of matchers exists. In other words, a container matches +// IsSupersetOf({e1, ..., en}) if and only if there is a permutation +// {y1, ..., yn} of some of the container's elements where y1 matches e1, +// ..., and yn matches en. Obviously, the size of the container must be >= n +// in order to have a match. Examples: +// +// - {1, 2, 3} matches IsSupersetOf({Ge(3), Ne(0)}), as 3 matches Ge(3) and +// 1 matches Ne(0). +// - {1, 2} doesn't match IsSupersetOf({Eq(1), Lt(2)}), even though 1 matches +// both Eq(1) and Lt(2). The reason is that different matchers must be used +// for elements in different slots of the container. +// - {1, 1, 2} matches IsSupersetOf({Eq(1), Lt(2)}), as (the first) 1 matches +// Eq(1) and (the second) 1 matches Lt(2). +// - {1, 2, 3} matches IsSupersetOf(Gt(1), Gt(1)), as 2 matches (the first) +// Gt(1) and 3 matches (the second) Gt(1). +// +// The matchers can be specified as an array, a pointer and count, a container, +// an initializer list, or an STL iterator range. In each of these cases, the +// underlying matchers can be either values or matchers. + +template <typename Iter> +inline internal::UnorderedElementsAreArrayMatcher< + typename ::std::iterator_traits<Iter>::value_type> +IsSupersetOf(Iter first, Iter last) { + typedef typename ::std::iterator_traits<Iter>::value_type T; + return internal::UnorderedElementsAreArrayMatcher<T>( + internal::UnorderedMatcherRequire::Superset, first, last); +} + +template <typename T> +inline internal::UnorderedElementsAreArrayMatcher<T> IsSupersetOf( + const T* pointer, size_t count) { + return IsSupersetOf(pointer, pointer + count); +} + +template <typename T, size_t N> +inline internal::UnorderedElementsAreArrayMatcher<T> IsSupersetOf( + const T (&array)[N]) { + return IsSupersetOf(array, N); +} + +template <typename Container> +inline internal::UnorderedElementsAreArrayMatcher< + typename Container::value_type> +IsSupersetOf(const Container& container) { + return IsSupersetOf(container.begin(), container.end()); +} + +template <typename T> +inline internal::UnorderedElementsAreArrayMatcher<T> IsSupersetOf( + ::std::initializer_list<T> xs) { + return IsSupersetOf(xs.begin(), xs.end()); +} + +// IsSubsetOf(iterator_first, iterator_last) +// IsSubsetOf(pointer, count) +// IsSubsetOf(array) +// IsSubsetOf(container) +// IsSubsetOf({e1, e2, ..., en}) +// +// IsSubsetOf() verifies that an injective mapping onto a collection of matchers +// exists. In other words, a container matches IsSubsetOf({e1, ..., en}) if and +// only if there is a subset of matchers {m1, ..., mk} which would match the +// container using UnorderedElementsAre. Obviously, the size of the container +// must be <= n in order to have a match. Examples: +// +// - {1} matches IsSubsetOf({Gt(0), Lt(0)}), as 1 matches Gt(0). +// - {1, -1} matches IsSubsetOf({Lt(0), Gt(0)}), as 1 matches Gt(0) and -1 +// matches Lt(0). +// - {1, 2} doesn't matches IsSubsetOf({Gt(0), Lt(0)}), even though 1 and 2 both +// match Gt(0). The reason is that different matchers must be used for +// elements in different slots of the container. +// +// The matchers can be specified as an array, a pointer and count, a container, +// an initializer list, or an STL iterator range. In each of these cases, the +// underlying matchers can be either values or matchers. + +template <typename Iter> +inline internal::UnorderedElementsAreArrayMatcher< + typename ::std::iterator_traits<Iter>::value_type> +IsSubsetOf(Iter first, Iter last) { + typedef typename ::std::iterator_traits<Iter>::value_type T; + return internal::UnorderedElementsAreArrayMatcher<T>( + internal::UnorderedMatcherRequire::Subset, first, last); +} + +template <typename T> +inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf( + const T* pointer, size_t count) { + return IsSubsetOf(pointer, pointer + count); +} + +template <typename T, size_t N> +inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf( + const T (&array)[N]) { + return IsSubsetOf(array, N); +} + +template <typename Container> +inline internal::UnorderedElementsAreArrayMatcher< + typename Container::value_type> +IsSubsetOf(const Container& container) { + return IsSubsetOf(container.begin(), container.end()); +} + +template <typename T> +inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf( + ::std::initializer_list<T> xs) { + return IsSubsetOf(xs.begin(), xs.end()); +} + +// Matches an STL-style container or a native array that contains only +// elements matching the given value or matcher. +// +// Each(m) is semantically equivalent to Not(Contains(Not(m))). Only +// the messages are different. +// +// Examples: +// ::std::set<int> page_ids; +// // Each(m) matches an empty container, regardless of what m is. +// EXPECT_THAT(page_ids, Each(Eq(1))); +// EXPECT_THAT(page_ids, Each(Eq(77))); +// +// page_ids.insert(3); +// EXPECT_THAT(page_ids, Each(Gt(0))); +// EXPECT_THAT(page_ids, Not(Each(Gt(4)))); +// page_ids.insert(1); +// EXPECT_THAT(page_ids, Not(Each(Lt(2)))); +// +// ::std::map<int, size_t> page_lengths; +// page_lengths[1] = 100; +// page_lengths[2] = 200; +// page_lengths[3] = 300; +// EXPECT_THAT(page_lengths, Not(Each(Pair(1, 100)))); +// EXPECT_THAT(page_lengths, Each(Key(Le(3)))); +// +// const char* user_ids[] = { "joe", "mike", "tom" }; +// EXPECT_THAT(user_ids, Not(Each(Eq(::std::string("tom"))))); +template <typename M> +inline internal::EachMatcher<M> Each(M matcher) { + return internal::EachMatcher<M>(matcher); +} + +// Key(inner_matcher) matches an std::pair whose 'first' field matches +// inner_matcher. For example, Contains(Key(Ge(5))) can be used to match an +// std::map that contains at least one element whose key is >= 5. +template <typename M> +inline internal::KeyMatcher<M> Key(M inner_matcher) { + return internal::KeyMatcher<M>(inner_matcher); +} + +// Pair(first_matcher, second_matcher) matches a std::pair whose 'first' field +// matches first_matcher and whose 'second' field matches second_matcher. For +// example, EXPECT_THAT(map_type, ElementsAre(Pair(Ge(5), "foo"))) can be used +// to match a std::map<int, string> that contains exactly one element whose key +// is >= 5 and whose value equals "foo". +template <typename FirstMatcher, typename SecondMatcher> +inline internal::PairMatcher<FirstMatcher, SecondMatcher> +Pair(FirstMatcher first_matcher, SecondMatcher second_matcher) { + return internal::PairMatcher<FirstMatcher, SecondMatcher>( + first_matcher, second_matcher); +} + +namespace no_adl { +// FieldsAre(matchers...) matches piecewise the fields of compatible structs. +// These include those that support `get<I>(obj)`, and when structured bindings +// are enabled any class that supports them. +// In particular, `std::tuple`, `std::pair`, `std::array` and aggregate types. +template <typename... M> +internal::FieldsAreMatcher<typename std::decay<M>::type...> FieldsAre( + M&&... matchers) { + return internal::FieldsAreMatcher<typename std::decay<M>::type...>( + std::forward<M>(matchers)...); +} + +// Creates a matcher that matches a pointer (raw or smart) that matches +// inner_matcher. +template <typename InnerMatcher> +inline internal::PointerMatcher<InnerMatcher> Pointer( + const InnerMatcher& inner_matcher) { + return internal::PointerMatcher<InnerMatcher>(inner_matcher); +} + +// Creates a matcher that matches an object that has an address that matches +// inner_matcher. +template <typename InnerMatcher> +inline internal::AddressMatcher<InnerMatcher> Address( + const InnerMatcher& inner_matcher) { + return internal::AddressMatcher<InnerMatcher>(inner_matcher); +} +} // namespace no_adl + +// Returns a predicate that is satisfied by anything that matches the +// given matcher. +template <typename M> +inline internal::MatcherAsPredicate<M> Matches(M matcher) { + return internal::MatcherAsPredicate<M>(matcher); +} + +// Returns true if and only if the value matches the matcher. +template <typename T, typename M> +inline bool Value(const T& value, M matcher) { + return testing::Matches(matcher)(value); +} + +// Matches the value against the given matcher and explains the match +// result to listener. +template <typename T, typename M> +inline bool ExplainMatchResult( + M matcher, const T& value, MatchResultListener* listener) { + return SafeMatcherCast<const T&>(matcher).MatchAndExplain(value, listener); +} + +// Returns a string representation of the given matcher. Useful for description +// strings of matchers defined using MATCHER_P* macros that accept matchers as +// their arguments. For example: +// +// MATCHER_P(XAndYThat, matcher, +// "X that " + DescribeMatcher<int>(matcher, negation) + +// " and Y that " + DescribeMatcher<double>(matcher, negation)) { +// return ExplainMatchResult(matcher, arg.x(), result_listener) && +// ExplainMatchResult(matcher, arg.y(), result_listener); +// } +template <typename T, typename M> +std::string DescribeMatcher(const M& matcher, bool negation = false) { + ::std::stringstream ss; + Matcher<T> monomorphic_matcher = SafeMatcherCast<T>(matcher); + if (negation) { + monomorphic_matcher.DescribeNegationTo(&ss); + } else { + monomorphic_matcher.DescribeTo(&ss); + } + return ss.str(); +} + +template <typename... Args> +internal::ElementsAreMatcher< + std::tuple<typename std::decay<const Args&>::type...>> +ElementsAre(const Args&... matchers) { + return internal::ElementsAreMatcher< + std::tuple<typename std::decay<const Args&>::type...>>( + std::make_tuple(matchers...)); +} + +template <typename... Args> +internal::UnorderedElementsAreMatcher< + std::tuple<typename std::decay<const Args&>::type...>> +UnorderedElementsAre(const Args&... matchers) { + return internal::UnorderedElementsAreMatcher< + std::tuple<typename std::decay<const Args&>::type...>>( + std::make_tuple(matchers...)); +} + +// Define variadic matcher versions. +template <typename... Args> +internal::AllOfMatcher<typename std::decay<const Args&>::type...> AllOf( + const Args&... matchers) { + return internal::AllOfMatcher<typename std::decay<const Args&>::type...>( + matchers...); +} + +template <typename... Args> +internal::AnyOfMatcher<typename std::decay<const Args&>::type...> AnyOf( + const Args&... matchers) { + return internal::AnyOfMatcher<typename std::decay<const Args&>::type...>( + matchers...); +} + +// AnyOfArray(array) +// AnyOfArray(pointer, count) +// AnyOfArray(container) +// AnyOfArray({ e1, e2, ..., en }) +// AnyOfArray(iterator_first, iterator_last) +// +// AnyOfArray() verifies whether a given value matches any member of a +// collection of matchers. +// +// AllOfArray(array) +// AllOfArray(pointer, count) +// AllOfArray(container) +// AllOfArray({ e1, e2, ..., en }) +// AllOfArray(iterator_first, iterator_last) +// +// AllOfArray() verifies whether a given value matches all members of a +// collection of matchers. +// +// The matchers can be specified as an array, a pointer and count, a container, +// an initializer list, or an STL iterator range. In each of these cases, the +// underlying matchers can be either values or matchers. + +template <typename Iter> +inline internal::AnyOfArrayMatcher< + typename ::std::iterator_traits<Iter>::value_type> +AnyOfArray(Iter first, Iter last) { + return internal::AnyOfArrayMatcher< + typename ::std::iterator_traits<Iter>::value_type>(first, last); +} + +template <typename Iter> +inline internal::AllOfArrayMatcher< + typename ::std::iterator_traits<Iter>::value_type> +AllOfArray(Iter first, Iter last) { + return internal::AllOfArrayMatcher< + typename ::std::iterator_traits<Iter>::value_type>(first, last); +} + +template <typename T> +inline internal::AnyOfArrayMatcher<T> AnyOfArray(const T* ptr, size_t count) { + return AnyOfArray(ptr, ptr + count); +} + +template <typename T> +inline internal::AllOfArrayMatcher<T> AllOfArray(const T* ptr, size_t count) { + return AllOfArray(ptr, ptr + count); +} + +template <typename T, size_t N> +inline internal::AnyOfArrayMatcher<T> AnyOfArray(const T (&array)[N]) { + return AnyOfArray(array, N); +} + +template <typename T, size_t N> +inline internal::AllOfArrayMatcher<T> AllOfArray(const T (&array)[N]) { + return AllOfArray(array, N); +} + +template <typename Container> +inline internal::AnyOfArrayMatcher<typename Container::value_type> AnyOfArray( + const Container& container) { + return AnyOfArray(container.begin(), container.end()); +} + +template <typename Container> +inline internal::AllOfArrayMatcher<typename Container::value_type> AllOfArray( + const Container& container) { + return AllOfArray(container.begin(), container.end()); +} + +template <typename T> +inline internal::AnyOfArrayMatcher<T> AnyOfArray( + ::std::initializer_list<T> xs) { + return AnyOfArray(xs.begin(), xs.end()); +} + +template <typename T> +inline internal::AllOfArrayMatcher<T> AllOfArray( + ::std::initializer_list<T> xs) { + return AllOfArray(xs.begin(), xs.end()); +} + +// Args<N1, N2, ..., Nk>(a_matcher) matches a tuple if the selected +// fields of it matches a_matcher. C++ doesn't support default +// arguments for function templates, so we have to overload it. +template <size_t... k, typename InnerMatcher> +internal::ArgsMatcher<typename std::decay<InnerMatcher>::type, k...> Args( + InnerMatcher&& matcher) { + return internal::ArgsMatcher<typename std::decay<InnerMatcher>::type, k...>( + std::forward<InnerMatcher>(matcher)); +} + +// AllArgs(m) is a synonym of m. This is useful in +// +// EXPECT_CALL(foo, Bar(_, _)).With(AllArgs(Eq())); +// +// which is easier to read than +// +// EXPECT_CALL(foo, Bar(_, _)).With(Eq()); +template <typename InnerMatcher> +inline InnerMatcher AllArgs(const InnerMatcher& matcher) { return matcher; } + +// Returns a matcher that matches the value of an optional<> type variable. +// The matcher implementation only uses '!arg' and requires that the optional<> +// type has a 'value_type' member type and that '*arg' is of type 'value_type' +// and is printable using 'PrintToString'. It is compatible with +// std::optional/std::experimental::optional. +// Note that to compare an optional type variable against nullopt you should +// use Eq(nullopt) and not Eq(Optional(nullopt)). The latter implies that the +// optional value contains an optional itself. +template <typename ValueMatcher> +inline internal::OptionalMatcher<ValueMatcher> Optional( + const ValueMatcher& value_matcher) { + return internal::OptionalMatcher<ValueMatcher>(value_matcher); +} + +// Returns a matcher that matches the value of a absl::any type variable. +template <typename T> +PolymorphicMatcher<internal::any_cast_matcher::AnyCastMatcher<T> > AnyWith( + const Matcher<const T&>& matcher) { + return MakePolymorphicMatcher( + internal::any_cast_matcher::AnyCastMatcher<T>(matcher)); +} + +// Returns a matcher that matches the value of a variant<> type variable. +// The matcher implementation uses ADL to find the holds_alternative and get +// functions. +// It is compatible with std::variant. +template <typename T> +PolymorphicMatcher<internal::variant_matcher::VariantMatcher<T> > VariantWith( + const Matcher<const T&>& matcher) { + return MakePolymorphicMatcher( + internal::variant_matcher::VariantMatcher<T>(matcher)); +} + +#if GTEST_HAS_EXCEPTIONS + +// Anything inside the `internal` namespace is internal to the implementation +// and must not be used in user code! +namespace internal { + +class WithWhatMatcherImpl { + public: + WithWhatMatcherImpl(Matcher<std::string> matcher) + : matcher_(std::move(matcher)) {} + + void DescribeTo(std::ostream* os) const { + *os << "contains .what() that "; + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(std::ostream* os) const { + *os << "contains .what() that does not "; + matcher_.DescribeTo(os); + } + + template <typename Err> + bool MatchAndExplain(const Err& err, MatchResultListener* listener) const { + *listener << "which contains .what() that "; + return matcher_.MatchAndExplain(err.what(), listener); + } + + private: + const Matcher<std::string> matcher_; +}; + +inline PolymorphicMatcher<WithWhatMatcherImpl> WithWhat( + Matcher<std::string> m) { + return MakePolymorphicMatcher(WithWhatMatcherImpl(std::move(m))); +} + +template <typename Err> +class ExceptionMatcherImpl { + class NeverThrown { + public: + const char* what() const noexcept { + return "this exception should never be thrown"; + } + }; + + // If the matchee raises an exception of a wrong type, we'd like to + // catch it and print its message and type. To do that, we add an additional + // catch clause: + // + // try { ... } + // catch (const Err&) { /* an expected exception */ } + // catch (const std::exception&) { /* exception of a wrong type */ } + // + // However, if the `Err` itself is `std::exception`, we'd end up with two + // identical `catch` clauses: + // + // try { ... } + // catch (const std::exception&) { /* an expected exception */ } + // catch (const std::exception&) { /* exception of a wrong type */ } + // + // This can cause a warning or an error in some compilers. To resolve + // the issue, we use a fake error type whenever `Err` is `std::exception`: + // + // try { ... } + // catch (const std::exception&) { /* an expected exception */ } + // catch (const NeverThrown&) { /* exception of a wrong type */ } + using DefaultExceptionType = typename std::conditional< + std::is_same<typename std::remove_cv< + typename std::remove_reference<Err>::type>::type, + std::exception>::value, + const NeverThrown&, const std::exception&>::type; + + public: + ExceptionMatcherImpl(Matcher<const Err&> matcher) + : matcher_(std::move(matcher)) {} + + void DescribeTo(std::ostream* os) const { + *os << "throws an exception which is a " << GetTypeName<Err>(); + *os << " which "; + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(std::ostream* os) const { + *os << "throws an exception which is not a " << GetTypeName<Err>(); + *os << " which "; + matcher_.DescribeNegationTo(os); + } + + template <typename T> + bool MatchAndExplain(T&& x, MatchResultListener* listener) const { + try { + (void)(std::forward<T>(x)()); + } catch (const Err& err) { + *listener << "throws an exception which is a " << GetTypeName<Err>(); + *listener << " "; + return matcher_.MatchAndExplain(err, listener); + } catch (DefaultExceptionType err) { +#if GTEST_HAS_RTTI + *listener << "throws an exception of type " << GetTypeName(typeid(err)); + *listener << " "; +#else + *listener << "throws an std::exception-derived type "; +#endif + *listener << "with description \"" << err.what() << "\""; + return false; + } catch (...) { + *listener << "throws an exception of an unknown type"; + return false; + } + + *listener << "does not throw any exception"; + return false; + } + + private: + const Matcher<const Err&> matcher_; +}; + +} // namespace internal + +// Throws() +// Throws(exceptionMatcher) +// ThrowsMessage(messageMatcher) +// +// This matcher accepts a callable and verifies that when invoked, it throws +// an exception with the given type and properties. +// +// Examples: +// +// EXPECT_THAT( +// []() { throw std::runtime_error("message"); }, +// Throws<std::runtime_error>()); +// +// EXPECT_THAT( +// []() { throw std::runtime_error("message"); }, +// ThrowsMessage<std::runtime_error>(HasSubstr("message"))); +// +// EXPECT_THAT( +// []() { throw std::runtime_error("message"); }, +// Throws<std::runtime_error>( +// Property(&std::runtime_error::what, HasSubstr("message")))); + +template <typename Err> +PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> Throws() { + return MakePolymorphicMatcher( + internal::ExceptionMatcherImpl<Err>(A<const Err&>())); +} + +template <typename Err, typename ExceptionMatcher> +PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> Throws( + const ExceptionMatcher& exception_matcher) { + // Using matcher cast allows users to pass a matcher of a more broad type. + // For example user may want to pass Matcher<std::exception> + // to Throws<std::runtime_error>, or Matcher<int64> to Throws<int32>. + return MakePolymorphicMatcher(internal::ExceptionMatcherImpl<Err>( + SafeMatcherCast<const Err&>(exception_matcher))); +} + +template <typename Err, typename MessageMatcher> +PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> ThrowsMessage( + MessageMatcher&& message_matcher) { + static_assert(std::is_base_of<std::exception, Err>::value, + "expected an std::exception-derived type"); + return Throws<Err>(internal::WithWhat( + MatcherCast<std::string>(std::forward<MessageMatcher>(message_matcher)))); +} + +#endif // GTEST_HAS_EXCEPTIONS + +// These macros allow using matchers to check values in Google Test +// tests. ASSERT_THAT(value, matcher) and EXPECT_THAT(value, matcher) +// succeed if and only if the value matches the matcher. If the assertion +// fails, the value and the description of the matcher will be printed. +#define ASSERT_THAT(value, matcher) ASSERT_PRED_FORMAT1(\ + ::testing::internal::MakePredicateFormatterFromMatcher(matcher), value) +#define EXPECT_THAT(value, matcher) EXPECT_PRED_FORMAT1(\ + ::testing::internal::MakePredicateFormatterFromMatcher(matcher), value) + +// MATCHER* macroses itself are listed below. +#define MATCHER(name, description) \ + class name##Matcher \ + : public ::testing::internal::MatcherBaseImpl<name##Matcher> { \ + public: \ + template <typename arg_type> \ + class gmock_Impl : public ::testing::MatcherInterface<const arg_type&> { \ + public: \ + gmock_Impl() {} \ + bool MatchAndExplain( \ + const arg_type& arg, \ + ::testing::MatchResultListener* result_listener) const override; \ + void DescribeTo(::std::ostream* gmock_os) const override { \ + *gmock_os << FormatDescription(false); \ + } \ + void DescribeNegationTo(::std::ostream* gmock_os) const override { \ + *gmock_os << FormatDescription(true); \ + } \ + \ + private: \ + ::std::string FormatDescription(bool negation) const { \ + ::std::string gmock_description = (description); \ + if (!gmock_description.empty()) { \ + return gmock_description; \ + } \ + return ::testing::internal::FormatMatcherDescription(negation, #name, \ + {}); \ + } \ + }; \ + }; \ + GTEST_ATTRIBUTE_UNUSED_ inline name##Matcher name() { return {}; } \ + template <typename arg_type> \ + bool name##Matcher::gmock_Impl<arg_type>::MatchAndExplain( \ + const arg_type& arg, \ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_) \ + const + +#define MATCHER_P(name, p0, description) \ + GMOCK_INTERNAL_MATCHER(name, name##MatcherP, description, (p0)) +#define MATCHER_P2(name, p0, p1, description) \ + GMOCK_INTERNAL_MATCHER(name, name##MatcherP2, description, (p0, p1)) +#define MATCHER_P3(name, p0, p1, p2, description) \ + GMOCK_INTERNAL_MATCHER(name, name##MatcherP3, description, (p0, p1, p2)) +#define MATCHER_P4(name, p0, p1, p2, p3, description) \ + GMOCK_INTERNAL_MATCHER(name, name##MatcherP4, description, (p0, p1, p2, p3)) +#define MATCHER_P5(name, p0, p1, p2, p3, p4, description) \ + GMOCK_INTERNAL_MATCHER(name, name##MatcherP5, description, \ + (p0, p1, p2, p3, p4)) +#define MATCHER_P6(name, p0, p1, p2, p3, p4, p5, description) \ + GMOCK_INTERNAL_MATCHER(name, name##MatcherP6, description, \ + (p0, p1, p2, p3, p4, p5)) +#define MATCHER_P7(name, p0, p1, p2, p3, p4, p5, p6, description) \ + GMOCK_INTERNAL_MATCHER(name, name##MatcherP7, description, \ + (p0, p1, p2, p3, p4, p5, p6)) +#define MATCHER_P8(name, p0, p1, p2, p3, p4, p5, p6, p7, description) \ + GMOCK_INTERNAL_MATCHER(name, name##MatcherP8, description, \ + (p0, p1, p2, p3, p4, p5, p6, p7)) +#define MATCHER_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, description) \ + GMOCK_INTERNAL_MATCHER(name, name##MatcherP9, description, \ + (p0, p1, p2, p3, p4, p5, p6, p7, p8)) +#define MATCHER_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, description) \ + GMOCK_INTERNAL_MATCHER(name, name##MatcherP10, description, \ + (p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) + +#define GMOCK_INTERNAL_MATCHER(name, full_name, description, args) \ + template <GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args)> \ + class full_name : public ::testing::internal::MatcherBaseImpl< \ + full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>> { \ + public: \ + using full_name::MatcherBaseImpl::MatcherBaseImpl; \ + template <typename arg_type> \ + class gmock_Impl : public ::testing::MatcherInterface<const arg_type&> { \ + public: \ + explicit gmock_Impl(GMOCK_INTERNAL_MATCHER_FUNCTION_ARGS(args)) \ + : GMOCK_INTERNAL_MATCHER_FORWARD_ARGS(args) {} \ + bool MatchAndExplain( \ + const arg_type& arg, \ + ::testing::MatchResultListener* result_listener) const override; \ + void DescribeTo(::std::ostream* gmock_os) const override { \ + *gmock_os << FormatDescription(false); \ + } \ + void DescribeNegationTo(::std::ostream* gmock_os) const override { \ + *gmock_os << FormatDescription(true); \ + } \ + GMOCK_INTERNAL_MATCHER_MEMBERS(args) \ + \ + private: \ + ::std::string FormatDescription(bool negation) const { \ + ::std::string gmock_description = (description); \ + if (!gmock_description.empty()) { \ + return gmock_description; \ + } \ + return ::testing::internal::FormatMatcherDescription( \ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings( \ + ::std::tuple<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>( \ + GMOCK_INTERNAL_MATCHER_MEMBERS_USAGE(args)))); \ + } \ + }; \ + }; \ + template <GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args)> \ + inline full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)> name( \ + GMOCK_INTERNAL_MATCHER_FUNCTION_ARGS(args)) { \ + return full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>( \ + GMOCK_INTERNAL_MATCHER_ARGS_USAGE(args)); \ + } \ + template <GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args)> \ + template <typename arg_type> \ + bool full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>::gmock_Impl< \ + arg_type>::MatchAndExplain(const arg_type& arg, \ + ::testing::MatchResultListener* \ + result_listener GTEST_ATTRIBUTE_UNUSED_) \ + const + +#define GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args) \ + GMOCK_PP_TAIL( \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAM, , args)) +#define GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAM(i_unused, data_unused, arg) \ + , typename arg##_type + +#define GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args) \ + GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_TYPE_PARAM, , args)) +#define GMOCK_INTERNAL_MATCHER_TYPE_PARAM(i_unused, data_unused, arg) \ + , arg##_type + +#define GMOCK_INTERNAL_MATCHER_FUNCTION_ARGS(args) \ + GMOCK_PP_TAIL(dummy_first GMOCK_PP_FOR_EACH( \ + GMOCK_INTERNAL_MATCHER_FUNCTION_ARG, , args)) +#define GMOCK_INTERNAL_MATCHER_FUNCTION_ARG(i, data_unused, arg) \ + , arg##_type gmock_p##i + +#define GMOCK_INTERNAL_MATCHER_FORWARD_ARGS(args) \ + GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_FORWARD_ARG, , args)) +#define GMOCK_INTERNAL_MATCHER_FORWARD_ARG(i, data_unused, arg) \ + , arg(::std::forward<arg##_type>(gmock_p##i)) + +#define GMOCK_INTERNAL_MATCHER_MEMBERS(args) \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_MEMBER, , args) +#define GMOCK_INTERNAL_MATCHER_MEMBER(i_unused, data_unused, arg) \ + const arg##_type arg; + +#define GMOCK_INTERNAL_MATCHER_MEMBERS_USAGE(args) \ + GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_MEMBER_USAGE, , args)) +#define GMOCK_INTERNAL_MATCHER_MEMBER_USAGE(i_unused, data_unused, arg) , arg + +#define GMOCK_INTERNAL_MATCHER_ARGS_USAGE(args) \ + GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_ARG_USAGE, , args)) +#define GMOCK_INTERNAL_MATCHER_ARG_USAGE(i, data_unused, arg_unused) \ + , gmock_p##i + +// To prevent ADL on certain functions we put them on a separate namespace. +using namespace no_adl; // NOLINT + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 5046 + +// Include any custom callback matchers added by the local installation. +// We must include this header at the end to make sure it can use the +// declarations from this file. +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. +// +// Injection point for custom user configurations. See README for details +// +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_MATCHERS_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_MATCHERS_H_ +#endif // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_MATCHERS_H_ + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ + +#if GTEST_HAS_EXCEPTIONS +# include <stdexcept> // NOLINT +#endif + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// An abstract handle of an expectation. +class Expectation; + +// A set of expectation handles. +class ExpectationSet; + +// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION +// and MUST NOT BE USED IN USER CODE!!! +namespace internal { + +// Implements a mock function. +template <typename F> class FunctionMocker; + +// Base class for expectations. +class ExpectationBase; + +// Implements an expectation. +template <typename F> class TypedExpectation; + +// Helper class for testing the Expectation class template. +class ExpectationTester; + +// Helper classes for implementing NiceMock, StrictMock, and NaggyMock. +template <typename MockClass> +class NiceMockImpl; +template <typename MockClass> +class StrictMockImpl; +template <typename MockClass> +class NaggyMockImpl; + +// Protects the mock object registry (in class Mock), all function +// mockers, and all expectations. +// +// The reason we don't use more fine-grained protection is: when a +// mock function Foo() is called, it needs to consult its expectations +// to see which one should be picked. If another thread is allowed to +// call a mock function (either Foo() or a different one) at the same +// time, it could affect the "retired" attributes of Foo()'s +// expectations when InSequence() is used, and thus affect which +// expectation gets picked. Therefore, we sequence all mock function +// calls to ensure the integrity of the mock objects' states. +GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_gmock_mutex); + +// Untyped base class for ActionResultHolder<R>. +class UntypedActionResultHolderBase; + +// Abstract base class of FunctionMocker. This is the +// type-agnostic part of the function mocker interface. Its pure +// virtual methods are implemented by FunctionMocker. +class GTEST_API_ UntypedFunctionMockerBase { + public: + UntypedFunctionMockerBase(); + virtual ~UntypedFunctionMockerBase(); + + // Verifies that all expectations on this mock function have been + // satisfied. Reports one or more Google Test non-fatal failures + // and returns false if not. + bool VerifyAndClearExpectationsLocked() + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex); + + // Clears the ON_CALL()s set on this mock function. + virtual void ClearDefaultActionsLocked() + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) = 0; + + // In all of the following Untyped* functions, it's the caller's + // responsibility to guarantee the correctness of the arguments' + // types. + + // Performs the default action with the given arguments and returns + // the action's result. The call description string will be used in + // the error message to describe the call in the case the default + // action fails. + // L = * + virtual UntypedActionResultHolderBase* UntypedPerformDefaultAction( + void* untyped_args, const std::string& call_description) const = 0; + + // Performs the given action with the given arguments and returns + // the action's result. + // L = * + virtual UntypedActionResultHolderBase* UntypedPerformAction( + const void* untyped_action, void* untyped_args) const = 0; + + // Writes a message that the call is uninteresting (i.e. neither + // explicitly expected nor explicitly unexpected) to the given + // ostream. + virtual void UntypedDescribeUninterestingCall( + const void* untyped_args, + ::std::ostream* os) const + GTEST_LOCK_EXCLUDED_(g_gmock_mutex) = 0; + + // Returns the expectation that matches the given function arguments + // (or NULL is there's no match); when a match is found, + // untyped_action is set to point to the action that should be + // performed (or NULL if the action is "do default"), and + // is_excessive is modified to indicate whether the call exceeds the + // expected number. + virtual const ExpectationBase* UntypedFindMatchingExpectation( + const void* untyped_args, + const void** untyped_action, bool* is_excessive, + ::std::ostream* what, ::std::ostream* why) + GTEST_LOCK_EXCLUDED_(g_gmock_mutex) = 0; + + // Prints the given function arguments to the ostream. + virtual void UntypedPrintArgs(const void* untyped_args, + ::std::ostream* os) const = 0; + + // Sets the mock object this mock method belongs to, and registers + // this information in the global mock registry. Will be called + // whenever an EXPECT_CALL() or ON_CALL() is executed on this mock + // method. + void RegisterOwner(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(g_gmock_mutex); + + // Sets the mock object this mock method belongs to, and sets the + // name of the mock function. Will be called upon each invocation + // of this mock function. + void SetOwnerAndName(const void* mock_obj, const char* name) + GTEST_LOCK_EXCLUDED_(g_gmock_mutex); + + // Returns the mock object this mock method belongs to. Must be + // called after RegisterOwner() or SetOwnerAndName() has been + // called. + const void* MockObject() const + GTEST_LOCK_EXCLUDED_(g_gmock_mutex); + + // Returns the name of this mock method. Must be called after + // SetOwnerAndName() has been called. + const char* Name() const + GTEST_LOCK_EXCLUDED_(g_gmock_mutex); + + // Returns the result of invoking this mock function with the given + // arguments. This function can be safely called from multiple + // threads concurrently. The caller is responsible for deleting the + // result. + UntypedActionResultHolderBase* UntypedInvokeWith(void* untyped_args) + GTEST_LOCK_EXCLUDED_(g_gmock_mutex); + + protected: + typedef std::vector<const void*> UntypedOnCallSpecs; + + using UntypedExpectations = std::vector<std::shared_ptr<ExpectationBase>>; + + // Returns an Expectation object that references and co-owns exp, + // which must be an expectation on this mock function. + Expectation GetHandleOf(ExpectationBase* exp); + + // Address of the mock object this mock method belongs to. Only + // valid after this mock method has been called or + // ON_CALL/EXPECT_CALL has been invoked on it. + const void* mock_obj_; // Protected by g_gmock_mutex. + + // Name of the function being mocked. Only valid after this mock + // method has been called. + const char* name_; // Protected by g_gmock_mutex. + + // All default action specs for this function mocker. + UntypedOnCallSpecs untyped_on_call_specs_; + + // All expectations for this function mocker. + // + // It's undefined behavior to interleave expectations (EXPECT_CALLs + // or ON_CALLs) and mock function calls. Also, the order of + // expectations is important. Therefore it's a logic race condition + // to read/write untyped_expectations_ concurrently. In order for + // tools like tsan to catch concurrent read/write accesses to + // untyped_expectations, we deliberately leave accesses to it + // unprotected. + UntypedExpectations untyped_expectations_; +}; // class UntypedFunctionMockerBase + +// Untyped base class for OnCallSpec<F>. +class UntypedOnCallSpecBase { + public: + // The arguments are the location of the ON_CALL() statement. + UntypedOnCallSpecBase(const char* a_file, int a_line) + : file_(a_file), line_(a_line), last_clause_(kNone) {} + + // Where in the source file was the default action spec defined? + const char* file() const { return file_; } + int line() const { return line_; } + + protected: + // Gives each clause in the ON_CALL() statement a name. + enum Clause { + // Do not change the order of the enum members! The run-time + // syntax checking relies on it. + kNone, + kWith, + kWillByDefault + }; + + // Asserts that the ON_CALL() statement has a certain property. + void AssertSpecProperty(bool property, + const std::string& failure_message) const { + Assert(property, file_, line_, failure_message); + } + + // Expects that the ON_CALL() statement has a certain property. + void ExpectSpecProperty(bool property, + const std::string& failure_message) const { + Expect(property, file_, line_, failure_message); + } + + const char* file_; + int line_; + + // The last clause in the ON_CALL() statement as seen so far. + // Initially kNone and changes as the statement is parsed. + Clause last_clause_; +}; // class UntypedOnCallSpecBase + +// This template class implements an ON_CALL spec. +template <typename F> +class OnCallSpec : public UntypedOnCallSpecBase { + public: + typedef typename Function<F>::ArgumentTuple ArgumentTuple; + typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple; + + // Constructs an OnCallSpec object from the information inside + // the parenthesis of an ON_CALL() statement. + OnCallSpec(const char* a_file, int a_line, + const ArgumentMatcherTuple& matchers) + : UntypedOnCallSpecBase(a_file, a_line), + matchers_(matchers), + // By default, extra_matcher_ should match anything. However, + // we cannot initialize it with _ as that causes ambiguity between + // Matcher's copy and move constructor for some argument types. + extra_matcher_(A<const ArgumentTuple&>()) {} + + // Implements the .With() clause. + OnCallSpec& With(const Matcher<const ArgumentTuple&>& m) { + // Makes sure this is called at most once. + ExpectSpecProperty(last_clause_ < kWith, + ".With() cannot appear " + "more than once in an ON_CALL()."); + last_clause_ = kWith; + + extra_matcher_ = m; + return *this; + } + + // Implements the .WillByDefault() clause. + OnCallSpec& WillByDefault(const Action<F>& action) { + ExpectSpecProperty(last_clause_ < kWillByDefault, + ".WillByDefault() must appear " + "exactly once in an ON_CALL()."); + last_clause_ = kWillByDefault; + + ExpectSpecProperty(!action.IsDoDefault(), + "DoDefault() cannot be used in ON_CALL()."); + action_ = action; + return *this; + } + + // Returns true if and only if the given arguments match the matchers. + bool Matches(const ArgumentTuple& args) const { + return TupleMatches(matchers_, args) && extra_matcher_.Matches(args); + } + + // Returns the action specified by the user. + const Action<F>& GetAction() const { + AssertSpecProperty(last_clause_ == kWillByDefault, + ".WillByDefault() must appear exactly " + "once in an ON_CALL()."); + return action_; + } + + private: + // The information in statement + // + // ON_CALL(mock_object, Method(matchers)) + // .With(multi-argument-matcher) + // .WillByDefault(action); + // + // is recorded in the data members like this: + // + // source file that contains the statement => file_ + // line number of the statement => line_ + // matchers => matchers_ + // multi-argument-matcher => extra_matcher_ + // action => action_ + ArgumentMatcherTuple matchers_; + Matcher<const ArgumentTuple&> extra_matcher_; + Action<F> action_; +}; // class OnCallSpec + +// Possible reactions on uninteresting calls. +enum CallReaction { + kAllow, + kWarn, + kFail, +}; + +} // namespace internal + +// Utilities for manipulating mock objects. +class GTEST_API_ Mock { + public: + // The following public methods can be called concurrently. + + // Tells Google Mock to ignore mock_obj when checking for leaked + // mock objects. + static void AllowLeak(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex); + + // Verifies and clears all expectations on the given mock object. + // If the expectations aren't satisfied, generates one or more + // Google Test non-fatal failures and returns false. + static bool VerifyAndClearExpectations(void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex); + + // Verifies all expectations on the given mock object and clears its + // default actions and expectations. Returns true if and only if the + // verification was successful. + static bool VerifyAndClear(void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex); + + // Returns whether the mock was created as a naggy mock (default) + static bool IsNaggy(void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex); + // Returns whether the mock was created as a nice mock + static bool IsNice(void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex); + // Returns whether the mock was created as a strict mock + static bool IsStrict(void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex); + + private: + friend class internal::UntypedFunctionMockerBase; + + // Needed for a function mocker to register itself (so that we know + // how to clear a mock object). + template <typename F> + friend class internal::FunctionMocker; + + template <typename MockClass> + friend class internal::NiceMockImpl; + template <typename MockClass> + friend class internal::NaggyMockImpl; + template <typename MockClass> + friend class internal::StrictMockImpl; + + // Tells Google Mock to allow uninteresting calls on the given mock + // object. + static void AllowUninterestingCalls(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex); + + // Tells Google Mock to warn the user about uninteresting calls on + // the given mock object. + static void WarnUninterestingCalls(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex); + + // Tells Google Mock to fail uninteresting calls on the given mock + // object. + static void FailUninterestingCalls(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex); + + // Tells Google Mock the given mock object is being destroyed and + // its entry in the call-reaction table should be removed. + static void UnregisterCallReaction(const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex); + + // Returns the reaction Google Mock will have on uninteresting calls + // made on the given mock object. + static internal::CallReaction GetReactionOnUninterestingCalls( + const void* mock_obj) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex); + + // Verifies that all expectations on the given mock object have been + // satisfied. Reports one or more Google Test non-fatal failures + // and returns false if not. + static bool VerifyAndClearExpectationsLocked(void* mock_obj) + GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex); + + // Clears all ON_CALL()s set on the given mock object. + static void ClearDefaultActionsLocked(void* mock_obj) + GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex); + + // Registers a mock object and a mock method it owns. + static void Register( + const void* mock_obj, + internal::UntypedFunctionMockerBase* mocker) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex); + + // Tells Google Mock where in the source code mock_obj is used in an + // ON_CALL or EXPECT_CALL. In case mock_obj is leaked, this + // information helps the user identify which object it is. + static void RegisterUseByOnCallOrExpectCall( + const void* mock_obj, const char* file, int line) + GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex); + + // Unregisters a mock method; removes the owning mock object from + // the registry when the last mock method associated with it has + // been unregistered. This is called only in the destructor of + // FunctionMocker. + static void UnregisterLocked(internal::UntypedFunctionMockerBase* mocker) + GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex); +}; // class Mock + +// An abstract handle of an expectation. Useful in the .After() +// clause of EXPECT_CALL() for setting the (partial) order of +// expectations. The syntax: +// +// Expectation e1 = EXPECT_CALL(...)...; +// EXPECT_CALL(...).After(e1)...; +// +// sets two expectations where the latter can only be matched after +// the former has been satisfied. +// +// Notes: +// - This class is copyable and has value semantics. +// - Constness is shallow: a const Expectation object itself cannot +// be modified, but the mutable methods of the ExpectationBase +// object it references can be called via expectation_base(). + +class GTEST_API_ Expectation { + public: + // Constructs a null object that doesn't reference any expectation. + Expectation(); + Expectation(Expectation&&) = default; + Expectation(const Expectation&) = default; + Expectation& operator=(Expectation&&) = default; + Expectation& operator=(const Expectation&) = default; + ~Expectation(); + + // This single-argument ctor must not be explicit, in order to support the + // Expectation e = EXPECT_CALL(...); + // syntax. + // + // A TypedExpectation object stores its pre-requisites as + // Expectation objects, and needs to call the non-const Retire() + // method on the ExpectationBase objects they reference. Therefore + // Expectation must receive a *non-const* reference to the + // ExpectationBase object. + Expectation(internal::ExpectationBase& exp); // NOLINT + + // The compiler-generated copy ctor and operator= work exactly as + // intended, so we don't need to define our own. + + // Returns true if and only if rhs references the same expectation as this + // object does. + bool operator==(const Expectation& rhs) const { + return expectation_base_ == rhs.expectation_base_; + } + + bool operator!=(const Expectation& rhs) const { return !(*this == rhs); } + + private: + friend class ExpectationSet; + friend class Sequence; + friend class ::testing::internal::ExpectationBase; + friend class ::testing::internal::UntypedFunctionMockerBase; + + template <typename F> + friend class ::testing::internal::FunctionMocker; + + template <typename F> + friend class ::testing::internal::TypedExpectation; + + // This comparator is needed for putting Expectation objects into a set. + class Less { + public: + bool operator()(const Expectation& lhs, const Expectation& rhs) const { + return lhs.expectation_base_.get() < rhs.expectation_base_.get(); + } + }; + + typedef ::std::set<Expectation, Less> Set; + + Expectation( + const std::shared_ptr<internal::ExpectationBase>& expectation_base); + + // Returns the expectation this object references. + const std::shared_ptr<internal::ExpectationBase>& expectation_base() const { + return expectation_base_; + } + + // A shared_ptr that co-owns the expectation this handle references. + std::shared_ptr<internal::ExpectationBase> expectation_base_; +}; + +// A set of expectation handles. Useful in the .After() clause of +// EXPECT_CALL() for setting the (partial) order of expectations. The +// syntax: +// +// ExpectationSet es; +// es += EXPECT_CALL(...)...; +// es += EXPECT_CALL(...)...; +// EXPECT_CALL(...).After(es)...; +// +// sets three expectations where the last one can only be matched +// after the first two have both been satisfied. +// +// This class is copyable and has value semantics. +class ExpectationSet { + public: + // A bidirectional iterator that can read a const element in the set. + typedef Expectation::Set::const_iterator const_iterator; + + // An object stored in the set. This is an alias of Expectation. + typedef Expectation::Set::value_type value_type; + + // Constructs an empty set. + ExpectationSet() {} + + // This single-argument ctor must not be explicit, in order to support the + // ExpectationSet es = EXPECT_CALL(...); + // syntax. + ExpectationSet(internal::ExpectationBase& exp) { // NOLINT + *this += Expectation(exp); + } + + // This single-argument ctor implements implicit conversion from + // Expectation and thus must not be explicit. This allows either an + // Expectation or an ExpectationSet to be used in .After(). + ExpectationSet(const Expectation& e) { // NOLINT + *this += e; + } + + // The compiler-generator ctor and operator= works exactly as + // intended, so we don't need to define our own. + + // Returns true if and only if rhs contains the same set of Expectation + // objects as this does. + bool operator==(const ExpectationSet& rhs) const { + return expectations_ == rhs.expectations_; + } + + bool operator!=(const ExpectationSet& rhs) const { return !(*this == rhs); } + + // Implements the syntax + // expectation_set += EXPECT_CALL(...); + ExpectationSet& operator+=(const Expectation& e) { + expectations_.insert(e); + return *this; + } + + int size() const { return static_cast<int>(expectations_.size()); } + + const_iterator begin() const { return expectations_.begin(); } + const_iterator end() const { return expectations_.end(); } + + private: + Expectation::Set expectations_; +}; + + +// Sequence objects are used by a user to specify the relative order +// in which the expectations should match. They are copyable (we rely +// on the compiler-defined copy constructor and assignment operator). +class GTEST_API_ Sequence { + public: + // Constructs an empty sequence. + Sequence() : last_expectation_(new Expectation) {} + + // Adds an expectation to this sequence. The caller must ensure + // that no other thread is accessing this Sequence object. + void AddExpectation(const Expectation& expectation) const; + + private: + // The last expectation in this sequence. + std::shared_ptr<Expectation> last_expectation_; +}; // class Sequence + +// An object of this type causes all EXPECT_CALL() statements +// encountered in its scope to be put in an anonymous sequence. The +// work is done in the constructor and destructor. You should only +// create an InSequence object on the stack. +// +// The sole purpose for this class is to support easy definition of +// sequential expectations, e.g. +// +// { +// InSequence dummy; // The name of the object doesn't matter. +// +// // The following expectations must match in the order they appear. +// EXPECT_CALL(a, Bar())...; +// EXPECT_CALL(a, Baz())...; +// ... +// EXPECT_CALL(b, Xyz())...; +// } +// +// You can create InSequence objects in multiple threads, as long as +// they are used to affect different mock objects. The idea is that +// each thread can create and set up its own mocks as if it's the only +// thread. However, for clarity of your tests we recommend you to set +// up mocks in the main thread unless you have a good reason not to do +// so. +class GTEST_API_ InSequence { + public: + InSequence(); + ~InSequence(); + private: + bool sequence_created_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InSequence); // NOLINT +} GTEST_ATTRIBUTE_UNUSED_; + +namespace internal { + +// Points to the implicit sequence introduced by a living InSequence +// object (if any) in the current thread or NULL. +GTEST_API_ extern ThreadLocal<Sequence*> g_gmock_implicit_sequence; + +// Base class for implementing expectations. +// +// There are two reasons for having a type-agnostic base class for +// Expectation: +// +// 1. We need to store collections of expectations of different +// types (e.g. all pre-requisites of a particular expectation, all +// expectations in a sequence). Therefore these expectation objects +// must share a common base class. +// +// 2. We can avoid binary code bloat by moving methods not depending +// on the template argument of Expectation to the base class. +// +// This class is internal and mustn't be used by user code directly. +class GTEST_API_ ExpectationBase { + public: + // source_text is the EXPECT_CALL(...) source that created this Expectation. + ExpectationBase(const char* file, int line, const std::string& source_text); + + virtual ~ExpectationBase(); + + // Where in the source file was the expectation spec defined? + const char* file() const { return file_; } + int line() const { return line_; } + const char* source_text() const { return source_text_.c_str(); } + // Returns the cardinality specified in the expectation spec. + const Cardinality& cardinality() const { return cardinality_; } + + // Describes the source file location of this expectation. + void DescribeLocationTo(::std::ostream* os) const { + *os << FormatFileLocation(file(), line()) << " "; + } + + // Describes how many times a function call matching this + // expectation has occurred. + void DescribeCallCountTo(::std::ostream* os) const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex); + + // If this mock method has an extra matcher (i.e. .With(matcher)), + // describes it to the ostream. + virtual void MaybeDescribeExtraMatcherTo(::std::ostream* os) = 0; + + protected: + friend class ::testing::Expectation; + friend class UntypedFunctionMockerBase; + + enum Clause { + // Don't change the order of the enum members! + kNone, + kWith, + kTimes, + kInSequence, + kAfter, + kWillOnce, + kWillRepeatedly, + kRetiresOnSaturation + }; + + typedef std::vector<const void*> UntypedActions; + + // Returns an Expectation object that references and co-owns this + // expectation. + virtual Expectation GetHandle() = 0; + + // Asserts that the EXPECT_CALL() statement has the given property. + void AssertSpecProperty(bool property, + const std::string& failure_message) const { + Assert(property, file_, line_, failure_message); + } + + // Expects that the EXPECT_CALL() statement has the given property. + void ExpectSpecProperty(bool property, + const std::string& failure_message) const { + Expect(property, file_, line_, failure_message); + } + + // Explicitly specifies the cardinality of this expectation. Used + // by the subclasses to implement the .Times() clause. + void SpecifyCardinality(const Cardinality& cardinality); + + // Returns true if and only if the user specified the cardinality + // explicitly using a .Times(). + bool cardinality_specified() const { return cardinality_specified_; } + + // Sets the cardinality of this expectation spec. + void set_cardinality(const Cardinality& a_cardinality) { + cardinality_ = a_cardinality; + } + + // The following group of methods should only be called after the + // EXPECT_CALL() statement, and only when g_gmock_mutex is held by + // the current thread. + + // Retires all pre-requisites of this expectation. + void RetireAllPreRequisites() + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex); + + // Returns true if and only if this expectation is retired. + bool is_retired() const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + return retired_; + } + + // Retires this expectation. + void Retire() + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + retired_ = true; + } + + // Returns true if and only if this expectation is satisfied. + bool IsSatisfied() const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + return cardinality().IsSatisfiedByCallCount(call_count_); + } + + // Returns true if and only if this expectation is saturated. + bool IsSaturated() const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + return cardinality().IsSaturatedByCallCount(call_count_); + } + + // Returns true if and only if this expectation is over-saturated. + bool IsOverSaturated() const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + return cardinality().IsOverSaturatedByCallCount(call_count_); + } + + // Returns true if and only if all pre-requisites of this expectation are + // satisfied. + bool AllPrerequisitesAreSatisfied() const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex); + + // Adds unsatisfied pre-requisites of this expectation to 'result'. + void FindUnsatisfiedPrerequisites(ExpectationSet* result) const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex); + + // Returns the number this expectation has been invoked. + int call_count() const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + return call_count_; + } + + // Increments the number this expectation has been invoked. + void IncrementCallCount() + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + call_count_++; + } + + // Checks the action count (i.e. the number of WillOnce() and + // WillRepeatedly() clauses) against the cardinality if this hasn't + // been done before. Prints a warning if there are too many or too + // few actions. + void CheckActionCountIfNotDone() const + GTEST_LOCK_EXCLUDED_(mutex_); + + friend class ::testing::Sequence; + friend class ::testing::internal::ExpectationTester; + + template <typename Function> + friend class TypedExpectation; + + // Implements the .Times() clause. + void UntypedTimes(const Cardinality& a_cardinality); + + // This group of fields are part of the spec and won't change after + // an EXPECT_CALL() statement finishes. + const char* file_; // The file that contains the expectation. + int line_; // The line number of the expectation. + const std::string source_text_; // The EXPECT_CALL(...) source text. + // True if and only if the cardinality is specified explicitly. + bool cardinality_specified_; + Cardinality cardinality_; // The cardinality of the expectation. + // The immediate pre-requisites (i.e. expectations that must be + // satisfied before this expectation can be matched) of this + // expectation. We use std::shared_ptr in the set because we want an + // Expectation object to be co-owned by its FunctionMocker and its + // successors. This allows multiple mock objects to be deleted at + // different times. + ExpectationSet immediate_prerequisites_; + + // This group of fields are the current state of the expectation, + // and can change as the mock function is called. + int call_count_; // How many times this expectation has been invoked. + bool retired_; // True if and only if this expectation has retired. + UntypedActions untyped_actions_; + bool extra_matcher_specified_; + bool repeated_action_specified_; // True if a WillRepeatedly() was specified. + bool retires_on_saturation_; + Clause last_clause_; + mutable bool action_count_checked_; // Under mutex_. + mutable Mutex mutex_; // Protects action_count_checked_. +}; // class ExpectationBase + +// Impements an expectation for the given function type. +template <typename F> +class TypedExpectation : public ExpectationBase { + public: + typedef typename Function<F>::ArgumentTuple ArgumentTuple; + typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple; + typedef typename Function<F>::Result Result; + + TypedExpectation(FunctionMocker<F>* owner, const char* a_file, int a_line, + const std::string& a_source_text, + const ArgumentMatcherTuple& m) + : ExpectationBase(a_file, a_line, a_source_text), + owner_(owner), + matchers_(m), + // By default, extra_matcher_ should match anything. However, + // we cannot initialize it with _ as that causes ambiguity between + // Matcher's copy and move constructor for some argument types. + extra_matcher_(A<const ArgumentTuple&>()), + repeated_action_(DoDefault()) {} + + ~TypedExpectation() override { + // Check the validity of the action count if it hasn't been done + // yet (for example, if the expectation was never used). + CheckActionCountIfNotDone(); + for (UntypedActions::const_iterator it = untyped_actions_.begin(); + it != untyped_actions_.end(); ++it) { + delete static_cast<const Action<F>*>(*it); + } + } + + // Implements the .With() clause. + TypedExpectation& With(const Matcher<const ArgumentTuple&>& m) { + if (last_clause_ == kWith) { + ExpectSpecProperty(false, + ".With() cannot appear " + "more than once in an EXPECT_CALL()."); + } else { + ExpectSpecProperty(last_clause_ < kWith, + ".With() must be the first " + "clause in an EXPECT_CALL()."); + } + last_clause_ = kWith; + + extra_matcher_ = m; + extra_matcher_specified_ = true; + return *this; + } + + // Implements the .Times() clause. + TypedExpectation& Times(const Cardinality& a_cardinality) { + ExpectationBase::UntypedTimes(a_cardinality); + return *this; + } + + // Implements the .Times() clause. + TypedExpectation& Times(int n) { + return Times(Exactly(n)); + } + + // Implements the .InSequence() clause. + TypedExpectation& InSequence(const Sequence& s) { + ExpectSpecProperty(last_clause_ <= kInSequence, + ".InSequence() cannot appear after .After()," + " .WillOnce(), .WillRepeatedly(), or " + ".RetiresOnSaturation()."); + last_clause_ = kInSequence; + + s.AddExpectation(GetHandle()); + return *this; + } + TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2) { + return InSequence(s1).InSequence(s2); + } + TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2, + const Sequence& s3) { + return InSequence(s1, s2).InSequence(s3); + } + TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2, + const Sequence& s3, const Sequence& s4) { + return InSequence(s1, s2, s3).InSequence(s4); + } + TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2, + const Sequence& s3, const Sequence& s4, + const Sequence& s5) { + return InSequence(s1, s2, s3, s4).InSequence(s5); + } + + // Implements that .After() clause. + TypedExpectation& After(const ExpectationSet& s) { + ExpectSpecProperty(last_clause_ <= kAfter, + ".After() cannot appear after .WillOnce()," + " .WillRepeatedly(), or " + ".RetiresOnSaturation()."); + last_clause_ = kAfter; + + for (ExpectationSet::const_iterator it = s.begin(); it != s.end(); ++it) { + immediate_prerequisites_ += *it; + } + return *this; + } + TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2) { + return After(s1).After(s2); + } + TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2, + const ExpectationSet& s3) { + return After(s1, s2).After(s3); + } + TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2, + const ExpectationSet& s3, const ExpectationSet& s4) { + return After(s1, s2, s3).After(s4); + } + TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2, + const ExpectationSet& s3, const ExpectationSet& s4, + const ExpectationSet& s5) { + return After(s1, s2, s3, s4).After(s5); + } + + // Implements the .WillOnce() clause. + TypedExpectation& WillOnce(const Action<F>& action) { + ExpectSpecProperty(last_clause_ <= kWillOnce, + ".WillOnce() cannot appear after " + ".WillRepeatedly() or .RetiresOnSaturation()."); + last_clause_ = kWillOnce; + + untyped_actions_.push_back(new Action<F>(action)); + if (!cardinality_specified()) { + set_cardinality(Exactly(static_cast<int>(untyped_actions_.size()))); + } + return *this; + } + + // Implements the .WillRepeatedly() clause. + TypedExpectation& WillRepeatedly(const Action<F>& action) { + if (last_clause_ == kWillRepeatedly) { + ExpectSpecProperty(false, + ".WillRepeatedly() cannot appear " + "more than once in an EXPECT_CALL()."); + } else { + ExpectSpecProperty(last_clause_ < kWillRepeatedly, + ".WillRepeatedly() cannot appear " + "after .RetiresOnSaturation()."); + } + last_clause_ = kWillRepeatedly; + repeated_action_specified_ = true; + + repeated_action_ = action; + if (!cardinality_specified()) { + set_cardinality(AtLeast(static_cast<int>(untyped_actions_.size()))); + } + + // Now that no more action clauses can be specified, we check + // whether their count makes sense. + CheckActionCountIfNotDone(); + return *this; + } + + // Implements the .RetiresOnSaturation() clause. + TypedExpectation& RetiresOnSaturation() { + ExpectSpecProperty(last_clause_ < kRetiresOnSaturation, + ".RetiresOnSaturation() cannot appear " + "more than once."); + last_clause_ = kRetiresOnSaturation; + retires_on_saturation_ = true; + + // Now that no more action clauses can be specified, we check + // whether their count makes sense. + CheckActionCountIfNotDone(); + return *this; + } + + // Returns the matchers for the arguments as specified inside the + // EXPECT_CALL() macro. + const ArgumentMatcherTuple& matchers() const { + return matchers_; + } + + // Returns the matcher specified by the .With() clause. + const Matcher<const ArgumentTuple&>& extra_matcher() const { + return extra_matcher_; + } + + // Returns the action specified by the .WillRepeatedly() clause. + const Action<F>& repeated_action() const { return repeated_action_; } + + // If this mock method has an extra matcher (i.e. .With(matcher)), + // describes it to the ostream. + void MaybeDescribeExtraMatcherTo(::std::ostream* os) override { + if (extra_matcher_specified_) { + *os << " Expected args: "; + extra_matcher_.DescribeTo(os); + *os << "\n"; + } + } + + private: + template <typename Function> + friend class FunctionMocker; + + // Returns an Expectation object that references and co-owns this + // expectation. + Expectation GetHandle() override { return owner_->GetHandleOf(this); } + + // The following methods will be called only after the EXPECT_CALL() + // statement finishes and when the current thread holds + // g_gmock_mutex. + + // Returns true if and only if this expectation matches the given arguments. + bool Matches(const ArgumentTuple& args) const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + return TupleMatches(matchers_, args) && extra_matcher_.Matches(args); + } + + // Returns true if and only if this expectation should handle the given + // arguments. + bool ShouldHandleArguments(const ArgumentTuple& args) const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + + // In case the action count wasn't checked when the expectation + // was defined (e.g. if this expectation has no WillRepeatedly() + // or RetiresOnSaturation() clause), we check it when the + // expectation is used for the first time. + CheckActionCountIfNotDone(); + return !is_retired() && AllPrerequisitesAreSatisfied() && Matches(args); + } + + // Describes the result of matching the arguments against this + // expectation to the given ostream. + void ExplainMatchResultTo( + const ArgumentTuple& args, + ::std::ostream* os) const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + + if (is_retired()) { + *os << " Expected: the expectation is active\n" + << " Actual: it is retired\n"; + } else if (!Matches(args)) { + if (!TupleMatches(matchers_, args)) { + ExplainMatchFailureTupleTo(matchers_, args, os); + } + StringMatchResultListener listener; + if (!extra_matcher_.MatchAndExplain(args, &listener)) { + *os << " Expected args: "; + extra_matcher_.DescribeTo(os); + *os << "\n Actual: don't match"; + + internal::PrintIfNotEmpty(listener.str(), os); + *os << "\n"; + } + } else if (!AllPrerequisitesAreSatisfied()) { + *os << " Expected: all pre-requisites are satisfied\n" + << " Actual: the following immediate pre-requisites " + << "are not satisfied:\n"; + ExpectationSet unsatisfied_prereqs; + FindUnsatisfiedPrerequisites(&unsatisfied_prereqs); + int i = 0; + for (ExpectationSet::const_iterator it = unsatisfied_prereqs.begin(); + it != unsatisfied_prereqs.end(); ++it) { + it->expectation_base()->DescribeLocationTo(os); + *os << "pre-requisite #" << i++ << "\n"; + } + *os << " (end of pre-requisites)\n"; + } else { + // This line is here just for completeness' sake. It will never + // be executed as currently the ExplainMatchResultTo() function + // is called only when the mock function call does NOT match the + // expectation. + *os << "The call matches the expectation.\n"; + } + } + + // Returns the action that should be taken for the current invocation. + const Action<F>& GetCurrentAction(const FunctionMocker<F>* mocker, + const ArgumentTuple& args) const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + const int count = call_count(); + Assert(count >= 1, __FILE__, __LINE__, + "call_count() is <= 0 when GetCurrentAction() is " + "called - this should never happen."); + + const int action_count = static_cast<int>(untyped_actions_.size()); + if (action_count > 0 && !repeated_action_specified_ && + count > action_count) { + // If there is at least one WillOnce() and no WillRepeatedly(), + // we warn the user when the WillOnce() clauses ran out. + ::std::stringstream ss; + DescribeLocationTo(&ss); + ss << "Actions ran out in " << source_text() << "...\n" + << "Called " << count << " times, but only " + << action_count << " WillOnce()" + << (action_count == 1 ? " is" : "s are") << " specified - "; + mocker->DescribeDefaultActionTo(args, &ss); + Log(kWarning, ss.str(), 1); + } + + return count <= action_count + ? *static_cast<const Action<F>*>( + untyped_actions_[static_cast<size_t>(count - 1)]) + : repeated_action(); + } + + // Given the arguments of a mock function call, if the call will + // over-saturate this expectation, returns the default action; + // otherwise, returns the next action in this expectation. Also + // describes *what* happened to 'what', and explains *why* Google + // Mock does it to 'why'. This method is not const as it calls + // IncrementCallCount(). A return value of NULL means the default + // action. + const Action<F>* GetActionForArguments(const FunctionMocker<F>* mocker, + const ArgumentTuple& args, + ::std::ostream* what, + ::std::ostream* why) + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + if (IsSaturated()) { + // We have an excessive call. + IncrementCallCount(); + *what << "Mock function called more times than expected - "; + mocker->DescribeDefaultActionTo(args, what); + DescribeCallCountTo(why); + + return nullptr; + } + + IncrementCallCount(); + RetireAllPreRequisites(); + + if (retires_on_saturation_ && IsSaturated()) { + Retire(); + } + + // Must be done after IncrementCount()! + *what << "Mock function call matches " << source_text() <<"...\n"; + return &(GetCurrentAction(mocker, args)); + } + + // All the fields below won't change once the EXPECT_CALL() + // statement finishes. + FunctionMocker<F>* const owner_; + ArgumentMatcherTuple matchers_; + Matcher<const ArgumentTuple&> extra_matcher_; + Action<F> repeated_action_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TypedExpectation); +}; // class TypedExpectation + +// A MockSpec object is used by ON_CALL() or EXPECT_CALL() for +// specifying the default behavior of, or expectation on, a mock +// function. + +// Note: class MockSpec really belongs to the ::testing namespace. +// However if we define it in ::testing, MSVC will complain when +// classes in ::testing::internal declare it as a friend class +// template. To workaround this compiler bug, we define MockSpec in +// ::testing::internal and import it into ::testing. + +// Logs a message including file and line number information. +GTEST_API_ void LogWithLocation(testing::internal::LogSeverity severity, + const char* file, int line, + const std::string& message); + +template <typename F> +class MockSpec { + public: + typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple; + typedef typename internal::Function<F>::ArgumentMatcherTuple + ArgumentMatcherTuple; + + // Constructs a MockSpec object, given the function mocker object + // that the spec is associated with. + MockSpec(internal::FunctionMocker<F>* function_mocker, + const ArgumentMatcherTuple& matchers) + : function_mocker_(function_mocker), matchers_(matchers) {} + + // Adds a new default action spec to the function mocker and returns + // the newly created spec. + internal::OnCallSpec<F>& InternalDefaultActionSetAt( + const char* file, int line, const char* obj, const char* call) { + LogWithLocation(internal::kInfo, file, line, + std::string("ON_CALL(") + obj + ", " + call + ") invoked"); + return function_mocker_->AddNewOnCallSpec(file, line, matchers_); + } + + // Adds a new expectation spec to the function mocker and returns + // the newly created spec. + internal::TypedExpectation<F>& InternalExpectedAt( + const char* file, int line, const char* obj, const char* call) { + const std::string source_text(std::string("EXPECT_CALL(") + obj + ", " + + call + ")"); + LogWithLocation(internal::kInfo, file, line, source_text + " invoked"); + return function_mocker_->AddNewExpectation( + file, line, source_text, matchers_); + } + + // This operator overload is used to swallow the superfluous parameter list + // introduced by the ON/EXPECT_CALL macros. See the macro comments for more + // explanation. + MockSpec<F>& operator()(const internal::WithoutMatchers&, void* const) { + return *this; + } + + private: + template <typename Function> + friend class internal::FunctionMocker; + + // The function mocker that owns this spec. + internal::FunctionMocker<F>* const function_mocker_; + // The argument matchers specified in the spec. + ArgumentMatcherTuple matchers_; +}; // class MockSpec + +// Wrapper type for generically holding an ordinary value or lvalue reference. +// If T is not a reference type, it must be copyable or movable. +// ReferenceOrValueWrapper<T> is movable, and will also be copyable unless +// T is a move-only value type (which means that it will always be copyable +// if the current platform does not support move semantics). +// +// The primary template defines handling for values, but function header +// comments describe the contract for the whole template (including +// specializations). +template <typename T> +class ReferenceOrValueWrapper { + public: + // Constructs a wrapper from the given value/reference. + explicit ReferenceOrValueWrapper(T value) + : value_(std::move(value)) { + } + + // Unwraps and returns the underlying value/reference, exactly as + // originally passed. The behavior of calling this more than once on + // the same object is unspecified. + T Unwrap() { return std::move(value_); } + + // Provides nondestructive access to the underlying value/reference. + // Always returns a const reference (more precisely, + // const std::add_lvalue_reference<T>::type). The behavior of calling this + // after calling Unwrap on the same object is unspecified. + const T& Peek() const { + return value_; + } + + private: + T value_; +}; + +// Specialization for lvalue reference types. See primary template +// for documentation. +template <typename T> +class ReferenceOrValueWrapper<T&> { + public: + // Workaround for debatable pass-by-reference lint warning (c-library-team + // policy precludes NOLINT in this context) + typedef T& reference; + explicit ReferenceOrValueWrapper(reference ref) + : value_ptr_(&ref) {} + T& Unwrap() { return *value_ptr_; } + const T& Peek() const { return *value_ptr_; } + + private: + T* value_ptr_; +}; + +// C++ treats the void type specially. For example, you cannot define +// a void-typed variable or pass a void value to a function. +// ActionResultHolder<T> holds a value of type T, where T must be a +// copyable type or void (T doesn't need to be default-constructable). +// It hides the syntactic difference between void and other types, and +// is used to unify the code for invoking both void-returning and +// non-void-returning mock functions. + +// Untyped base class for ActionResultHolder<T>. +class UntypedActionResultHolderBase { + public: + virtual ~UntypedActionResultHolderBase() {} + + // Prints the held value as an action's result to os. + virtual void PrintAsActionResult(::std::ostream* os) const = 0; +}; + +// This generic definition is used when T is not void. +template <typename T> +class ActionResultHolder : public UntypedActionResultHolderBase { + public: + // Returns the held value. Must not be called more than once. + T Unwrap() { + return result_.Unwrap(); + } + + // Prints the held value as an action's result to os. + void PrintAsActionResult(::std::ostream* os) const override { + *os << "\n Returns: "; + // T may be a reference type, so we don't use UniversalPrint(). + UniversalPrinter<T>::Print(result_.Peek(), os); + } + + // Performs the given mock function's default action and returns the + // result in a new-ed ActionResultHolder. + template <typename F> + static ActionResultHolder* PerformDefaultAction( + const FunctionMocker<F>* func_mocker, + typename Function<F>::ArgumentTuple&& args, + const std::string& call_description) { + return new ActionResultHolder(Wrapper(func_mocker->PerformDefaultAction( + std::move(args), call_description))); + } + + // Performs the given action and returns the result in a new-ed + // ActionResultHolder. + template <typename F> + static ActionResultHolder* PerformAction( + const Action<F>& action, typename Function<F>::ArgumentTuple&& args) { + return new ActionResultHolder( + Wrapper(action.Perform(std::move(args)))); + } + + private: + typedef ReferenceOrValueWrapper<T> Wrapper; + + explicit ActionResultHolder(Wrapper result) + : result_(std::move(result)) { + } + + Wrapper result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionResultHolder); +}; + +// Specialization for T = void. +template <> +class ActionResultHolder<void> : public UntypedActionResultHolderBase { + public: + void Unwrap() { } + + void PrintAsActionResult(::std::ostream* /* os */) const override {} + + // Performs the given mock function's default action and returns ownership + // of an empty ActionResultHolder*. + template <typename F> + static ActionResultHolder* PerformDefaultAction( + const FunctionMocker<F>* func_mocker, + typename Function<F>::ArgumentTuple&& args, + const std::string& call_description) { + func_mocker->PerformDefaultAction(std::move(args), call_description); + return new ActionResultHolder; + } + + // Performs the given action and returns ownership of an empty + // ActionResultHolder*. + template <typename F> + static ActionResultHolder* PerformAction( + const Action<F>& action, typename Function<F>::ArgumentTuple&& args) { + action.Perform(std::move(args)); + return new ActionResultHolder; + } + + private: + ActionResultHolder() {} + GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionResultHolder); +}; + +template <typename F> +class FunctionMocker; + +template <typename R, typename... Args> +class FunctionMocker<R(Args...)> final : public UntypedFunctionMockerBase { + using F = R(Args...); + + public: + using Result = R; + using ArgumentTuple = std::tuple<Args...>; + using ArgumentMatcherTuple = std::tuple<Matcher<Args>...>; + + FunctionMocker() {} + + // There is no generally useful and implementable semantics of + // copying a mock object, so copying a mock is usually a user error. + // Thus we disallow copying function mockers. If the user really + // wants to copy a mock object, they should implement their own copy + // operation, for example: + // + // class MockFoo : public Foo { + // public: + // // Defines a copy constructor explicitly. + // MockFoo(const MockFoo& src) {} + // ... + // }; + FunctionMocker(const FunctionMocker&) = delete; + FunctionMocker& operator=(const FunctionMocker&) = delete; + + // The destructor verifies that all expectations on this mock + // function have been satisfied. If not, it will report Google Test + // non-fatal failures for the violations. + ~FunctionMocker() override GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + MutexLock l(&g_gmock_mutex); + VerifyAndClearExpectationsLocked(); + Mock::UnregisterLocked(this); + ClearDefaultActionsLocked(); + } + + // Returns the ON_CALL spec that matches this mock function with the + // given arguments; returns NULL if no matching ON_CALL is found. + // L = * + const OnCallSpec<F>* FindOnCallSpec( + const ArgumentTuple& args) const { + for (UntypedOnCallSpecs::const_reverse_iterator it + = untyped_on_call_specs_.rbegin(); + it != untyped_on_call_specs_.rend(); ++it) { + const OnCallSpec<F>* spec = static_cast<const OnCallSpec<F>*>(*it); + if (spec->Matches(args)) + return spec; + } + + return nullptr; + } + + // Performs the default action of this mock function on the given + // arguments and returns the result. Asserts (or throws if + // exceptions are enabled) with a helpful call descrption if there + // is no valid return value. This method doesn't depend on the + // mutable state of this object, and thus can be called concurrently + // without locking. + // L = * + Result PerformDefaultAction(ArgumentTuple&& args, + const std::string& call_description) const { + const OnCallSpec<F>* const spec = + this->FindOnCallSpec(args); + if (spec != nullptr) { + return spec->GetAction().Perform(std::move(args)); + } + const std::string message = + call_description + + "\n The mock function has no default action " + "set, and its return type has no default value set."; +#if GTEST_HAS_EXCEPTIONS + if (!DefaultValue<Result>::Exists()) { + throw std::runtime_error(message); + } +#else + Assert(DefaultValue<Result>::Exists(), "", -1, message); +#endif + return DefaultValue<Result>::Get(); + } + + // Performs the default action with the given arguments and returns + // the action's result. The call description string will be used in + // the error message to describe the call in the case the default + // action fails. The caller is responsible for deleting the result. + // L = * + UntypedActionResultHolderBase* UntypedPerformDefaultAction( + void* untyped_args, // must point to an ArgumentTuple + const std::string& call_description) const override { + ArgumentTuple* args = static_cast<ArgumentTuple*>(untyped_args); + return ResultHolder::PerformDefaultAction(this, std::move(*args), + call_description); + } + + // Performs the given action with the given arguments and returns + // the action's result. The caller is responsible for deleting the + // result. + // L = * + UntypedActionResultHolderBase* UntypedPerformAction( + const void* untyped_action, void* untyped_args) const override { + // Make a copy of the action before performing it, in case the + // action deletes the mock object (and thus deletes itself). + const Action<F> action = *static_cast<const Action<F>*>(untyped_action); + ArgumentTuple* args = static_cast<ArgumentTuple*>(untyped_args); + return ResultHolder::PerformAction(action, std::move(*args)); + } + + // Implements UntypedFunctionMockerBase::ClearDefaultActionsLocked(): + // clears the ON_CALL()s set on this mock function. + void ClearDefaultActionsLocked() override + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + + // Deleting our default actions may trigger other mock objects to be + // deleted, for example if an action contains a reference counted smart + // pointer to that mock object, and that is the last reference. So if we + // delete our actions within the context of the global mutex we may deadlock + // when this method is called again. Instead, make a copy of the set of + // actions to delete, clear our set within the mutex, and then delete the + // actions outside of the mutex. + UntypedOnCallSpecs specs_to_delete; + untyped_on_call_specs_.swap(specs_to_delete); + + g_gmock_mutex.Unlock(); + for (UntypedOnCallSpecs::const_iterator it = + specs_to_delete.begin(); + it != specs_to_delete.end(); ++it) { + delete static_cast<const OnCallSpec<F>*>(*it); + } + + // Lock the mutex again, since the caller expects it to be locked when we + // return. + g_gmock_mutex.Lock(); + } + + // Returns the result of invoking this mock function with the given + // arguments. This function can be safely called from multiple + // threads concurrently. + Result Invoke(Args... args) GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + ArgumentTuple tuple(std::forward<Args>(args)...); + std::unique_ptr<ResultHolder> holder(DownCast_<ResultHolder*>( + this->UntypedInvokeWith(static_cast<void*>(&tuple)))); + return holder->Unwrap(); + } + + MockSpec<F> With(Matcher<Args>... m) { + return MockSpec<F>(this, ::std::make_tuple(std::move(m)...)); + } + + protected: + template <typename Function> + friend class MockSpec; + + typedef ActionResultHolder<Result> ResultHolder; + + // Adds and returns a default action spec for this mock function. + OnCallSpec<F>& AddNewOnCallSpec( + const char* file, int line, + const ArgumentMatcherTuple& m) + GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line); + OnCallSpec<F>* const on_call_spec = new OnCallSpec<F>(file, line, m); + untyped_on_call_specs_.push_back(on_call_spec); + return *on_call_spec; + } + + // Adds and returns an expectation spec for this mock function. + TypedExpectation<F>& AddNewExpectation(const char* file, int line, + const std::string& source_text, + const ArgumentMatcherTuple& m) + GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line); + TypedExpectation<F>* const expectation = + new TypedExpectation<F>(this, file, line, source_text, m); + const std::shared_ptr<ExpectationBase> untyped_expectation(expectation); + // See the definition of untyped_expectations_ for why access to + // it is unprotected here. + untyped_expectations_.push_back(untyped_expectation); + + // Adds this expectation into the implicit sequence if there is one. + Sequence* const implicit_sequence = g_gmock_implicit_sequence.get(); + if (implicit_sequence != nullptr) { + implicit_sequence->AddExpectation(Expectation(untyped_expectation)); + } + + return *expectation; + } + + private: + template <typename Func> friend class TypedExpectation; + + // Some utilities needed for implementing UntypedInvokeWith(). + + // Describes what default action will be performed for the given + // arguments. + // L = * + void DescribeDefaultActionTo(const ArgumentTuple& args, + ::std::ostream* os) const { + const OnCallSpec<F>* const spec = FindOnCallSpec(args); + + if (spec == nullptr) { + *os << (std::is_void<Result>::value ? "returning directly.\n" + : "returning default value.\n"); + } else { + *os << "taking default action specified at:\n" + << FormatFileLocation(spec->file(), spec->line()) << "\n"; + } + } + + // Writes a message that the call is uninteresting (i.e. neither + // explicitly expected nor explicitly unexpected) to the given + // ostream. + void UntypedDescribeUninterestingCall(const void* untyped_args, + ::std::ostream* os) const override + GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + const ArgumentTuple& args = + *static_cast<const ArgumentTuple*>(untyped_args); + *os << "Uninteresting mock function call - "; + DescribeDefaultActionTo(args, os); + *os << " Function call: " << Name(); + UniversalPrint(args, os); + } + + // Returns the expectation that matches the given function arguments + // (or NULL is there's no match); when a match is found, + // untyped_action is set to point to the action that should be + // performed (or NULL if the action is "do default"), and + // is_excessive is modified to indicate whether the call exceeds the + // expected number. + // + // Critical section: We must find the matching expectation and the + // corresponding action that needs to be taken in an ATOMIC + // transaction. Otherwise another thread may call this mock + // method in the middle and mess up the state. + // + // However, performing the action has to be left out of the critical + // section. The reason is that we have no control on what the + // action does (it can invoke an arbitrary user function or even a + // mock function) and excessive locking could cause a dead lock. + const ExpectationBase* UntypedFindMatchingExpectation( + const void* untyped_args, const void** untyped_action, bool* is_excessive, + ::std::ostream* what, ::std::ostream* why) override + GTEST_LOCK_EXCLUDED_(g_gmock_mutex) { + const ArgumentTuple& args = + *static_cast<const ArgumentTuple*>(untyped_args); + MutexLock l(&g_gmock_mutex); + TypedExpectation<F>* exp = this->FindMatchingExpectationLocked(args); + if (exp == nullptr) { // A match wasn't found. + this->FormatUnexpectedCallMessageLocked(args, what, why); + return nullptr; + } + + // This line must be done before calling GetActionForArguments(), + // which will increment the call count for *exp and thus affect + // its saturation status. + *is_excessive = exp->IsSaturated(); + const Action<F>* action = exp->GetActionForArguments(this, args, what, why); + if (action != nullptr && action->IsDoDefault()) + action = nullptr; // Normalize "do default" to NULL. + *untyped_action = action; + return exp; + } + + // Prints the given function arguments to the ostream. + void UntypedPrintArgs(const void* untyped_args, + ::std::ostream* os) const override { + const ArgumentTuple& args = + *static_cast<const ArgumentTuple*>(untyped_args); + UniversalPrint(args, os); + } + + // Returns the expectation that matches the arguments, or NULL if no + // expectation matches them. + TypedExpectation<F>* FindMatchingExpectationLocked( + const ArgumentTuple& args) const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + // See the definition of untyped_expectations_ for why access to + // it is unprotected here. + for (typename UntypedExpectations::const_reverse_iterator it = + untyped_expectations_.rbegin(); + it != untyped_expectations_.rend(); ++it) { + TypedExpectation<F>* const exp = + static_cast<TypedExpectation<F>*>(it->get()); + if (exp->ShouldHandleArguments(args)) { + return exp; + } + } + return nullptr; + } + + // Returns a message that the arguments don't match any expectation. + void FormatUnexpectedCallMessageLocked( + const ArgumentTuple& args, + ::std::ostream* os, + ::std::ostream* why) const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + *os << "\nUnexpected mock function call - "; + DescribeDefaultActionTo(args, os); + PrintTriedExpectationsLocked(args, why); + } + + // Prints a list of expectations that have been tried against the + // current mock function call. + void PrintTriedExpectationsLocked( + const ArgumentTuple& args, + ::std::ostream* why) const + GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) { + g_gmock_mutex.AssertHeld(); + const size_t count = untyped_expectations_.size(); + *why << "Google Mock tried the following " << count << " " + << (count == 1 ? "expectation, but it didn't match" : + "expectations, but none matched") + << ":\n"; + for (size_t i = 0; i < count; i++) { + TypedExpectation<F>* const expectation = + static_cast<TypedExpectation<F>*>(untyped_expectations_[i].get()); + *why << "\n"; + expectation->DescribeLocationTo(why); + if (count > 1) { + *why << "tried expectation #" << i << ": "; + } + *why << expectation->source_text() << "...\n"; + expectation->ExplainMatchResultTo(args, why); + expectation->DescribeCallCountTo(why); + } + } +}; // class FunctionMocker + +// Reports an uninteresting call (whose description is in msg) in the +// manner specified by 'reaction'. +void ReportUninterestingCall(CallReaction reaction, const std::string& msg); + +} // namespace internal + +namespace internal { + +template <typename F> +class MockFunction; + +template <typename R, typename... Args> +class MockFunction<R(Args...)> { + public: + MockFunction(const MockFunction&) = delete; + MockFunction& operator=(const MockFunction&) = delete; + + std::function<R(Args...)> AsStdFunction() { + return [this](Args... args) -> R { + return this->Call(std::forward<Args>(args)...); + }; + } + + // Implementation detail: the expansion of the MOCK_METHOD macro. + R Call(Args... args) { + mock_.SetOwnerAndName(this, "Call"); + return mock_.Invoke(std::forward<Args>(args)...); + } + + MockSpec<R(Args...)> gmock_Call(Matcher<Args>... m) { + mock_.RegisterOwner(this); + return mock_.With(std::move(m)...); + } + + MockSpec<R(Args...)> gmock_Call(const WithoutMatchers&, R (*)(Args...)) { + return this->gmock_Call(::testing::A<Args>()...); + } + + protected: + MockFunction() = default; + ~MockFunction() = default; + + private: + FunctionMocker<R(Args...)> mock_; +}; + +/* +The SignatureOf<F> struct is a meta-function returning function signature +corresponding to the provided F argument. + +It makes use of MockFunction easier by allowing it to accept more F arguments +than just function signatures. + +Specializations provided here cover only a signature type itself and +std::function. However, if need be it can be easily extended to cover also other +types (like for example boost::function). +*/ + +template <typename F> +struct SignatureOf; + +template <typename R, typename... Args> +struct SignatureOf<R(Args...)> { + using type = R(Args...); +}; + +template <typename F> +struct SignatureOf<std::function<F>> : SignatureOf<F> {}; + +template <typename F> +using SignatureOfT = typename SignatureOf<F>::type; + +} // namespace internal + +// A MockFunction<F> type has one mock method whose type is +// internal::SignatureOfT<F>. It is useful when you just want your +// test code to emit some messages and have Google Mock verify the +// right messages are sent (and perhaps at the right times). For +// example, if you are exercising code: +// +// Foo(1); +// Foo(2); +// Foo(3); +// +// and want to verify that Foo(1) and Foo(3) both invoke +// mock.Bar("a"), but Foo(2) doesn't invoke anything, you can write: +// +// TEST(FooTest, InvokesBarCorrectly) { +// MyMock mock; +// MockFunction<void(string check_point_name)> check; +// { +// InSequence s; +// +// EXPECT_CALL(mock, Bar("a")); +// EXPECT_CALL(check, Call("1")); +// EXPECT_CALL(check, Call("2")); +// EXPECT_CALL(mock, Bar("a")); +// } +// Foo(1); +// check.Call("1"); +// Foo(2); +// check.Call("2"); +// Foo(3); +// } +// +// The expectation spec says that the first Bar("a") must happen +// before check point "1", the second Bar("a") must happen after check +// point "2", and nothing should happen between the two check +// points. The explicit check points make it easy to tell which +// Bar("a") is called by which call to Foo(). +// +// MockFunction<F> can also be used to exercise code that accepts +// std::function<internal::SignatureOfT<F>> callbacks. To do so, use +// AsStdFunction() method to create std::function proxy forwarding to +// original object's Call. Example: +// +// TEST(FooTest, RunsCallbackWithBarArgument) { +// MockFunction<int(string)> callback; +// EXPECT_CALL(callback, Call("bar")).WillOnce(Return(1)); +// Foo(callback.AsStdFunction()); +// } +// +// The internal::SignatureOfT<F> indirection allows to use other types +// than just function signature type. This is typically useful when +// providing a mock for a predefined std::function type. Example: +// +// using FilterPredicate = std::function<bool(string)>; +// void MyFilterAlgorithm(FilterPredicate predicate); +// +// TEST(FooTest, FilterPredicateAlwaysAccepts) { +// MockFunction<FilterPredicate> predicateMock; +// EXPECT_CALL(predicateMock, Call(_)).WillRepeatedly(Return(true)); +// MyFilterAlgorithm(predicateMock.AsStdFunction()); +// } +template <typename F> +class MockFunction : public internal::MockFunction<internal::SignatureOfT<F>> { + using Base = internal::MockFunction<internal::SignatureOfT<F>>; + + public: + using Base::Base; +}; + +// The style guide prohibits "using" statements in a namespace scope +// inside a header file. However, the MockSpec class template is +// meant to be defined in the ::testing namespace. The following line +// is just a trick for working around a bug in MSVC 8.0, which cannot +// handle it if we define MockSpec in ::testing. +using internal::MockSpec; + +// Const(x) is a convenient function for obtaining a const reference +// to x. This is useful for setting expectations on an overloaded +// const mock method, e.g. +// +// class MockFoo : public FooInterface { +// public: +// MOCK_METHOD0(Bar, int()); +// MOCK_CONST_METHOD0(Bar, int&()); +// }; +// +// MockFoo foo; +// // Expects a call to non-const MockFoo::Bar(). +// EXPECT_CALL(foo, Bar()); +// // Expects a call to const MockFoo::Bar(). +// EXPECT_CALL(Const(foo), Bar()); +template <typename T> +inline const T& Const(const T& x) { return x; } + +// Constructs an Expectation object that references and co-owns exp. +inline Expectation::Expectation(internal::ExpectationBase& exp) // NOLINT + : expectation_base_(exp.GetHandle().expectation_base()) {} + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// Implementation for ON_CALL and EXPECT_CALL macros. A separate macro is +// required to avoid compile errors when the name of the method used in call is +// a result of macro expansion. See CompilesWithMethodNameExpandedFromMacro +// tests in internal/gmock-spec-builders_test.cc for more details. +// +// This macro supports statements both with and without parameter matchers. If +// the parameter list is omitted, gMock will accept any parameters, which allows +// tests to be written that don't need to encode the number of method +// parameter. This technique may only be used for non-overloaded methods. +// +// // These are the same: +// ON_CALL(mock, NoArgsMethod()).WillByDefault(...); +// ON_CALL(mock, NoArgsMethod).WillByDefault(...); +// +// // As are these: +// ON_CALL(mock, TwoArgsMethod(_, _)).WillByDefault(...); +// ON_CALL(mock, TwoArgsMethod).WillByDefault(...); +// +// // Can also specify args if you want, of course: +// ON_CALL(mock, TwoArgsMethod(_, 45)).WillByDefault(...); +// +// // Overloads work as long as you specify parameters: +// ON_CALL(mock, OverloadedMethod(_)).WillByDefault(...); +// ON_CALL(mock, OverloadedMethod(_, _)).WillByDefault(...); +// +// // Oops! Which overload did you want? +// ON_CALL(mock, OverloadedMethod).WillByDefault(...); +// => ERROR: call to member function 'gmock_OverloadedMethod' is ambiguous +// +// How this works: The mock class uses two overloads of the gmock_Method +// expectation setter method plus an operator() overload on the MockSpec object. +// In the matcher list form, the macro expands to: +// +// // This statement: +// ON_CALL(mock, TwoArgsMethod(_, 45))... +// +// // ...expands to: +// mock.gmock_TwoArgsMethod(_, 45)(WithoutMatchers(), nullptr)... +// |-------------v---------------||------------v-------------| +// invokes first overload swallowed by operator() +// +// // ...which is essentially: +// mock.gmock_TwoArgsMethod(_, 45)... +// +// Whereas the form without a matcher list: +// +// // This statement: +// ON_CALL(mock, TwoArgsMethod)... +// +// // ...expands to: +// mock.gmock_TwoArgsMethod(WithoutMatchers(), nullptr)... +// |-----------------------v--------------------------| +// invokes second overload +// +// // ...which is essentially: +// mock.gmock_TwoArgsMethod(_, _)... +// +// The WithoutMatchers() argument is used to disambiguate overloads and to +// block the caller from accidentally invoking the second overload directly. The +// second argument is an internal type derived from the method signature. The +// failure to disambiguate two overloads of this method in the ON_CALL statement +// is how we block callers from setting expectations on overloaded methods. +#define GMOCK_ON_CALL_IMPL_(mock_expr, Setter, call) \ + ((mock_expr).gmock_##call)(::testing::internal::GetWithoutMatchers(), \ + nullptr) \ + .Setter(__FILE__, __LINE__, #mock_expr, #call) + +#define ON_CALL(obj, call) \ + GMOCK_ON_CALL_IMPL_(obj, InternalDefaultActionSetAt, call) + +#define EXPECT_CALL(obj, call) \ + GMOCK_ON_CALL_IMPL_(obj, InternalExpectedAt, call) + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_ + +namespace testing { +namespace internal { +template <typename T> +using identity_t = T; + +template <typename Pattern> +struct ThisRefAdjuster { + template <typename T> + using AdjustT = typename std::conditional< + std::is_const<typename std::remove_reference<Pattern>::type>::value, + typename std::conditional<std::is_lvalue_reference<Pattern>::value, + const T&, const T&&>::type, + typename std::conditional<std::is_lvalue_reference<Pattern>::value, T&, + T&&>::type>::type; + + template <typename MockType> + static AdjustT<MockType> Adjust(const MockType& mock) { + return static_cast<AdjustT<MockType>>(const_cast<MockType&>(mock)); + } +}; + +} // namespace internal + +// The style guide prohibits "using" statements in a namespace scope +// inside a header file. However, the FunctionMocker class template +// is meant to be defined in the ::testing namespace. The following +// line is just a trick for working around a bug in MSVC 8.0, which +// cannot handle it if we define FunctionMocker in ::testing. +using internal::FunctionMocker; +} // namespace testing + +#define MOCK_METHOD(...) \ + GMOCK_PP_VARIADIC_CALL(GMOCK_INTERNAL_MOCK_METHOD_ARG_, __VA_ARGS__) + +#define GMOCK_INTERNAL_MOCK_METHOD_ARG_1(...) \ + GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) + +#define GMOCK_INTERNAL_MOCK_METHOD_ARG_2(...) \ + GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) + +#define GMOCK_INTERNAL_MOCK_METHOD_ARG_3(_Ret, _MethodName, _Args) \ + GMOCK_INTERNAL_MOCK_METHOD_ARG_4(_Ret, _MethodName, _Args, ()) + +#define GMOCK_INTERNAL_MOCK_METHOD_ARG_4(_Ret, _MethodName, _Args, _Spec) \ + GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Args); \ + GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Spec); \ + GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE( \ + GMOCK_PP_NARG0 _Args, GMOCK_INTERNAL_SIGNATURE(_Ret, _Args)); \ + GMOCK_INTERNAL_ASSERT_VALID_SPEC(_Spec) \ + GMOCK_INTERNAL_MOCK_METHOD_IMPL( \ + GMOCK_PP_NARG0 _Args, _MethodName, GMOCK_INTERNAL_HAS_CONST(_Spec), \ + GMOCK_INTERNAL_HAS_OVERRIDE(_Spec), GMOCK_INTERNAL_HAS_FINAL(_Spec), \ + GMOCK_INTERNAL_GET_NOEXCEPT_SPEC(_Spec), \ + GMOCK_INTERNAL_GET_CALLTYPE(_Spec), GMOCK_INTERNAL_GET_REF_SPEC(_Spec), \ + (GMOCK_INTERNAL_SIGNATURE(_Ret, _Args))) + +#define GMOCK_INTERNAL_MOCK_METHOD_ARG_5(...) \ + GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) + +#define GMOCK_INTERNAL_MOCK_METHOD_ARG_6(...) \ + GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) + +#define GMOCK_INTERNAL_MOCK_METHOD_ARG_7(...) \ + GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) + +#define GMOCK_INTERNAL_WRONG_ARITY(...) \ + static_assert( \ + false, \ + "MOCK_METHOD must be called with 3 or 4 arguments. _Ret, " \ + "_MethodName, _Args and optionally _Spec. _Args and _Spec must be " \ + "enclosed in parentheses. If _Ret is a type with unprotected commas, " \ + "it must also be enclosed in parentheses.") + +#define GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Tuple) \ + static_assert( \ + GMOCK_PP_IS_ENCLOSED_PARENS(_Tuple), \ + GMOCK_PP_STRINGIZE(_Tuple) " should be enclosed in parentheses.") + +#define GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE(_N, ...) \ + static_assert( \ + std::is_function<__VA_ARGS__>::value, \ + "Signature must be a function type, maybe return type contains " \ + "unprotected comma."); \ + static_assert( \ + ::testing::tuple_size<typename ::testing::internal::Function< \ + __VA_ARGS__>::ArgumentTuple>::value == _N, \ + "This method does not take " GMOCK_PP_STRINGIZE( \ + _N) " arguments. Parenthesize all types with unprotected commas.") + +#define GMOCK_INTERNAL_ASSERT_VALID_SPEC(_Spec) \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_ASSERT_VALID_SPEC_ELEMENT, ~, _Spec) + +#define GMOCK_INTERNAL_MOCK_METHOD_IMPL(_N, _MethodName, _Constness, \ + _Override, _Final, _NoexceptSpec, \ + _CallType, _RefSpec, _Signature) \ + typename ::testing::internal::Function<GMOCK_PP_REMOVE_PARENS( \ + _Signature)>::Result \ + GMOCK_INTERNAL_EXPAND(_CallType) \ + _MethodName(GMOCK_PP_REPEAT(GMOCK_INTERNAL_PARAMETER, _Signature, _N)) \ + GMOCK_PP_IF(_Constness, const, ) _RefSpec _NoexceptSpec \ + GMOCK_PP_IF(_Override, override, ) GMOCK_PP_IF(_Final, final, ) { \ + GMOCK_MOCKER_(_N, _Constness, _MethodName) \ + .SetOwnerAndName(this, #_MethodName); \ + return GMOCK_MOCKER_(_N, _Constness, _MethodName) \ + .Invoke(GMOCK_PP_REPEAT(GMOCK_INTERNAL_FORWARD_ARG, _Signature, _N)); \ + } \ + ::testing::MockSpec<GMOCK_PP_REMOVE_PARENS(_Signature)> gmock_##_MethodName( \ + GMOCK_PP_REPEAT(GMOCK_INTERNAL_MATCHER_PARAMETER, _Signature, _N)) \ + GMOCK_PP_IF(_Constness, const, ) _RefSpec { \ + GMOCK_MOCKER_(_N, _Constness, _MethodName).RegisterOwner(this); \ + return GMOCK_MOCKER_(_N, _Constness, _MethodName) \ + .With(GMOCK_PP_REPEAT(GMOCK_INTERNAL_MATCHER_ARGUMENT, , _N)); \ + } \ + ::testing::MockSpec<GMOCK_PP_REMOVE_PARENS(_Signature)> gmock_##_MethodName( \ + const ::testing::internal::WithoutMatchers&, \ + GMOCK_PP_IF(_Constness, const, )::testing::internal::Function< \ + GMOCK_PP_REMOVE_PARENS(_Signature)>*) const _RefSpec _NoexceptSpec { \ + return ::testing::internal::ThisRefAdjuster<GMOCK_PP_IF( \ + _Constness, const, ) int _RefSpec>::Adjust(*this) \ + .gmock_##_MethodName(GMOCK_PP_REPEAT( \ + GMOCK_INTERNAL_A_MATCHER_ARGUMENT, _Signature, _N)); \ + } \ + mutable ::testing::FunctionMocker<GMOCK_PP_REMOVE_PARENS(_Signature)> \ + GMOCK_MOCKER_(_N, _Constness, _MethodName) + +#define GMOCK_INTERNAL_EXPAND(...) __VA_ARGS__ + +// Five Valid modifiers. +#define GMOCK_INTERNAL_HAS_CONST(_Tuple) \ + GMOCK_PP_HAS_COMMA(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_CONST, ~, _Tuple)) + +#define GMOCK_INTERNAL_HAS_OVERRIDE(_Tuple) \ + GMOCK_PP_HAS_COMMA( \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_OVERRIDE, ~, _Tuple)) + +#define GMOCK_INTERNAL_HAS_FINAL(_Tuple) \ + GMOCK_PP_HAS_COMMA(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_FINAL, ~, _Tuple)) + +#define GMOCK_INTERNAL_GET_NOEXCEPT_SPEC(_Tuple) \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_NOEXCEPT_SPEC_IF_NOEXCEPT, ~, _Tuple) + +#define GMOCK_INTERNAL_NOEXCEPT_SPEC_IF_NOEXCEPT(_i, _, _elem) \ + GMOCK_PP_IF( \ + GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem)), \ + _elem, ) + +#define GMOCK_INTERNAL_GET_REF_SPEC(_Tuple) \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_REF_SPEC_IF_REF, ~, _Tuple) + +#define GMOCK_INTERNAL_REF_SPEC_IF_REF(_i, _, _elem) \ + GMOCK_PP_IF(GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_REF(_i, _, _elem)), \ + GMOCK_PP_CAT(GMOCK_INTERNAL_UNPACK_, _elem), ) + +#define GMOCK_INTERNAL_GET_CALLTYPE(_Tuple) \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GET_CALLTYPE_IMPL, ~, _Tuple) + +#define GMOCK_INTERNAL_ASSERT_VALID_SPEC_ELEMENT(_i, _, _elem) \ + static_assert( \ + (GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_CONST(_i, _, _elem)) + \ + GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_OVERRIDE(_i, _, _elem)) + \ + GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_FINAL(_i, _, _elem)) + \ + GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem)) + \ + GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_REF(_i, _, _elem)) + \ + GMOCK_INTERNAL_IS_CALLTYPE(_elem)) == 1, \ + GMOCK_PP_STRINGIZE( \ + _elem) " cannot be recognized as a valid specification modifier."); + +// Modifiers implementation. +#define GMOCK_INTERNAL_DETECT_CONST(_i, _, _elem) \ + GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_CONST_I_, _elem) + +#define GMOCK_INTERNAL_DETECT_CONST_I_const , + +#define GMOCK_INTERNAL_DETECT_OVERRIDE(_i, _, _elem) \ + GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_OVERRIDE_I_, _elem) + +#define GMOCK_INTERNAL_DETECT_OVERRIDE_I_override , + +#define GMOCK_INTERNAL_DETECT_FINAL(_i, _, _elem) \ + GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_FINAL_I_, _elem) + +#define GMOCK_INTERNAL_DETECT_FINAL_I_final , + +#define GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem) \ + GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_NOEXCEPT_I_, _elem) + +#define GMOCK_INTERNAL_DETECT_NOEXCEPT_I_noexcept , + +#define GMOCK_INTERNAL_DETECT_REF(_i, _, _elem) \ + GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_REF_I_, _elem) + +#define GMOCK_INTERNAL_DETECT_REF_I_ref , + +#define GMOCK_INTERNAL_UNPACK_ref(x) x + +#define GMOCK_INTERNAL_GET_CALLTYPE_IMPL(_i, _, _elem) \ + GMOCK_PP_IF(GMOCK_INTERNAL_IS_CALLTYPE(_elem), \ + GMOCK_INTERNAL_GET_VALUE_CALLTYPE, GMOCK_PP_EMPTY) \ + (_elem) + +// TODO(iserna): GMOCK_INTERNAL_IS_CALLTYPE and +// GMOCK_INTERNAL_GET_VALUE_CALLTYPE needed more expansions to work on windows +// maybe they can be simplified somehow. +#define GMOCK_INTERNAL_IS_CALLTYPE(_arg) \ + GMOCK_INTERNAL_IS_CALLTYPE_I( \ + GMOCK_PP_CAT(GMOCK_INTERNAL_IS_CALLTYPE_HELPER_, _arg)) +#define GMOCK_INTERNAL_IS_CALLTYPE_I(_arg) GMOCK_PP_IS_ENCLOSED_PARENS(_arg) + +#define GMOCK_INTERNAL_GET_VALUE_CALLTYPE(_arg) \ + GMOCK_INTERNAL_GET_VALUE_CALLTYPE_I( \ + GMOCK_PP_CAT(GMOCK_INTERNAL_IS_CALLTYPE_HELPER_, _arg)) +#define GMOCK_INTERNAL_GET_VALUE_CALLTYPE_I(_arg) \ + GMOCK_PP_IDENTITY _arg + +#define GMOCK_INTERNAL_IS_CALLTYPE_HELPER_Calltype + +// Note: The use of `identity_t` here allows _Ret to represent return types that +// would normally need to be specified in a different way. For example, a method +// returning a function pointer must be written as +// +// fn_ptr_return_t (*method(method_args_t...))(fn_ptr_args_t...) +// +// But we only support placing the return type at the beginning. To handle this, +// we wrap all calls in identity_t, so that a declaration will be expanded to +// +// identity_t<fn_ptr_return_t (*)(fn_ptr_args_t...)> method(method_args_t...) +// +// This allows us to work around the syntactic oddities of function/method +// types. +#define GMOCK_INTERNAL_SIGNATURE(_Ret, _Args) \ + ::testing::internal::identity_t<GMOCK_PP_IF(GMOCK_PP_IS_BEGIN_PARENS(_Ret), \ + GMOCK_PP_REMOVE_PARENS, \ + GMOCK_PP_IDENTITY)(_Ret)>( \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GET_TYPE, _, _Args)) + +#define GMOCK_INTERNAL_GET_TYPE(_i, _, _elem) \ + GMOCK_PP_COMMA_IF(_i) \ + GMOCK_PP_IF(GMOCK_PP_IS_BEGIN_PARENS(_elem), GMOCK_PP_REMOVE_PARENS, \ + GMOCK_PP_IDENTITY) \ + (_elem) + +#define GMOCK_INTERNAL_PARAMETER(_i, _Signature, _) \ + GMOCK_PP_COMMA_IF(_i) \ + GMOCK_INTERNAL_ARG_O(_i, GMOCK_PP_REMOVE_PARENS(_Signature)) \ + gmock_a##_i + +#define GMOCK_INTERNAL_FORWARD_ARG(_i, _Signature, _) \ + GMOCK_PP_COMMA_IF(_i) \ + ::std::forward<GMOCK_INTERNAL_ARG_O( \ + _i, GMOCK_PP_REMOVE_PARENS(_Signature))>(gmock_a##_i) + +#define GMOCK_INTERNAL_MATCHER_PARAMETER(_i, _Signature, _) \ + GMOCK_PP_COMMA_IF(_i) \ + GMOCK_INTERNAL_MATCHER_O(_i, GMOCK_PP_REMOVE_PARENS(_Signature)) \ + gmock_a##_i + +#define GMOCK_INTERNAL_MATCHER_ARGUMENT(_i, _1, _2) \ + GMOCK_PP_COMMA_IF(_i) \ + gmock_a##_i + +#define GMOCK_INTERNAL_A_MATCHER_ARGUMENT(_i, _Signature, _) \ + GMOCK_PP_COMMA_IF(_i) \ + ::testing::A<GMOCK_INTERNAL_ARG_O(_i, GMOCK_PP_REMOVE_PARENS(_Signature))>() + +#define GMOCK_INTERNAL_ARG_O(_i, ...) \ + typename ::testing::internal::Function<__VA_ARGS__>::template Arg<_i>::type + +#define GMOCK_INTERNAL_MATCHER_O(_i, ...) \ + const ::testing::Matcher<typename ::testing::internal::Function< \ + __VA_ARGS__>::template Arg<_i>::type>& + +#define MOCK_METHOD0(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 0, __VA_ARGS__) +#define MOCK_METHOD1(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 1, __VA_ARGS__) +#define MOCK_METHOD2(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 2, __VA_ARGS__) +#define MOCK_METHOD3(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 3, __VA_ARGS__) +#define MOCK_METHOD4(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 4, __VA_ARGS__) +#define MOCK_METHOD5(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 5, __VA_ARGS__) +#define MOCK_METHOD6(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 6, __VA_ARGS__) +#define MOCK_METHOD7(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 7, __VA_ARGS__) +#define MOCK_METHOD8(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 8, __VA_ARGS__) +#define MOCK_METHOD9(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 9, __VA_ARGS__) +#define MOCK_METHOD10(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, , m, 10, __VA_ARGS__) + +#define MOCK_CONST_METHOD0(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 0, __VA_ARGS__) +#define MOCK_CONST_METHOD1(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 1, __VA_ARGS__) +#define MOCK_CONST_METHOD2(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 2, __VA_ARGS__) +#define MOCK_CONST_METHOD3(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 3, __VA_ARGS__) +#define MOCK_CONST_METHOD4(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 4, __VA_ARGS__) +#define MOCK_CONST_METHOD5(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 5, __VA_ARGS__) +#define MOCK_CONST_METHOD6(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 6, __VA_ARGS__) +#define MOCK_CONST_METHOD7(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 7, __VA_ARGS__) +#define MOCK_CONST_METHOD8(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 8, __VA_ARGS__) +#define MOCK_CONST_METHOD9(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 9, __VA_ARGS__) +#define MOCK_CONST_METHOD10(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 10, __VA_ARGS__) + +#define MOCK_METHOD0_T(m, ...) MOCK_METHOD0(m, __VA_ARGS__) +#define MOCK_METHOD1_T(m, ...) MOCK_METHOD1(m, __VA_ARGS__) +#define MOCK_METHOD2_T(m, ...) MOCK_METHOD2(m, __VA_ARGS__) +#define MOCK_METHOD3_T(m, ...) MOCK_METHOD3(m, __VA_ARGS__) +#define MOCK_METHOD4_T(m, ...) MOCK_METHOD4(m, __VA_ARGS__) +#define MOCK_METHOD5_T(m, ...) MOCK_METHOD5(m, __VA_ARGS__) +#define MOCK_METHOD6_T(m, ...) MOCK_METHOD6(m, __VA_ARGS__) +#define MOCK_METHOD7_T(m, ...) MOCK_METHOD7(m, __VA_ARGS__) +#define MOCK_METHOD8_T(m, ...) MOCK_METHOD8(m, __VA_ARGS__) +#define MOCK_METHOD9_T(m, ...) MOCK_METHOD9(m, __VA_ARGS__) +#define MOCK_METHOD10_T(m, ...) MOCK_METHOD10(m, __VA_ARGS__) + +#define MOCK_CONST_METHOD0_T(m, ...) MOCK_CONST_METHOD0(m, __VA_ARGS__) +#define MOCK_CONST_METHOD1_T(m, ...) MOCK_CONST_METHOD1(m, __VA_ARGS__) +#define MOCK_CONST_METHOD2_T(m, ...) MOCK_CONST_METHOD2(m, __VA_ARGS__) +#define MOCK_CONST_METHOD3_T(m, ...) MOCK_CONST_METHOD3(m, __VA_ARGS__) +#define MOCK_CONST_METHOD4_T(m, ...) MOCK_CONST_METHOD4(m, __VA_ARGS__) +#define MOCK_CONST_METHOD5_T(m, ...) MOCK_CONST_METHOD5(m, __VA_ARGS__) +#define MOCK_CONST_METHOD6_T(m, ...) MOCK_CONST_METHOD6(m, __VA_ARGS__) +#define MOCK_CONST_METHOD7_T(m, ...) MOCK_CONST_METHOD7(m, __VA_ARGS__) +#define MOCK_CONST_METHOD8_T(m, ...) MOCK_CONST_METHOD8(m, __VA_ARGS__) +#define MOCK_CONST_METHOD9_T(m, ...) MOCK_CONST_METHOD9(m, __VA_ARGS__) +#define MOCK_CONST_METHOD10_T(m, ...) MOCK_CONST_METHOD10(m, __VA_ARGS__) + +#define MOCK_METHOD0_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 0, __VA_ARGS__) +#define MOCK_METHOD1_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 1, __VA_ARGS__) +#define MOCK_METHOD2_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 2, __VA_ARGS__) +#define MOCK_METHOD3_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 3, __VA_ARGS__) +#define MOCK_METHOD4_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 4, __VA_ARGS__) +#define MOCK_METHOD5_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 5, __VA_ARGS__) +#define MOCK_METHOD6_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 6, __VA_ARGS__) +#define MOCK_METHOD7_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 7, __VA_ARGS__) +#define MOCK_METHOD8_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 8, __VA_ARGS__) +#define MOCK_METHOD9_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 9, __VA_ARGS__) +#define MOCK_METHOD10_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 10, __VA_ARGS__) + +#define MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 0, __VA_ARGS__) +#define MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 1, __VA_ARGS__) +#define MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 2, __VA_ARGS__) +#define MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 3, __VA_ARGS__) +#define MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 4, __VA_ARGS__) +#define MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 5, __VA_ARGS__) +#define MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 6, __VA_ARGS__) +#define MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 7, __VA_ARGS__) +#define MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 8, __VA_ARGS__) +#define MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 9, __VA_ARGS__) +#define MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 10, __VA_ARGS__) + +#define MOCK_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD0_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD1_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD2_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD3_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD4_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD5_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD6_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD7_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD8_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD9_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD10_WITH_CALLTYPE(ct, m, __VA_ARGS__) + +#define MOCK_CONST_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, __VA_ARGS__) + +#define GMOCK_INTERNAL_MOCK_METHODN(constness, ct, Method, args_num, ...) \ + GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE( \ + args_num, ::testing::internal::identity_t<__VA_ARGS__>); \ + GMOCK_INTERNAL_MOCK_METHOD_IMPL( \ + args_num, Method, GMOCK_PP_NARG0(constness), 0, 0, , ct, , \ + (::testing::internal::identity_t<__VA_ARGS__>)) + +#define GMOCK_MOCKER_(arity, constness, Method) \ + GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__) + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used variadic actions. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_ + +#include <memory> +#include <utility> + + +// Include any custom callback actions added by the local installation. +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_ + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_ + +// Sometimes you want to give an action explicit template parameters +// that cannot be inferred from its value parameters. ACTION() and +// ACTION_P*() don't support that. ACTION_TEMPLATE() remedies that +// and can be viewed as an extension to ACTION() and ACTION_P*(). +// +// The syntax: +// +// ACTION_TEMPLATE(ActionName, +// HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), +// AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } +// +// defines an action template that takes m explicit template +// parameters and n value parameters. name_i is the name of the i-th +// template parameter, and kind_i specifies whether it's a typename, +// an integral constant, or a template. p_i is the name of the i-th +// value parameter. +// +// Example: +// +// // DuplicateArg<k, T>(output) converts the k-th argument of the mock +// // function to type T and copies it to *output. +// ACTION_TEMPLATE(DuplicateArg, +// HAS_2_TEMPLATE_PARAMS(int, k, typename, T), +// AND_1_VALUE_PARAMS(output)) { +// *output = T(::std::get<k>(args)); +// } +// ... +// int n; +// EXPECT_CALL(mock, Foo(_, _)) +// .WillOnce(DuplicateArg<1, unsigned char>(&n)); +// +// To create an instance of an action template, write: +// +// ActionName<t1, ..., t_m>(v1, ..., v_n) +// +// where the ts are the template arguments and the vs are the value +// arguments. The value argument types are inferred by the compiler. +// If you want to explicitly specify the value argument types, you can +// provide additional template arguments: +// +// ActionName<t1, ..., t_m, u1, ..., u_k>(v1, ..., v_n) +// +// where u_i is the desired type of v_i. +// +// ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the +// number of value parameters, but not on the number of template +// parameters. Without the restriction, the meaning of the following +// is unclear: +// +// OverloadedAction<int, bool>(x); +// +// Are we using a single-template-parameter action where 'bool' refers +// to the type of x, or are we using a two-template-parameter action +// where the compiler is asked to infer the type of x? +// +// Implementation notes: +// +// GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and +// GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for +// implementing ACTION_TEMPLATE. The main trick we use is to create +// new macro invocations when expanding a macro. For example, we have +// +// #define ACTION_TEMPLATE(name, template_params, value_params) +// ... GMOCK_INTERNAL_DECL_##template_params ... +// +// which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...) +// to expand to +// +// ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ... +// +// Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the +// preprocessor will continue to expand it to +// +// ... typename T ... +// +// This technique conforms to the C++ standard and is portable. It +// allows us to implement action templates using O(N) code, where N is +// the maximum number of template/value parameters supported. Without +// using it, we'd have to devote O(N^2) amount of code to implement all +// combinations of m and n. + +// Declares the template parameters. +#define GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(kind0, name0) kind0 name0 +#define GMOCK_INTERNAL_DECL_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1) kind0 name0, kind1 name1 +#define GMOCK_INTERNAL_DECL_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2) kind0 name0, kind1 name1, kind2 name2 +#define GMOCK_INTERNAL_DECL_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3) kind0 name0, kind1 name1, kind2 name2, \ + kind3 name3 +#define GMOCK_INTERNAL_DECL_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4) kind0 name0, kind1 name1, \ + kind2 name2, kind3 name3, kind4 name4 +#define GMOCK_INTERNAL_DECL_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5) kind0 name0, \ + kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5 +#define GMOCK_INTERNAL_DECL_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6) kind0 name0, kind1 name1, kind2 name2, kind3 name3, kind4 name4, \ + kind5 name5, kind6 name6 +#define GMOCK_INTERNAL_DECL_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7) kind0 name0, kind1 name1, kind2 name2, kind3 name3, \ + kind4 name4, kind5 name5, kind6 name6, kind7 name7 +#define GMOCK_INTERNAL_DECL_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7, kind8, name8) kind0 name0, kind1 name1, kind2 name2, \ + kind3 name3, kind4 name4, kind5 name5, kind6 name6, kind7 name7, \ + kind8 name8 +#define GMOCK_INTERNAL_DECL_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6, kind7, name7, kind8, name8, kind9, name9) kind0 name0, \ + kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5, \ + kind6 name6, kind7 name7, kind8 name8, kind9 name9 + +// Lists the template parameters. +#define GMOCK_INTERNAL_LIST_HAS_1_TEMPLATE_PARAMS(kind0, name0) name0 +#define GMOCK_INTERNAL_LIST_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1) name0, name1 +#define GMOCK_INTERNAL_LIST_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2) name0, name1, name2 +#define GMOCK_INTERNAL_LIST_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3) name0, name1, name2, name3 +#define GMOCK_INTERNAL_LIST_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4) name0, name1, name2, name3, \ + name4 +#define GMOCK_INTERNAL_LIST_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5) name0, name1, \ + name2, name3, name4, name5 +#define GMOCK_INTERNAL_LIST_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6) name0, name1, name2, name3, name4, name5, name6 +#define GMOCK_INTERNAL_LIST_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7) name0, name1, name2, name3, name4, name5, name6, name7 +#define GMOCK_INTERNAL_LIST_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7, kind8, name8) name0, name1, name2, name3, name4, name5, \ + name6, name7, name8 +#define GMOCK_INTERNAL_LIST_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6, kind7, name7, kind8, name8, kind9, name9) name0, name1, name2, \ + name3, name4, name5, name6, name7, name8, name9 + +// Declares the types of value parameters. +#define GMOCK_INTERNAL_DECL_TYPE_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_DECL_TYPE_AND_1_VALUE_PARAMS(p0) , typename p0##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_2_VALUE_PARAMS(p0, p1) , \ + typename p0##_type, typename p1##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , \ + typename p0##_type, typename p1##_type, typename p2##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \ + typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \ + typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \ + typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) , typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type, \ + typename p6##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7) , typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type, \ + typename p6##_type, typename p7##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8) , typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type, \ + typename p6##_type, typename p7##_type, typename p8##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8, p9) , typename p0##_type, typename p1##_type, \ + typename p2##_type, typename p3##_type, typename p4##_type, \ + typename p5##_type, typename p6##_type, typename p7##_type, \ + typename p8##_type, typename p9##_type + +// Initializes the value parameters. +#define GMOCK_INTERNAL_INIT_AND_0_VALUE_PARAMS()\ + () +#define GMOCK_INTERNAL_INIT_AND_1_VALUE_PARAMS(p0)\ + (p0##_type gmock_p0) : p0(::std::move(gmock_p0)) +#define GMOCK_INTERNAL_INIT_AND_2_VALUE_PARAMS(p0, p1)\ + (p0##_type gmock_p0, p1##_type gmock_p1) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)) +#define GMOCK_INTERNAL_INIT_AND_3_VALUE_PARAMS(p0, p1, p2)\ + (p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)) +#define GMOCK_INTERNAL_INIT_AND_4_VALUE_PARAMS(p0, p1, p2, p3)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)) +#define GMOCK_INTERNAL_INIT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)) +#define GMOCK_INTERNAL_INIT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \ + p5(::std::move(gmock_p5)) +#define GMOCK_INTERNAL_INIT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \ + p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)) +#define GMOCK_INTERNAL_INIT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \ + p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \ + p7(::std::move(gmock_p7)) +#define GMOCK_INTERNAL_INIT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \ + p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \ + p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8)) +#define GMOCK_INTERNAL_INIT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \ + p9##_type gmock_p9) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \ + p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \ + p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8)), \ + p9(::std::move(gmock_p9)) + +// Defines the copy constructor +#define GMOCK_INTERNAL_DEFN_COPY_AND_0_VALUE_PARAMS() \ + {} // Avoid https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82134 +#define GMOCK_INTERNAL_DEFN_COPY_AND_1_VALUE_PARAMS(...) = default; +#define GMOCK_INTERNAL_DEFN_COPY_AND_2_VALUE_PARAMS(...) = default; +#define GMOCK_INTERNAL_DEFN_COPY_AND_3_VALUE_PARAMS(...) = default; +#define GMOCK_INTERNAL_DEFN_COPY_AND_4_VALUE_PARAMS(...) = default; +#define GMOCK_INTERNAL_DEFN_COPY_AND_5_VALUE_PARAMS(...) = default; +#define GMOCK_INTERNAL_DEFN_COPY_AND_6_VALUE_PARAMS(...) = default; +#define GMOCK_INTERNAL_DEFN_COPY_AND_7_VALUE_PARAMS(...) = default; +#define GMOCK_INTERNAL_DEFN_COPY_AND_8_VALUE_PARAMS(...) = default; +#define GMOCK_INTERNAL_DEFN_COPY_AND_9_VALUE_PARAMS(...) = default; +#define GMOCK_INTERNAL_DEFN_COPY_AND_10_VALUE_PARAMS(...) = default; + +// Declares the fields for storing the value parameters. +#define GMOCK_INTERNAL_DEFN_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_DEFN_AND_1_VALUE_PARAMS(p0) p0##_type p0; +#define GMOCK_INTERNAL_DEFN_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0; \ + p1##_type p1; +#define GMOCK_INTERNAL_DEFN_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0; \ + p1##_type p1; p2##_type p2; +#define GMOCK_INTERNAL_DEFN_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0; \ + p1##_type p1; p2##_type p2; p3##_type p3; +#define GMOCK_INTERNAL_DEFN_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \ + p4) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; +#define GMOCK_INTERNAL_DEFN_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \ + p5) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ + p5##_type p5; +#define GMOCK_INTERNAL_DEFN_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ + p5##_type p5; p6##_type p6; +#define GMOCK_INTERNAL_DEFN_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ + p5##_type p5; p6##_type p6; p7##_type p7; +#define GMOCK_INTERNAL_DEFN_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \ + p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; +#define GMOCK_INTERNAL_DEFN_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \ + p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; \ + p9##_type p9; + +// Lists the value parameters. +#define GMOCK_INTERNAL_LIST_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_LIST_AND_1_VALUE_PARAMS(p0) p0 +#define GMOCK_INTERNAL_LIST_AND_2_VALUE_PARAMS(p0, p1) p0, p1 +#define GMOCK_INTERNAL_LIST_AND_3_VALUE_PARAMS(p0, p1, p2) p0, p1, p2 +#define GMOCK_INTERNAL_LIST_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0, p1, p2, p3 +#define GMOCK_INTERNAL_LIST_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) p0, p1, \ + p2, p3, p4 +#define GMOCK_INTERNAL_LIST_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) p0, \ + p1, p2, p3, p4, p5 +#define GMOCK_INTERNAL_LIST_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) p0, p1, p2, p3, p4, p5, p6 +#define GMOCK_INTERNAL_LIST_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) p0, p1, p2, p3, p4, p5, p6, p7 +#define GMOCK_INTERNAL_LIST_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) p0, p1, p2, p3, p4, p5, p6, p7, p8 +#define GMOCK_INTERNAL_LIST_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) p0, p1, p2, p3, p4, p5, p6, p7, p8, p9 + +// Lists the value parameter types. +#define GMOCK_INTERNAL_LIST_TYPE_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_LIST_TYPE_AND_1_VALUE_PARAMS(p0) , p0##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_2_VALUE_PARAMS(p0, p1) , p0##_type, \ + p1##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , p0##_type, \ + p1##_type, p2##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \ + p0##_type, p1##_type, p2##_type, p3##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \ + p0##_type, p1##_type, p2##_type, p3##_type, p4##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \ + p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type, \ + p6##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ + p5##_type, p6##_type, p7##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ + p5##_type, p6##_type, p7##_type, p8##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8, p9) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ + p5##_type, p6##_type, p7##_type, p8##_type, p9##_type + +// Declares the value parameters. +#define GMOCK_INTERNAL_DECL_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_DECL_AND_1_VALUE_PARAMS(p0) p0##_type p0 +#define GMOCK_INTERNAL_DECL_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0, \ + p1##_type p1 +#define GMOCK_INTERNAL_DECL_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0, \ + p1##_type p1, p2##_type p2 +#define GMOCK_INTERNAL_DECL_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0, \ + p1##_type p1, p2##_type p2, p3##_type p3 +#define GMOCK_INTERNAL_DECL_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \ + p4) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4 +#define GMOCK_INTERNAL_DECL_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \ + p5) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ + p5##_type p5 +#define GMOCK_INTERNAL_DECL_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ + p5##_type p5, p6##_type p6 +#define GMOCK_INTERNAL_DECL_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ + p5##_type p5, p6##_type p6, p7##_type p7 +#define GMOCK_INTERNAL_DECL_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8 +#define GMOCK_INTERNAL_DECL_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \ + p9##_type p9 + +// The suffix of the class template implementing the action template. +#define GMOCK_INTERNAL_COUNT_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_COUNT_AND_1_VALUE_PARAMS(p0) P +#define GMOCK_INTERNAL_COUNT_AND_2_VALUE_PARAMS(p0, p1) P2 +#define GMOCK_INTERNAL_COUNT_AND_3_VALUE_PARAMS(p0, p1, p2) P3 +#define GMOCK_INTERNAL_COUNT_AND_4_VALUE_PARAMS(p0, p1, p2, p3) P4 +#define GMOCK_INTERNAL_COUNT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) P5 +#define GMOCK_INTERNAL_COUNT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) P6 +#define GMOCK_INTERNAL_COUNT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6) P7 +#define GMOCK_INTERNAL_COUNT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) P8 +#define GMOCK_INTERNAL_COUNT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) P9 +#define GMOCK_INTERNAL_COUNT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) P10 + +// The name of the class template implementing the action template. +#define GMOCK_ACTION_CLASS_(name, value_params)\ + GTEST_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params) + +#define ACTION_TEMPLATE(name, template_params, value_params) \ + template <GMOCK_INTERNAL_DECL_##template_params \ + GMOCK_INTERNAL_DECL_TYPE_##value_params> \ + class GMOCK_ACTION_CLASS_(name, value_params) { \ + public: \ + explicit GMOCK_ACTION_CLASS_(name, value_params)( \ + GMOCK_INTERNAL_DECL_##value_params) \ + GMOCK_PP_IF(GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params), \ + = default; , \ + : impl_(std::make_shared<gmock_Impl>( \ + GMOCK_INTERNAL_LIST_##value_params)) { }) \ + GMOCK_ACTION_CLASS_(name, value_params)( \ + const GMOCK_ACTION_CLASS_(name, value_params)&) noexcept \ + GMOCK_INTERNAL_DEFN_COPY_##value_params \ + GMOCK_ACTION_CLASS_(name, value_params)( \ + GMOCK_ACTION_CLASS_(name, value_params)&&) noexcept \ + GMOCK_INTERNAL_DEFN_COPY_##value_params \ + template <typename F> \ + operator ::testing::Action<F>() const { \ + return GMOCK_PP_IF( \ + GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params), \ + (::testing::internal::MakeAction<F, gmock_Impl>()), \ + (::testing::internal::MakeAction<F>(impl_))); \ + } \ + private: \ + class gmock_Impl { \ + public: \ + explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {} \ + template <typename function_type, typename return_type, \ + typename args_type, GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \ + return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \ + GMOCK_INTERNAL_DEFN_##value_params \ + }; \ + GMOCK_PP_IF(GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params), \ + , std::shared_ptr<const gmock_Impl> impl_;) \ + }; \ + template <GMOCK_INTERNAL_DECL_##template_params \ + GMOCK_INTERNAL_DECL_TYPE_##value_params> \ + GMOCK_ACTION_CLASS_(name, value_params)< \ + GMOCK_INTERNAL_LIST_##template_params \ + GMOCK_INTERNAL_LIST_TYPE_##value_params> name( \ + GMOCK_INTERNAL_DECL_##value_params) GTEST_MUST_USE_RESULT_; \ + template <GMOCK_INTERNAL_DECL_##template_params \ + GMOCK_INTERNAL_DECL_TYPE_##value_params> \ + inline GMOCK_ACTION_CLASS_(name, value_params)< \ + GMOCK_INTERNAL_LIST_##template_params \ + GMOCK_INTERNAL_LIST_TYPE_##value_params> name( \ + GMOCK_INTERNAL_DECL_##value_params) { \ + return GMOCK_ACTION_CLASS_(name, value_params)< \ + GMOCK_INTERNAL_LIST_##template_params \ + GMOCK_INTERNAL_LIST_TYPE_##value_params>( \ + GMOCK_INTERNAL_LIST_##value_params); \ + } \ + template <GMOCK_INTERNAL_DECL_##template_params \ + GMOCK_INTERNAL_DECL_TYPE_##value_params> \ + template <typename function_type, typename return_type, typename args_type, \ + GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \ + return_type GMOCK_ACTION_CLASS_(name, value_params)< \ + GMOCK_INTERNAL_LIST_##template_params \ + GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl::gmock_PerformImpl( \ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +namespace testing { + +// The ACTION*() macros trigger warning C4100 (unreferenced formal +// parameter) in MSVC with -W4. Unfortunately they cannot be fixed in +// the macro definition, as the warnings are generated when the macro +// is expanded and macro expansion cannot contain #pragma. Therefore +// we suppress them here. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#endif + +namespace internal { + +// internal::InvokeArgument - a helper for InvokeArgument action. +// The basic overloads are provided here for generic functors. +// Overloads for other custom-callables are provided in the +// internal/custom/gmock-generated-actions.h header. +template <typename F, typename... Args> +auto InvokeArgument(F f, Args... args) -> decltype(f(args...)) { + return f(args...); +} + +template <std::size_t index, typename... Params> +struct InvokeArgumentAction { + template <typename... Args> + auto operator()(Args&&... args) const -> decltype(internal::InvokeArgument( + std::get<index>(std::forward_as_tuple(std::forward<Args>(args)...)), + std::declval<const Params&>()...)) { + internal::FlatTuple<Args&&...> args_tuple(FlatTupleConstructTag{}, + std::forward<Args>(args)...); + return params.Apply([&](const Params&... unpacked_params) { + auto&& callable = args_tuple.template Get<index>(); + return internal::InvokeArgument( + std::forward<decltype(callable)>(callable), unpacked_params...); + }); + } + + internal::FlatTuple<Params...> params; +}; + +} // namespace internal + +// The InvokeArgument<N>(a1, a2, ..., a_k) action invokes the N-th +// (0-based) argument, which must be a k-ary callable, of the mock +// function, with arguments a1, a2, ..., a_k. +// +// Notes: +// +// 1. The arguments are passed by value by default. If you need to +// pass an argument by reference, wrap it inside std::ref(). For +// example, +// +// InvokeArgument<1>(5, string("Hello"), std::ref(foo)) +// +// passes 5 and string("Hello") by value, and passes foo by +// reference. +// +// 2. If the callable takes an argument by reference but std::ref() is +// not used, it will receive the reference to a copy of the value, +// instead of the original value. For example, when the 0-th +// argument of the mock function takes a const string&, the action +// +// InvokeArgument<0>(string("Hello")) +// +// makes a copy of the temporary string("Hello") object and passes a +// reference of the copy, instead of the original temporary object, +// to the callable. This makes it easy for a user to define an +// InvokeArgument action from temporary values and have it performed +// later. +template <std::size_t index, typename... Params> +internal::InvokeArgumentAction<index, typename std::decay<Params>::type...> +InvokeArgument(Params&&... params) { + return {internal::FlatTuple<typename std::decay<Params>::type...>( + internal::FlatTupleConstructTag{}, std::forward<Params>(params)...)}; +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +} // namespace testing + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_ +// Copyright 2013, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some matchers that depend on gmock-matchers.h. +// +// Note that tests are implemented in gmock-matchers_test.cc rather than +// gmock-more-matchers-test.cc. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_MATCHERS_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_MATCHERS_H_ + + +namespace testing { + +// Silence C4100 (unreferenced formal +// parameter) for MSVC +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#if (_MSC_VER == 1900) +// and silence C4800 (C4800: 'int *const ': forcing value +// to bool 'true' or 'false') for MSVC 14 +# pragma warning(disable:4800) + #endif +#endif + +// Defines a matcher that matches an empty container. The container must +// support both size() and empty(), which all STL-like containers provide. +MATCHER(IsEmpty, negation ? "isn't empty" : "is empty") { + if (arg.empty()) { + return true; + } + *result_listener << "whose size is " << arg.size(); + return false; +} + +// Define a matcher that matches a value that evaluates in boolean +// context to true. Useful for types that define "explicit operator +// bool" operators and so can't be compared for equality with true +// and false. +MATCHER(IsTrue, negation ? "is false" : "is true") { + return static_cast<bool>(arg); +} + +// Define a matcher that matches a value that evaluates in boolean +// context to false. Useful for types that define "explicit operator +// bool" operators and so can't be compared for equality with true +// and false. +MATCHER(IsFalse, negation ? "is true" : "is false") { + return !static_cast<bool>(arg); +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + + +} // namespace testing + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_MATCHERS_H_ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Implements class templates NiceMock, NaggyMock, and StrictMock. +// +// Given a mock class MockFoo that is created using Google Mock, +// NiceMock<MockFoo> is a subclass of MockFoo that allows +// uninteresting calls (i.e. calls to mock methods that have no +// EXPECT_CALL specs), NaggyMock<MockFoo> is a subclass of MockFoo +// that prints a warning when an uninteresting call occurs, and +// StrictMock<MockFoo> is a subclass of MockFoo that treats all +// uninteresting calls as errors. +// +// Currently a mock is naggy by default, so MockFoo and +// NaggyMock<MockFoo> behave like the same. However, we will soon +// switch the default behavior of mocks to be nice, as that in general +// leads to more maintainable tests. When that happens, MockFoo will +// stop behaving like NaggyMock<MockFoo> and start behaving like +// NiceMock<MockFoo>. +// +// NiceMock, NaggyMock, and StrictMock "inherit" the constructors of +// their respective base class. Therefore you can write +// NiceMock<MockFoo>(5, "a") to construct a nice mock where MockFoo +// has a constructor that accepts (int, const char*), for example. +// +// A known limitation is that NiceMock<MockFoo>, NaggyMock<MockFoo>, +// and StrictMock<MockFoo> only works for mock methods defined using +// the MOCK_METHOD* family of macros DIRECTLY in the MockFoo class. +// If a mock method is defined in a base class of MockFoo, the "nice" +// or "strict" modifier may not affect it, depending on the compiler. +// In particular, nesting NiceMock, NaggyMock, and StrictMock is NOT +// supported. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_ + +#include <type_traits> + + +namespace testing { +template <class MockClass> +class NiceMock; +template <class MockClass> +class NaggyMock; +template <class MockClass> +class StrictMock; + +namespace internal { +template <typename T> +std::true_type StrictnessModifierProbe(const NiceMock<T>&); +template <typename T> +std::true_type StrictnessModifierProbe(const NaggyMock<T>&); +template <typename T> +std::true_type StrictnessModifierProbe(const StrictMock<T>&); +std::false_type StrictnessModifierProbe(...); + +template <typename T> +constexpr bool HasStrictnessModifier() { + return decltype(StrictnessModifierProbe(std::declval<const T&>()))::value; +} + +// Base classes that register and deregister with testing::Mock to alter the +// default behavior around uninteresting calls. Inheriting from one of these +// classes first and then MockClass ensures the MockClass constructor is run +// after registration, and that the MockClass destructor runs before +// deregistration. This guarantees that MockClass's constructor and destructor +// run with the same level of strictness as its instance methods. + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW && \ + (defined(_MSC_VER) || defined(__clang__)) +// We need to mark these classes with this declspec to ensure that +// the empty base class optimization is performed. +#define GTEST_INTERNAL_EMPTY_BASE_CLASS __declspec(empty_bases) +#else +#define GTEST_INTERNAL_EMPTY_BASE_CLASS +#endif + +template <typename Base> +class NiceMockImpl { + public: + NiceMockImpl() { ::testing::Mock::AllowUninterestingCalls(this); } + + ~NiceMockImpl() { ::testing::Mock::UnregisterCallReaction(this); } +}; + +template <typename Base> +class NaggyMockImpl { + public: + NaggyMockImpl() { ::testing::Mock::WarnUninterestingCalls(this); } + + ~NaggyMockImpl() { ::testing::Mock::UnregisterCallReaction(this); } +}; + +template <typename Base> +class StrictMockImpl { + public: + StrictMockImpl() { ::testing::Mock::FailUninterestingCalls(this); } + + ~StrictMockImpl() { ::testing::Mock::UnregisterCallReaction(this); } +}; + +} // namespace internal + +template <class MockClass> +class GTEST_INTERNAL_EMPTY_BASE_CLASS NiceMock + : private internal::NiceMockImpl<MockClass>, + public MockClass { + public: + static_assert(!internal::HasStrictnessModifier<MockClass>(), + "Can't apply NiceMock to a class hierarchy that already has a " + "strictness modifier. See " + "https://google.github.io/googletest/" + "gmock_cook_book.html#NiceStrictNaggy"); + NiceMock() : MockClass() { + static_assert(sizeof(*this) == sizeof(MockClass), + "The impl subclass shouldn't introduce any padding"); + } + + // Ideally, we would inherit base class's constructors through a using + // declaration, which would preserve their visibility. However, many existing + // tests rely on the fact that current implementation reexports protected + // constructors as public. These tests would need to be cleaned up first. + + // Single argument constructor is special-cased so that it can be + // made explicit. + template <typename A> + explicit NiceMock(A&& arg) : MockClass(std::forward<A>(arg)) { + static_assert(sizeof(*this) == sizeof(MockClass), + "The impl subclass shouldn't introduce any padding"); + } + + template <typename TArg1, typename TArg2, typename... An> + NiceMock(TArg1&& arg1, TArg2&& arg2, An&&... args) + : MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2), + std::forward<An>(args)...) { + static_assert(sizeof(*this) == sizeof(MockClass), + "The impl subclass shouldn't introduce any padding"); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(NiceMock); +}; + +template <class MockClass> +class GTEST_INTERNAL_EMPTY_BASE_CLASS NaggyMock + : private internal::NaggyMockImpl<MockClass>, + public MockClass { + static_assert(!internal::HasStrictnessModifier<MockClass>(), + "Can't apply NaggyMock to a class hierarchy that already has a " + "strictness modifier. See " + "https://google.github.io/googletest/" + "gmock_cook_book.html#NiceStrictNaggy"); + + public: + NaggyMock() : MockClass() { + static_assert(sizeof(*this) == sizeof(MockClass), + "The impl subclass shouldn't introduce any padding"); + } + + // Ideally, we would inherit base class's constructors through a using + // declaration, which would preserve their visibility. However, many existing + // tests rely on the fact that current implementation reexports protected + // constructors as public. These tests would need to be cleaned up first. + + // Single argument constructor is special-cased so that it can be + // made explicit. + template <typename A> + explicit NaggyMock(A&& arg) : MockClass(std::forward<A>(arg)) { + static_assert(sizeof(*this) == sizeof(MockClass), + "The impl subclass shouldn't introduce any padding"); + } + + template <typename TArg1, typename TArg2, typename... An> + NaggyMock(TArg1&& arg1, TArg2&& arg2, An&&... args) + : MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2), + std::forward<An>(args)...) { + static_assert(sizeof(*this) == sizeof(MockClass), + "The impl subclass shouldn't introduce any padding"); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(NaggyMock); +}; + +template <class MockClass> +class GTEST_INTERNAL_EMPTY_BASE_CLASS StrictMock + : private internal::StrictMockImpl<MockClass>, + public MockClass { + public: + static_assert( + !internal::HasStrictnessModifier<MockClass>(), + "Can't apply StrictMock to a class hierarchy that already has a " + "strictness modifier. See " + "https://google.github.io/googletest/" + "gmock_cook_book.html#NiceStrictNaggy"); + StrictMock() : MockClass() { + static_assert(sizeof(*this) == sizeof(MockClass), + "The impl subclass shouldn't introduce any padding"); + } + + // Ideally, we would inherit base class's constructors through a using + // declaration, which would preserve their visibility. However, many existing + // tests rely on the fact that current implementation reexports protected + // constructors as public. These tests would need to be cleaned up first. + + // Single argument constructor is special-cased so that it can be + // made explicit. + template <typename A> + explicit StrictMock(A&& arg) : MockClass(std::forward<A>(arg)) { + static_assert(sizeof(*this) == sizeof(MockClass), + "The impl subclass shouldn't introduce any padding"); + } + + template <typename TArg1, typename TArg2, typename... An> + StrictMock(TArg1&& arg1, TArg2&& arg2, An&&... args) + : MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2), + std::forward<An>(args)...) { + static_assert(sizeof(*this) == sizeof(MockClass), + "The impl subclass shouldn't introduce any padding"); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(StrictMock); +}; + +#undef GTEST_INTERNAL_EMPTY_BASE_CLASS + +} // namespace testing + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_ + +namespace testing { + +// Declares Google Mock flags that we want a user to use programmatically. +GMOCK_DECLARE_bool_(catch_leaked_mocks); +GMOCK_DECLARE_string_(verbose); +GMOCK_DECLARE_int32_(default_mock_behavior); + +// Initializes Google Mock. This must be called before running the +// tests. In particular, it parses the command line for the flags +// that Google Mock recognizes. Whenever a Google Mock flag is seen, +// it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Mock flag variables are +// updated. +// +// Since Google Test is needed for Google Mock to work, this function +// also initializes Google Test and parses its flags, if that hasn't +// been done. +GTEST_API_ void InitGoogleMock(int* argc, char** argv); + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +GTEST_API_ void InitGoogleMock(int* argc, wchar_t** argv); + +// This overloaded version can be used on Arduino/embedded platforms where +// there is no argc/argv. +GTEST_API_ void InitGoogleMock(); + +} // namespace testing + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_H_ diff --git a/src/fmt/test/gtest/gtest/gtest-spi.h b/src/fmt/test/gtest/gtest/gtest-spi.h new file mode 100644 index 000000000..eacef4466 --- /dev/null +++ b/src/fmt/test/gtest/gtest/gtest-spi.h @@ -0,0 +1,238 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +// GOOGLETEST_CM0004 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include "gtest/gtest.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// This helper class can be used to mock out Google Test failure reporting +// so that we can test Google Test or code that builds on Google Test. +// +// An object of this class appends a TestPartResult object to the +// TestPartResultArray object given in the constructor whenever a Google Test +// failure is reported. It can either intercept only failures that are +// generated in the same thread that created this object or it can intercept +// all generated failures. The scope of this mock object can be controlled with +// the second argument to the two arguments constructor. +class GTEST_API_ ScopedFakeTestPartResultReporter + : public TestPartResultReporterInterface { + public: + // The two possible mocking modes of this object. + enum InterceptMode { + INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. + INTERCEPT_ALL_THREADS // Intercepts all failures. + }; + + // The c'tor sets this object as the test part result reporter used + // by Google Test. The 'result' parameter specifies where to report the + // results. This reporter will only catch failures generated in the current + // thread. DEPRECATED + explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); + + // Same as above, but you can choose the interception scope of this object. + ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, + TestPartResultArray* result); + + // The d'tor restores the previous test part result reporter. + ~ScopedFakeTestPartResultReporter() override; + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + void ReportTestPartResult(const TestPartResult& result) override; + + private: + void Init(); + + const InterceptMode intercept_mode_; + TestPartResultReporterInterface* old_reporter_; + TestPartResultArray* const result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter); +}; + +namespace internal { + +// A helper class for implementing EXPECT_FATAL_FAILURE() and +// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +class GTEST_API_ SingleFailureChecker { + public: + // The constructor remembers the arguments. + SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, const std::string& substr); + ~SingleFailureChecker(); + private: + const TestPartResultArray* const results_; + const TestPartResult::Type type_; + const std::string substr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); +}; + +} // namespace internal + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// A set of macros for testing Google Test assertions or code that's expected +// to generate Google Test fatal failures. It verifies that the given +// statement will cause exactly one fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_FATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - 'statement' cannot reference local non-static variables or +// non-static members of the current object. +// - 'statement' cannot return a value. +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. The AcceptsMacroThatExpandsToUnprotectedComma test in +// gtest_unittest.cc will fail to compile if we do that. +#define EXPECT_FATAL_FAILURE(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ALL_THREADS, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test non-fatal failures. It asserts that the given +// statement will cause exactly one non-fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// 'statement' is allowed to reference local variables and members of +// the current object. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. If we do that, the code won't compile when the user gives +// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that +// expands to code containing an unprotected comma. The +// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc +// catches that. +// +// For the same reason, we have to write +// if (::testing::internal::AlwaysTrue()) { statement; } +// instead of +// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) +// to avoid an MSVC warning on unreachable code. +#define EXPECT_NONFATAL_FAILURE(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ + >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ diff --git a/src/fmt/test/gtest/gtest/gtest.h b/src/fmt/test/gtest/gtest/gtest.h new file mode 100644 index 000000000..55a31d0a8 --- /dev/null +++ b/src/fmt/test/gtest/gtest/gtest.h @@ -0,0 +1,12398 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the public API for Google Test. It should be +// included by any test program that uses Google Test. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! +// +// Acknowledgment: Google Test borrowed the idea of automatic test +// registration from Barthelemy Dagenais' (barthelemy@prologique.com) +// easyUnit framework. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_H_ + +#include <cstddef> +#include <limits> +#include <memory> +#include <ostream> +#include <type_traits> +#include <vector> + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file declares functions and macros used internally by +// Google Test. They are subject to change without notice. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. +// +// Low-level types and utilities for porting Google Test to various +// platforms. All macros ending with _ and symbols defined in an +// internal namespace are subject to change without notice. Code +// outside Google Test MUST NOT USE THEM DIRECTLY. Macros that don't +// end with _ are part of Google Test's public API and can be used by +// code outside Google Test. +// +// This file is fundamental to Google Test. All other Google Test source +// files are expected to #include this. Therefore, it cannot #include +// any other Google Test header. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +// Environment-describing macros +// ----------------------------- +// +// Google Test can be used in many different environments. Macros in +// this section tell Google Test what kind of environment it is being +// used in, such that Google Test can provide environment-specific +// features and implementations. +// +// Google Test tries to automatically detect the properties of its +// environment, so users usually don't need to worry about these +// macros. However, the automatic detection is not perfect. +// Sometimes it's necessary for a user to define some of the following +// macros in the build script to override Google Test's decisions. +// +// If the user doesn't define a macro in the list, Google Test will +// provide a default definition. After this header is #included, all +// macros in this list will be defined to either 1 or 0. +// +// Notes to maintainers: +// - Each macro here is a user-tweakable knob; do not grow the list +// lightly. +// - Use #if to key off these macros. Don't use #ifdef or "#if +// defined(...)", which will not work as these macros are ALWAYS +// defined. +// +// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2) +// is/isn't available. +// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions +// are enabled. +// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular +// expressions are/aren't available. +// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that <pthread.h> +// is/isn't available. +// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't +// enabled. +// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that +// std::wstring does/doesn't work (Google Test can +// be used where std::wstring is unavailable). +// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the +// compiler supports Microsoft's "Structured +// Exception Handling". +// GTEST_HAS_STREAM_REDIRECTION +// - Define it to 1/0 to indicate whether the +// platform supports I/O stream redirection using +// dup() and dup2(). +// GTEST_LINKED_AS_SHARED_LIBRARY +// - Define to 1 when compiling tests that use +// Google Test as a shared library (known as +// DLL on Windows). +// GTEST_CREATE_SHARED_LIBRARY +// - Define to 1 when compiling Google Test itself +// as a shared library. +// GTEST_DEFAULT_DEATH_TEST_STYLE +// - The default value of --gtest_death_test_style. +// The legacy default has been "fast" in the open +// source version since 2008. The recommended value +// is "threadsafe", and can be set in +// custom/gtest-port.h. + +// Platform-indicating macros +// -------------------------- +// +// Macros indicating the platform on which Google Test is being used +// (a macro is defined to 1 if compiled on the given platform; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. +// +// GTEST_OS_AIX - IBM AIX +// GTEST_OS_CYGWIN - Cygwin +// GTEST_OS_DRAGONFLY - DragonFlyBSD +// GTEST_OS_FREEBSD - FreeBSD +// GTEST_OS_FUCHSIA - Fuchsia +// GTEST_OS_GNU_KFREEBSD - GNU/kFreeBSD +// GTEST_OS_HAIKU - Haiku +// GTEST_OS_HPUX - HP-UX +// GTEST_OS_LINUX - Linux +// GTEST_OS_LINUX_ANDROID - Google Android +// GTEST_OS_MAC - Mac OS X +// GTEST_OS_IOS - iOS +// GTEST_OS_NACL - Google Native Client (NaCl) +// GTEST_OS_NETBSD - NetBSD +// GTEST_OS_OPENBSD - OpenBSD +// GTEST_OS_OS2 - OS/2 +// GTEST_OS_QNX - QNX +// GTEST_OS_SOLARIS - Sun Solaris +// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) +// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop +// GTEST_OS_WINDOWS_MINGW - MinGW +// GTEST_OS_WINDOWS_MOBILE - Windows Mobile +// GTEST_OS_WINDOWS_PHONE - Windows Phone +// GTEST_OS_WINDOWS_RT - Windows Store App/WinRT +// GTEST_OS_ZOS - z/OS +// +// Among the platforms, Cygwin, Linux, Mac OS X, and Windows have the +// most stable support. Since core members of the Google Test project +// don't have access to other platforms, support for them may be less +// stable. If you notice any problems on your platform, please notify +// googletestframework@googlegroups.com (patches for fixing them are +// even more welcome!). +// +// It is possible that none of the GTEST_OS_* macros are defined. + +// Feature-indicating macros +// ------------------------- +// +// Macros indicating which Google Test features are available (a macro +// is defined to 1 if the corresponding feature is supported; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. +// +// These macros are public so that portable tests can be written. +// Such tests typically surround code using a feature with an #if +// which controls that code. For example: +// +// #if GTEST_HAS_DEATH_TEST +// EXPECT_DEATH(DoSomethingDeadly()); +// #endif +// +// GTEST_HAS_DEATH_TEST - death tests +// GTEST_HAS_TYPED_TEST - typed tests +// GTEST_HAS_TYPED_TEST_P - type-parameterized tests +// GTEST_IS_THREADSAFE - Google Test is thread-safe. +// GOOGLETEST_CM0007 DO NOT DELETE +// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with +// GTEST_HAS_POSIX_RE (see above) which users can +// define themselves. +// GTEST_USES_SIMPLE_RE - our own simple regex is used; +// the above RE\b(s) are mutually exclusive. + +// Misc public macros +// ------------------ +// +// GTEST_FLAG(flag_name) - references the variable corresponding to +// the given Google Test flag. + +// Internal utilities +// ------------------ +// +// The following macros and utilities are for Google Test's INTERNAL +// use only. Code outside Google Test MUST NOT USE THEM DIRECTLY. +// +// Macros for basic C++ coding: +// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning. +// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a +// variable don't have to be used. +// GTEST_DISALLOW_ASSIGN_ - disables copy operator=. +// GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=. +// GTEST_DISALLOW_MOVE_ASSIGN_ - disables move operator=. +// GTEST_DISALLOW_MOVE_AND_ASSIGN_ - disables move ctor and operator=. +// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. +// GTEST_INTENTIONAL_CONST_COND_PUSH_ - start code section where MSVC C4127 is +// suppressed (constant conditional). +// GTEST_INTENTIONAL_CONST_COND_POP_ - finish code section where MSVC C4127 +// is suppressed. +// GTEST_INTERNAL_HAS_ANY - for enabling UniversalPrinter<std::any> or +// UniversalPrinter<absl::any> specializations. +// GTEST_INTERNAL_HAS_OPTIONAL - for enabling UniversalPrinter<std::optional> +// or +// UniversalPrinter<absl::optional> +// specializations. +// GTEST_INTERNAL_HAS_STRING_VIEW - for enabling Matcher<std::string_view> or +// Matcher<absl::string_view> +// specializations. +// GTEST_INTERNAL_HAS_VARIANT - for enabling UniversalPrinter<std::variant> or +// UniversalPrinter<absl::variant> +// specializations. +// +// Synchronization: +// Mutex, MutexLock, ThreadLocal, GetThreadCount() +// - synchronization primitives. +// +// Regular expressions: +// RE - a simple regular expression class using the POSIX +// Extended Regular Expression syntax on UNIX-like platforms +// GOOGLETEST_CM0008 DO NOT DELETE +// or a reduced regular exception syntax on other +// platforms, including Windows. +// Logging: +// GTEST_LOG_() - logs messages at the specified severity level. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. +// +// Stdout and stderr capturing: +// CaptureStdout() - starts capturing stdout. +// GetCapturedStdout() - stops capturing stdout and returns the captured +// string. +// CaptureStderr() - starts capturing stderr. +// GetCapturedStderr() - stops capturing stderr and returns the captured +// string. +// +// Integer types: +// TypeWithSize - maps an integer to a int type. +// TimeInMillis - integers of known sizes. +// BiggestInt - the biggest signed integer type. +// +// Command-line utilities: +// GTEST_DECLARE_*() - declares a flag. +// GTEST_DEFINE_*() - defines a flag. +// GetInjectableArgvs() - returns the command line as a vector of strings. +// +// Environment variable utilities: +// GetEnv() - gets the value of an environment variable. +// BoolFromGTestEnv() - parses a bool environment variable. +// Int32FromGTestEnv() - parses an int32_t environment variable. +// StringFromGTestEnv() - parses a string environment variable. +// +// Deprecation warnings: +// GTEST_INTERNAL_DEPRECATED(message) - attribute marking a function as +// deprecated; calling a marked function +// should generate a compiler warning + +#include <ctype.h> // for isspace, etc +#include <stddef.h> // for ptrdiff_t +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <cerrno> +#include <cstdint> +#include <limits> +#include <type_traits> + +#ifndef _WIN32_WCE +# include <sys/types.h> +# include <sys/stat.h> +#endif // !_WIN32_WCE + +#if defined __APPLE__ +# include <AvailabilityMacros.h> +# include <TargetConditionals.h> +#endif + +#include <iostream> // NOLINT +#include <locale> +#include <memory> +#include <string> // NOLINT +#include <tuple> +#include <vector> // NOLINT + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the GTEST_OS_* macro. +// It is separate from gtest-port.h so that custom/gtest-port.h can include it. + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ + +// Determines the platform on which Google Test is compiled. +#ifdef __CYGWIN__ +# define GTEST_OS_CYGWIN 1 +# elif defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__) +# define GTEST_OS_WINDOWS_MINGW 1 +# define GTEST_OS_WINDOWS 1 +#elif defined _WIN32 +# define GTEST_OS_WINDOWS 1 +# ifdef _WIN32_WCE +# define GTEST_OS_WINDOWS_MOBILE 1 +# elif defined(WINAPI_FAMILY) +# include <winapifamily.h> +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define GTEST_OS_WINDOWS_DESKTOP 1 +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) +# define GTEST_OS_WINDOWS_PHONE 1 +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) +# define GTEST_OS_WINDOWS_RT 1 +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE) +# define GTEST_OS_WINDOWS_PHONE 1 +# define GTEST_OS_WINDOWS_TV_TITLE 1 +# else + // WINAPI_FAMILY defined but no known partition matched. + // Default to desktop. +# define GTEST_OS_WINDOWS_DESKTOP 1 +# endif +# else +# define GTEST_OS_WINDOWS_DESKTOP 1 +# endif // _WIN32_WCE +#elif defined __OS2__ +# define GTEST_OS_OS2 1 +#elif defined __APPLE__ +# define GTEST_OS_MAC 1 +# include <TargetConditionals.h> +# if TARGET_OS_IPHONE +# define GTEST_OS_IOS 1 +# endif +#elif defined __DragonFly__ +# define GTEST_OS_DRAGONFLY 1 +#elif defined __FreeBSD__ +# define GTEST_OS_FREEBSD 1 +#elif defined __Fuchsia__ +# define GTEST_OS_FUCHSIA 1 +#elif defined(__GLIBC__) && defined(__FreeBSD_kernel__) +# define GTEST_OS_GNU_KFREEBSD 1 +#elif defined __linux__ +# define GTEST_OS_LINUX 1 +# if defined __ANDROID__ +# define GTEST_OS_LINUX_ANDROID 1 +# endif +#elif defined __MVS__ +# define GTEST_OS_ZOS 1 +#elif defined(__sun) && defined(__SVR4) +# define GTEST_OS_SOLARIS 1 +#elif defined(_AIX) +# define GTEST_OS_AIX 1 +#elif defined(__hpux) +# define GTEST_OS_HPUX 1 +#elif defined __native_client__ +# define GTEST_OS_NACL 1 +#elif defined __NetBSD__ +# define GTEST_OS_NETBSD 1 +#elif defined __OpenBSD__ +# define GTEST_OS_OPENBSD 1 +#elif defined __QNX__ +# define GTEST_OS_QNX 1 +#elif defined(__HAIKU__) +#define GTEST_OS_HAIKU 1 +#elif defined ESP8266 +#define GTEST_OS_ESP8266 1 +#elif defined ESP32 +#define GTEST_OS_ESP32 1 +#elif defined(__XTENSA__) +#define GTEST_OS_XTENSA 1 +#endif // __CYGWIN__ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ + +#if !defined(GTEST_DEV_EMAIL_) +# define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" +# define GTEST_FLAG_PREFIX_ "gtest_" +# define GTEST_FLAG_PREFIX_DASH_ "gtest-" +# define GTEST_FLAG_PREFIX_UPPER_ "GTEST_" +# define GTEST_NAME_ "Google Test" +# define GTEST_PROJECT_URL_ "https://github.com/google/googletest/" +#endif // !defined(GTEST_DEV_EMAIL_) + +#if !defined(GTEST_INIT_GOOGLE_TEST_NAME_) +# define GTEST_INIT_GOOGLE_TEST_NAME_ "testing::InitGoogleTest" +#endif // !defined(GTEST_INIT_GOOGLE_TEST_NAME_) + +// Determines the version of gcc that is used to compile this. +#ifdef __GNUC__ +// 40302 means version 4.3.2. +# define GTEST_GCC_VER_ \ + (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__) +#endif // __GNUC__ + +// Macros for disabling Microsoft Visual C++ warnings. +// +// GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385) +// /* code that triggers warnings C4800 and C4385 */ +// GTEST_DISABLE_MSC_WARNINGS_POP_() +#if defined(_MSC_VER) +# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \ + __pragma(warning(push)) \ + __pragma(warning(disable: warnings)) +# define GTEST_DISABLE_MSC_WARNINGS_POP_() \ + __pragma(warning(pop)) +#else +// Not all compilers are MSVC +# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) +# define GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + +// Clang on Windows does not understand MSVC's pragma warning. +// We need clang-specific way to disable function deprecation warning. +#ifdef __clang__ +# define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-implementations\"") +#define GTEST_DISABLE_MSC_DEPRECATED_POP_() \ + _Pragma("clang diagnostic pop") +#else +# define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \ + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996) +# define GTEST_DISABLE_MSC_DEPRECATED_POP_() \ + GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + +// Brings in definitions for functions used in the testing::internal::posix +// namespace (read, write, close, chdir, isatty, stat). We do not currently +// use them on Windows Mobile. +#if GTEST_OS_WINDOWS +# if !GTEST_OS_WINDOWS_MOBILE +# include <direct.h> +# include <io.h> +# endif +// In order to avoid having to include <windows.h>, use forward declaration +#if GTEST_OS_WINDOWS_MINGW && !defined(__MINGW64_VERSION_MAJOR) +// MinGW defined _CRITICAL_SECTION and _RTL_CRITICAL_SECTION as two +// separate (equivalent) structs, instead of using typedef +typedef struct _CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#else +// Assume CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION. +// This assumption is verified by +// WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION. +typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#endif +#elif GTEST_OS_XTENSA +#include <unistd.h> +// Xtensa toolchains define strcasecmp in the string.h header instead of +// strings.h. string.h is already included. +#else +// This assumes that non-Windows OSes provide unistd.h. For OSes where this +// is not the case, we need to include headers that provide the functions +// mentioned above. +# include <unistd.h> +# include <strings.h> +#endif // GTEST_OS_WINDOWS + +#if GTEST_OS_LINUX_ANDROID +// Used to define __ANDROID_API__ matching the target NDK API level. +# include <android/api-level.h> // NOLINT +#endif + +// Defines this to true if and only if Google Test can use POSIX regular +// expressions. +#ifndef GTEST_HAS_POSIX_RE +# if GTEST_OS_LINUX_ANDROID +// On Android, <regex.h> is only available starting with Gingerbread. +# define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9) +# else +#define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS && !GTEST_OS_XTENSA) +# endif +#endif + +#if GTEST_USES_PCRE +// The appropriate headers have already been included. + +#elif GTEST_HAS_POSIX_RE + +// On some platforms, <regex.h> needs someone to define size_t, and +// won't compile otherwise. We can #include it here as we already +// included <stdlib.h>, which is guaranteed to define size_t through +// <stddef.h>. +# include <regex.h> // NOLINT + +# define GTEST_USES_POSIX_RE 1 + +#elif GTEST_OS_WINDOWS + +// <regex.h> is not available on Windows. Use our own simple regex +// implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#else + +// <regex.h> may not be available on this platform. Use our own +// simple regex implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#endif // GTEST_USES_PCRE + +#ifndef GTEST_HAS_EXCEPTIONS +// The user didn't tell us whether exceptions are enabled, so we need +// to figure it out. +# if defined(_MSC_VER) && defined(_CPPUNWIND) +// MSVC defines _CPPUNWIND to 1 if and only if exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__BORLANDC__) +// C++Builder's implementation of the STL uses the _HAS_EXCEPTIONS +// macro to enable exceptions, so we'll do the same. +// Assumes that exceptions are enabled by default. +# ifndef _HAS_EXCEPTIONS +# define _HAS_EXCEPTIONS 1 +# endif // _HAS_EXCEPTIONS +# define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS +# elif defined(__clang__) +// clang defines __EXCEPTIONS if and only if exceptions are enabled before clang +// 220714, but if and only if cleanups are enabled after that. In Obj-C++ files, +// there can be cleanups for ObjC exceptions which also need cleanups, even if +// C++ exceptions are disabled. clang has __has_feature(cxx_exceptions) which +// checks for C++ exceptions starting at clang r206352, but which checked for +// cleanups prior to that. To reliably check for C++ exception availability with +// clang, check for +// __EXCEPTIONS && __has_feature(cxx_exceptions). +# define GTEST_HAS_EXCEPTIONS (__EXCEPTIONS && __has_feature(cxx_exceptions)) +# elif defined(__GNUC__) && __EXCEPTIONS +// gcc defines __EXCEPTIONS to 1 if and only if exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__SUNPRO_CC) +// Sun Pro CC supports exceptions. However, there is no compile-time way of +// detecting whether they are enabled or not. Therefore, we assume that +// they are enabled unless the user tells us otherwise. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__IBMCPP__) && __EXCEPTIONS +// xlC defines __EXCEPTIONS to 1 if and only if exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__HP_aCC) +// Exception handling is in effect by default in HP aCC compiler. It has to +// be turned of by +noeh compiler option if desired. +# define GTEST_HAS_EXCEPTIONS 1 +# else +// For other compilers, we assume exceptions are disabled to be +// conservative. +# define GTEST_HAS_EXCEPTIONS 0 +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +#endif // GTEST_HAS_EXCEPTIONS + +#ifndef GTEST_HAS_STD_WSTRING +// The user didn't tell us whether ::std::wstring is available, so we need +// to figure it out. +// Cygwin 1.7 and below doesn't support ::std::wstring. +// Solaris' libc++ doesn't support it either. Android has +// no support for it at least as recent as Froyo (2.2). +#define GTEST_HAS_STD_WSTRING \ + (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + GTEST_OS_HAIKU || GTEST_OS_ESP32 || GTEST_OS_ESP8266 || GTEST_OS_XTENSA)) + +#endif // GTEST_HAS_STD_WSTRING + +// Determines whether RTTI is available. +#ifndef GTEST_HAS_RTTI +// The user didn't tell us whether RTTI is enabled, so we need to +// figure it out. + +# ifdef _MSC_VER + +#ifdef _CPPRTTI // MSVC defines this macro if and only if RTTI is enabled. +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +// Starting with version 4.3.2, gcc defines __GXX_RTTI if and only if RTTI is +// enabled. +# elif defined(__GNUC__) + +# ifdef __GXX_RTTI +// When building against STLport with the Android NDK and with +// -frtti -fno-exceptions, the build fails at link time with undefined +// references to __cxa_bad_typeid. Note sure if STL or toolchain bug, +// so disable RTTI when detected. +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) && \ + !defined(__EXCEPTIONS) +# define GTEST_HAS_RTTI 0 +# else +# define GTEST_HAS_RTTI 1 +# endif // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS +# else +# define GTEST_HAS_RTTI 0 +# endif // __GXX_RTTI + +// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends +// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the +// first version with C++ support. +# elif defined(__clang__) + +# define GTEST_HAS_RTTI __has_feature(cxx_rtti) + +// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if +// both the typeid and dynamic_cast features are present. +# elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) + +# ifdef __RTTI_ALL__ +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +# else + +// For all other compilers, we assume RTTI is enabled. +# define GTEST_HAS_RTTI 1 + +# endif // _MSC_VER + +#endif // GTEST_HAS_RTTI + +// It's this header's responsibility to #include <typeinfo> when RTTI +// is enabled. +#if GTEST_HAS_RTTI +# include <typeinfo> +#endif + +// Determines whether Google Test can use the pthreads library. +#ifndef GTEST_HAS_PTHREAD +// The user didn't tell us explicitly, so we make reasonable assumptions about +// which platforms have pthreads support. +// +// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 +// to your compiler flags. +#define GTEST_HAS_PTHREAD \ + (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX || GTEST_OS_QNX || \ + GTEST_OS_FREEBSD || GTEST_OS_NACL || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA || \ + GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_OPENBSD || \ + GTEST_OS_HAIKU) +#endif // GTEST_HAS_PTHREAD + +#if GTEST_HAS_PTHREAD +// gtest-port.h guarantees to #include <pthread.h> when GTEST_HAS_PTHREAD is +// true. +# include <pthread.h> // NOLINT + +// For timespec and nanosleep, used below. +# include <time.h> // NOLINT +#endif + +// Determines whether clone(2) is supported. +// Usually it will only be available on Linux, excluding +// Linux on the Itanium architecture. +// Also see http://linux.die.net/man/2/clone. +#ifndef GTEST_HAS_CLONE +// The user didn't tell us, so we need to figure it out. + +# if GTEST_OS_LINUX && !defined(__ia64__) +# if GTEST_OS_LINUX_ANDROID +// On Android, clone() became available at different API levels for each 32-bit +// architecture. +# if defined(__LP64__) || \ + (defined(__arm__) && __ANDROID_API__ >= 9) || \ + (defined(__mips__) && __ANDROID_API__ >= 12) || \ + (defined(__i386__) && __ANDROID_API__ >= 17) +# define GTEST_HAS_CLONE 1 +# else +# define GTEST_HAS_CLONE 0 +# endif +# else +# define GTEST_HAS_CLONE 1 +# endif +# else +# define GTEST_HAS_CLONE 0 +# endif // GTEST_OS_LINUX && !defined(__ia64__) + +#endif // GTEST_HAS_CLONE + +// Determines whether to support stream redirection. This is used to test +// output correctness and to implement death tests. +#ifndef GTEST_HAS_STREAM_REDIRECTION +// By default, we assume that stream redirection is supported on all +// platforms except known mobile ones. +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \ + GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_XTENSA +# define GTEST_HAS_STREAM_REDIRECTION 0 +# else +# define GTEST_HAS_STREAM_REDIRECTION 1 +# endif // !GTEST_OS_WINDOWS_MOBILE +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Determines whether to support death tests. +// pops up a dialog window that cannot be suppressed programmatically. +#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + (GTEST_OS_MAC && !GTEST_OS_IOS) || \ + (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER) || GTEST_OS_WINDOWS_MINGW || \ + GTEST_OS_AIX || GTEST_OS_HPUX || GTEST_OS_OPENBSD || GTEST_OS_QNX || \ + GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA || \ + GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_HAIKU) +# define GTEST_HAS_DEATH_TEST 1 +#endif + +// Determines whether to support type-driven tests. + +// Typed tests need <typeinfo> and variadic macros, which GCC, VC++ 8.0, +// Sun Pro CC, IBM Visual Age, and HP aCC support. +#if defined(__GNUC__) || defined(_MSC_VER) || defined(__SUNPRO_CC) || \ + defined(__IBMCPP__) || defined(__HP_aCC) +# define GTEST_HAS_TYPED_TEST 1 +# define GTEST_HAS_TYPED_TEST_P 1 +#endif + +// Determines whether the system compiler uses UTF-16 for encoding wide strings. +#define GTEST_WIDE_STRING_USES_UTF16_ \ + (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_AIX || GTEST_OS_OS2) + +// Determines whether test results can be streamed to a socket. +#if GTEST_OS_LINUX || GTEST_OS_GNU_KFREEBSD || GTEST_OS_DRAGONFLY || \ + GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_OPENBSD +# define GTEST_CAN_STREAM_RESULTS_ 1 +#endif + +// Defines some utility macros. + +// The GNU compiler emits a warning if nested "if" statements are followed by +// an "else" statement and braces are not used to explicitly disambiguate the +// "else" binding. This leads to problems with code like: +// +// if (gate) +// ASSERT_*(condition) << "Some message"; +// +// The "switch (0) case 0:" idiom is used to suppress this. +#ifdef __INTEL_COMPILER +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ +#else +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default: // NOLINT +#endif + +// Use this annotation at the end of a struct/class definition to +// prevent the compiler from optimizing away instances that are never +// used. This is useful when all interesting logic happens inside the +// c'tor and / or d'tor. Example: +// +// struct Foo { +// Foo() { ... } +// } GTEST_ATTRIBUTE_UNUSED_; +// +// Also use it after a variable or parameter declaration to tell the +// compiler the variable/parameter does not have to be used. +#if defined(__GNUC__) && !defined(COMPILER_ICC) +# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) +#elif defined(__clang__) +# if __has_attribute(unused) +# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) +# endif +#endif +#ifndef GTEST_ATTRIBUTE_UNUSED_ +# define GTEST_ATTRIBUTE_UNUSED_ +#endif + +// Use this annotation before a function that takes a printf format string. +#if (defined(__GNUC__) || defined(__clang__)) && !defined(COMPILER_ICC) +# if defined(__MINGW_PRINTF_FORMAT) +// MinGW has two different printf implementations. Ensure the format macro +// matches the selected implementation. See +// https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/. +# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((__format__(__MINGW_PRINTF_FORMAT, string_index, \ + first_to_check))) +# else +# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((__format__(__printf__, string_index, first_to_check))) +# endif +#else +# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) +#endif + + +// A macro to disallow copy operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_ASSIGN_(type) \ + type& operator=(type const &) = delete + +// A macro to disallow copy constructor and operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type) \ + type(type const&) = delete; \ + type& operator=(type const&) = delete + +// A macro to disallow move operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_MOVE_ASSIGN_(type) \ + type& operator=(type &&) noexcept = delete + +// A macro to disallow move constructor and operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_MOVE_AND_ASSIGN_(type) \ + type(type&&) noexcept = delete; \ + type& operator=(type&&) noexcept = delete + +// Tell the compiler to warn about unused return values for functions declared +// with this macro. The macro should be used on function declarations +// following the argument list: +// +// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_; +#if defined(__GNUC__) && !defined(COMPILER_ICC) +# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result)) +#else +# define GTEST_MUST_USE_RESULT_ +#endif // __GNUC__ && !COMPILER_ICC + +// MS C++ compiler emits warning when a conditional expression is compile time +// constant. In some contexts this warning is false positive and needs to be +// suppressed. Use the following two macros in such cases: +// +// GTEST_INTENTIONAL_CONST_COND_PUSH_() +// while (true) { +// GTEST_INTENTIONAL_CONST_COND_POP_() +// } +# define GTEST_INTENTIONAL_CONST_COND_PUSH_() \ + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4127) +# define GTEST_INTENTIONAL_CONST_COND_POP_() \ + GTEST_DISABLE_MSC_WARNINGS_POP_() + +// Determine whether the compiler supports Microsoft's Structured Exception +// Handling. This is supported by several Windows compilers but generally +// does not exist on any other system. +#ifndef GTEST_HAS_SEH +// The user didn't tell us, so we need to figure it out. + +# if defined(_MSC_VER) || defined(__BORLANDC__) +// These two compilers are known to support SEH. +# define GTEST_HAS_SEH 1 +# else +// Assume no SEH. +# define GTEST_HAS_SEH 0 +# endif + +#endif // GTEST_HAS_SEH + +#ifndef GTEST_IS_THREADSAFE + +#define GTEST_IS_THREADSAFE \ + (GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ || \ + (GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT) || \ + GTEST_HAS_PTHREAD) + +#endif // GTEST_IS_THREADSAFE + +// GTEST_API_ qualifies all symbols that must be exported. The definitions below +// are guarded by #ifndef to give embedders a chance to define GTEST_API_ in +// gtest/internal/custom/gtest-port.h +#ifndef GTEST_API_ + +#ifdef _MSC_VER +# if GTEST_LINKED_AS_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllimport) +# elif GTEST_CREATE_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllexport) +# endif +#elif __GNUC__ >= 4 || defined(__clang__) +# define GTEST_API_ __attribute__((visibility ("default"))) +#endif // _MSC_VER + +#endif // GTEST_API_ + +#ifndef GTEST_API_ +# define GTEST_API_ +#endif // GTEST_API_ + +#ifndef GTEST_DEFAULT_DEATH_TEST_STYLE +# define GTEST_DEFAULT_DEATH_TEST_STYLE "fast" +#endif // GTEST_DEFAULT_DEATH_TEST_STYLE + +#ifdef __GNUC__ +// Ask the compiler to never inline a given function. +# define GTEST_NO_INLINE_ __attribute__((noinline)) +#else +# define GTEST_NO_INLINE_ +#endif + +// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project. +#if !defined(GTEST_HAS_CXXABI_H_) +# if defined(__GLIBCXX__) || (defined(_LIBCPP_VERSION) && !defined(_MSC_VER)) +# define GTEST_HAS_CXXABI_H_ 1 +# else +# define GTEST_HAS_CXXABI_H_ 0 +# endif +#endif + +// A function level attribute to disable checking for use of uninitialized +// memory when built with MemorySanitizer. +#if defined(__clang__) +# if __has_feature(memory_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ \ + __attribute__((no_sanitize_memory)) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +# endif // __has_feature(memory_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +#endif // __clang__ + +// A function level attribute to disable AddressSanitizer instrumentation. +#if defined(__clang__) +# if __has_feature(address_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ \ + __attribute__((no_sanitize_address)) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +# endif // __has_feature(address_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +#endif // __clang__ + +// A function level attribute to disable HWAddressSanitizer instrumentation. +#if defined(__clang__) +# if __has_feature(hwaddress_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ \ + __attribute__((no_sanitize("hwaddress"))) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +# endif // __has_feature(hwaddress_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +#endif // __clang__ + +// A function level attribute to disable ThreadSanitizer instrumentation. +#if defined(__clang__) +# if __has_feature(thread_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ \ + __attribute__((no_sanitize_thread)) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +# endif // __has_feature(thread_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +#endif // __clang__ + +namespace testing { + +class Message; + +// Legacy imports for backwards compatibility. +// New code should use std:: names directly. +using std::get; +using std::make_tuple; +using std::tuple; +using std::tuple_element; +using std::tuple_size; + +namespace internal { + +// A secret type that Google Test users don't know about. It has no +// definition on purpose. Therefore it's impossible to create a +// Secret object, which is what we want. +class Secret; + +// The GTEST_COMPILE_ASSERT_ is a legacy macro used to verify that a compile +// time expression is true (in new code, use static_assert instead). For +// example, you could use it to verify the size of a static array: +// +// GTEST_COMPILE_ASSERT_(GTEST_ARRAY_SIZE_(names) == NUM_NAMES, +// names_incorrect_size); +// +// The second argument to the macro must be a valid C++ identifier. If the +// expression is false, compiler will issue an error containing this identifier. +#define GTEST_COMPILE_ASSERT_(expr, msg) static_assert(expr, #msg) + +// A helper for suppressing warnings on constant condition. It just +// returns 'condition'. +GTEST_API_ bool IsTrue(bool condition); + +// Defines RE. + +#if GTEST_USES_PCRE +// if used, PCRE is injected by custom/gtest-port.h +#elif GTEST_USES_POSIX_RE || GTEST_USES_SIMPLE_RE + +// A simple C++ wrapper for <regex.h>. It uses the POSIX Extended +// Regular Expression syntax. +class GTEST_API_ RE { + public: + // A copy constructor is required by the Standard to initialize object + // references from r-values. + RE(const RE& other) { Init(other.pattern()); } + + // Constructs an RE from a string. + RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT + + RE(const char* regex) { Init(regex); } // NOLINT + ~RE(); + + // Returns the string representation of the regex. + const char* pattern() const { return pattern_; } + + // FullMatch(str, re) returns true if and only if regular expression re + // matches the entire str. + // PartialMatch(str, re) returns true if and only if regular expression re + // matches a substring of str (including str itself). + static bool FullMatch(const ::std::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::std::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + + static bool FullMatch(const char* str, const RE& re); + static bool PartialMatch(const char* str, const RE& re); + + private: + void Init(const char* regex); + const char* pattern_; + bool is_valid_; + +# if GTEST_USES_POSIX_RE + + regex_t full_regex_; // For FullMatch(). + regex_t partial_regex_; // For PartialMatch(). + +# else // GTEST_USES_SIMPLE_RE + + const char* full_pattern_; // For FullMatch(); + +# endif +}; + +#endif // GTEST_USES_PCRE + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line); + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file, + int line); + +// Defines logging utilities: +// GTEST_LOG_(severity) - logs messages at the specified severity level. The +// message itself is streamed into the macro. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. + +enum GTestLogSeverity { + GTEST_INFO, + GTEST_WARNING, + GTEST_ERROR, + GTEST_FATAL +}; + +// Formats log entry severity, provides a stream object for streaming the +// log message, and terminates the message with a newline when going out of +// scope. +class GTEST_API_ GTestLog { + public: + GTestLog(GTestLogSeverity severity, const char* file, int line); + + // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. + ~GTestLog(); + + ::std::ostream& GetStream() { return ::std::cerr; } + + private: + const GTestLogSeverity severity_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog); +}; + +#if !defined(GTEST_LOG_) + +# define GTEST_LOG_(severity) \ + ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \ + __FILE__, __LINE__).GetStream() + +inline void LogToStderr() {} +inline void FlushInfoLog() { fflush(nullptr); } + +#endif // !defined(GTEST_LOG_) + +#if !defined(GTEST_CHECK_) +// INTERNAL IMPLEMENTATION - DO NOT USE. +// +// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition +// is not satisfied. +// Synopsys: +// GTEST_CHECK_(boolean_condition); +// or +// GTEST_CHECK_(boolean_condition) << "Additional message"; +// +// This checks the condition and if the condition is not satisfied +// it prints message about the condition violation, including the +// condition itself, plus additional message streamed into it, if any, +// and then it aborts the program. It aborts the program irrespective of +// whether it is built in the debug mode or not. +# define GTEST_CHECK_(condition) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::IsTrue(condition)) \ + ; \ + else \ + GTEST_LOG_(FATAL) << "Condition " #condition " failed. " +#endif // !defined(GTEST_CHECK_) + +// An all-mode assert to verify that the given POSIX-style function +// call returns 0 (indicating success). Known limitation: this +// doesn't expand to a balanced 'if' statement, so enclose the macro +// in {} if you need to use it as the only statement in an 'if' +// branch. +#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \ + if (const int gtest_error = (posix_call)) \ + GTEST_LOG_(FATAL) << #posix_call << "failed with error " \ + << gtest_error + +// Transforms "T" into "const T&" according to standard reference collapsing +// rules (this is only needed as a backport for C++98 compilers that do not +// support reference collapsing). Specifically, it transforms: +// +// char ==> const char& +// const char ==> const char& +// char& ==> char& +// const char& ==> const char& +// +// Note that the non-const reference will not have "const" added. This is +// standard, and necessary so that "T" can always bind to "const T&". +template <typename T> +struct ConstRef { typedef const T& type; }; +template <typename T> +struct ConstRef<T&> { typedef T& type; }; + +// The argument T must depend on some template parameters. +#define GTEST_REFERENCE_TO_CONST_(T) \ + typename ::testing::internal::ConstRef<T>::type + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Use ImplicitCast_ as a safe version of static_cast for upcasting in +// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a +// const Foo*). When you use ImplicitCast_, the compiler checks that +// the cast is safe. Such explicit ImplicitCast_s are necessary in +// surprisingly many situations where C++ demands an exact type match +// instead of an argument type convertable to a target type. +// +// The syntax for using ImplicitCast_ is the same as for static_cast: +// +// ImplicitCast_<ToType>(expr) +// +// ImplicitCast_ would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., implicit_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template<typename To> +inline To ImplicitCast_(To x) { return x; } + +// When you upcast (that is, cast a pointer from type Foo to type +// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts +// always succeed. When you downcast (that is, cast a pointer from +// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because +// how do you know the pointer is really of type SubclassOfFoo? It +// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus, +// when you downcast, you should use this macro. In debug mode, we +// use dynamic_cast<> to double-check the downcast is legal (we die +// if it's not). In normal mode, we do the efficient static_cast<> +// instead. Thus, it's important to test in debug mode to make sure +// the cast is legal! +// This is the only place in the code we should use dynamic_cast<>. +// In particular, you SHOULDN'T be using dynamic_cast<> in order to +// do RTTI (eg code like this: +// if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo); +// if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo); +// You should design the code some other way not to need this. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., down_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template<typename To, typename From> // use like this: DownCast_<T*>(foo); +inline To DownCast_(From* f) { // so we only accept pointers + // Ensures that To is a sub-type of From *. This test is here only + // for compile-time type checking, and has no overhead in an + // optimized build at run-time, as it will be optimized away + // completely. + GTEST_INTENTIONAL_CONST_COND_PUSH_() + if (false) { + GTEST_INTENTIONAL_CONST_COND_POP_() + const To to = nullptr; + ::testing::internal::ImplicitCast_<From*>(to); + } + +#if GTEST_HAS_RTTI + // RTTI: debug mode only! + GTEST_CHECK_(f == nullptr || dynamic_cast<To>(f) != nullptr); +#endif + return static_cast<To>(f); +} + +// Downcasts the pointer of type Base to Derived. +// Derived must be a subclass of Base. The parameter MUST +// point to a class of type Derived, not any subclass of it. +// When RTTI is available, the function performs a runtime +// check to enforce this. +template <class Derived, class Base> +Derived* CheckedDowncastToActualType(Base* base) { +#if GTEST_HAS_RTTI + GTEST_CHECK_(typeid(*base) == typeid(Derived)); +#endif + +#if GTEST_HAS_DOWNCAST_ + return ::down_cast<Derived*>(base); +#elif GTEST_HAS_RTTI + return dynamic_cast<Derived*>(base); // NOLINT +#else + return static_cast<Derived*>(base); // Poor man's downcast. +#endif +} + +#if GTEST_HAS_STREAM_REDIRECTION + +// Defines the stderr capturer: +// CaptureStdout - starts capturing stdout. +// GetCapturedStdout - stops capturing stdout and returns the captured string. +// CaptureStderr - starts capturing stderr. +// GetCapturedStderr - stops capturing stderr and returns the captured string. +// +GTEST_API_ void CaptureStdout(); +GTEST_API_ std::string GetCapturedStdout(); +GTEST_API_ void CaptureStderr(); +GTEST_API_ std::string GetCapturedStderr(); + +#endif // GTEST_HAS_STREAM_REDIRECTION +// Returns the size (in bytes) of a file. +GTEST_API_ size_t GetFileSize(FILE* file); + +// Reads the entire content of a file as a string. +GTEST_API_ std::string ReadEntireFile(FILE* file); + +// All command line arguments. +GTEST_API_ std::vector<std::string> GetArgvs(); + +#if GTEST_HAS_DEATH_TEST + +std::vector<std::string> GetInjectableArgvs(); +// Deprecated: pass the args vector by value instead. +void SetInjectableArgvs(const std::vector<std::string>* new_argvs); +void SetInjectableArgvs(const std::vector<std::string>& new_argvs); +void ClearInjectableArgvs(); + +#endif // GTEST_HAS_DEATH_TEST + +// Defines synchronization primitives. +#if GTEST_IS_THREADSAFE +# if GTEST_HAS_PTHREAD +// Sleeps for (roughly) n milliseconds. This function is only for testing +// Google Test's own constructs. Don't use it in user tests, either +// directly or indirectly. +inline void SleepMilliseconds(int n) { + const timespec time = { + 0, // 0 seconds. + n * 1000L * 1000L, // And n ms. + }; + nanosleep(&time, nullptr); +} +# endif // GTEST_HAS_PTHREAD + +# if GTEST_HAS_NOTIFICATION_ +// Notification has already been imported into the namespace. +// Nothing to do here. + +# elif GTEST_HAS_PTHREAD +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +class Notification { + public: + Notification() : notified_(false) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, nullptr)); + } + ~Notification() { + pthread_mutex_destroy(&mutex_); + } + + // Notifies all threads created with this notification to start. Must + // be called from the controller thread. + void Notify() { + pthread_mutex_lock(&mutex_); + notified_ = true; + pthread_mutex_unlock(&mutex_); + } + + // Blocks until the controller thread notifies. Must be called from a test + // thread. + void WaitForNotification() { + for (;;) { + pthread_mutex_lock(&mutex_); + const bool notified = notified_; + pthread_mutex_unlock(&mutex_); + if (notified) + break; + SleepMilliseconds(10); + } + } + + private: + pthread_mutex_t mutex_; + bool notified_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); +}; + +# elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + +GTEST_API_ void SleepMilliseconds(int n); + +// Provides leak-safe Windows kernel handle ownership. +// Used in death tests and in threading support. +class GTEST_API_ AutoHandle { + public: + // Assume that Win32 HANDLE type is equivalent to void*. Doing so allows us to + // avoid including <windows.h> in this header file. Including <windows.h> is + // undesirable because it defines a lot of symbols and macros that tend to + // conflict with client code. This assumption is verified by + // WindowsTypesTest.HANDLEIsVoidStar. + typedef void* Handle; + AutoHandle(); + explicit AutoHandle(Handle handle); + + ~AutoHandle(); + + Handle Get() const; + void Reset(); + void Reset(Handle handle); + + private: + // Returns true if and only if the handle is a valid handle object that can be + // closed. + bool IsCloseable() const; + + Handle handle_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); +}; + +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +class GTEST_API_ Notification { + public: + Notification(); + void Notify(); + void WaitForNotification(); + + private: + AutoHandle event_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); +}; +# endif // GTEST_HAS_NOTIFICATION_ + +// On MinGW, we can have both GTEST_OS_WINDOWS and GTEST_HAS_PTHREAD +// defined, but we don't want to use MinGW's pthreads implementation, which +// has conformance problems with some versions of the POSIX standard. +# if GTEST_HAS_PTHREAD && !GTEST_OS_WINDOWS_MINGW + +// As a C-function, ThreadFuncWithCLinkage cannot be templated itself. +// Consequently, it cannot select a correct instantiation of ThreadWithParam +// in order to call its Run(). Introducing ThreadWithParamBase as a +// non-templated base class for ThreadWithParam allows us to bypass this +// problem. +class ThreadWithParamBase { + public: + virtual ~ThreadWithParamBase() {} + virtual void Run() = 0; +}; + +// pthread_create() accepts a pointer to a function type with the C linkage. +// According to the Standard (7.5/1), function types with different linkages +// are different even if they are otherwise identical. Some compilers (for +// example, SunStudio) treat them as different types. Since class methods +// cannot be defined with C-linkage we need to define a free C-function to +// pass into pthread_create(). +extern "C" inline void* ThreadFuncWithCLinkage(void* thread) { + static_cast<ThreadWithParamBase*>(thread)->Run(); + return nullptr; +} + +// Helper class for testing Google Test's multi-threading constructs. +// To use it, write: +// +// void ThreadFunc(int param) { /* Do things with param */ } +// Notification thread_can_start; +// ... +// // The thread_can_start parameter is optional; you can supply NULL. +// ThreadWithParam<int> thread(&ThreadFunc, 5, &thread_can_start); +// thread_can_start.Notify(); +// +// These classes are only for testing Google Test's own constructs. Do +// not use them in user tests, either directly or indirectly. +template <typename T> +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void UserThreadFunc(T); + + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) + : func_(func), + param_(param), + thread_can_start_(thread_can_start), + finished_(false) { + ThreadWithParamBase* const base = this; + // The thread can be created only after all fields except thread_ + // have been initialized. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_create(&thread_, nullptr, &ThreadFuncWithCLinkage, base)); + } + ~ThreadWithParam() override { Join(); } + + void Join() { + if (!finished_) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, nullptr)); + finished_ = true; + } + } + + void Run() override { + if (thread_can_start_ != nullptr) thread_can_start_->WaitForNotification(); + func_(param_); + } + + private: + UserThreadFunc* const func_; // User-supplied thread function. + const T param_; // User-supplied parameter to the thread function. + // When non-NULL, used to block execution until the controller thread + // notifies. + Notification* const thread_can_start_; + bool finished_; // true if and only if we know that the thread function has + // finished. + pthread_t thread_; // The native thread object. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); +}; +# endif // !GTEST_OS_WINDOWS && GTEST_HAS_PTHREAD || + // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ + +# if GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ +// Mutex and ThreadLocal have already been imported into the namespace. +// Nothing to do here. + +# elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + +// Mutex implements mutex on Windows platforms. It is used in conjunction +// with class MutexLock: +// +// Mutex mutex; +// ... +// MutexLock lock(&mutex); // Acquires the mutex and releases it at the +// // end of the current scope. +// +// A static Mutex *must* be defined or declared using one of the following +// macros: +// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex); +// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex); +// +// (A non-static Mutex is defined/declared in the usual way). +class GTEST_API_ Mutex { + public: + enum MutexType { kStatic = 0, kDynamic = 1 }; + // We rely on kStaticMutex being 0 as it is to what the linker initializes + // type_ in static mutexes. critical_section_ will be initialized lazily + // in ThreadSafeLazyInit(). + enum StaticConstructorSelector { kStaticMutex = 0 }; + + // This constructor intentionally does nothing. It relies on type_ being + // statically initialized to 0 (effectively setting it to kStatic) and on + // ThreadSafeLazyInit() to lazily initialize the rest of the members. + explicit Mutex(StaticConstructorSelector /*dummy*/) {} + + Mutex(); + ~Mutex(); + + void Lock(); + + void Unlock(); + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld(); + + private: + // Initializes owner_thread_id_ and critical_section_ in static mutexes. + void ThreadSafeLazyInit(); + + // Per https://blogs.msdn.microsoft.com/oldnewthing/20040223-00/?p=40503, + // we assume that 0 is an invalid value for thread IDs. + unsigned int owner_thread_id_; + + // For static mutexes, we rely on these members being initialized to zeros + // by the linker. + MutexType type_; + long critical_section_init_phase_; // NOLINT + GTEST_CRITICAL_SECTION* critical_section_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); +}; + +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::Mutex mutex(::testing::internal::Mutex::kStaticMutex) + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex* mutex) + : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + Mutex* const mutex_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock); +}; + +typedef GTestMutexLock MutexLock; + +// Base class for ValueHolder<T>. Allows a caller to hold and delete a value +// without knowing its type. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Provides a way for a thread to send notifications to a ThreadLocal +// regardless of its parameter type. +class ThreadLocalBase { + public: + // Creates a new ValueHolder<T> object holding a default value passed to + // this ThreadLocal<T>'s constructor and returns it. It is the caller's + // responsibility not to call this when the ThreadLocal<T> instance already + // has a value on the current thread. + virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const = 0; + + protected: + ThreadLocalBase() {} + virtual ~ThreadLocalBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocalBase); +}; + +// Maps a thread to a set of ThreadLocals that have values instantiated on that +// thread and notifies them when the thread exits. A ThreadLocal instance is +// expected to persist until all threads it has values on have terminated. +class GTEST_API_ ThreadLocalRegistry { + public: + // Registers thread_local_instance as having value on the current thread. + // Returns a value that can be used to identify the thread from other threads. + static ThreadLocalValueHolderBase* GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance); + + // Invoked when a ThreadLocal instance is destroyed. + static void OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance); +}; + +class GTEST_API_ ThreadWithParamBase { + public: + void Join(); + + protected: + class Runnable { + public: + virtual ~Runnable() {} + virtual void Run() = 0; + }; + + ThreadWithParamBase(Runnable *runnable, Notification* thread_can_start); + virtual ~ThreadWithParamBase(); + + private: + AutoHandle thread_; +}; + +// Helper class for testing Google Test's multi-threading constructs. +template <typename T> +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void UserThreadFunc(T); + + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) + : ThreadWithParamBase(new RunnableImpl(func, param), thread_can_start) { + } + virtual ~ThreadWithParam() {} + + private: + class RunnableImpl : public Runnable { + public: + RunnableImpl(UserThreadFunc* func, T param) + : func_(func), + param_(param) { + } + virtual ~RunnableImpl() {} + virtual void Run() { + func_(param_); + } + + private: + UserThreadFunc* const func_; + const T param_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(RunnableImpl); + }; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); +}; + +// Implements thread-local storage on Windows systems. +// +// // Thread 1 +// ThreadLocal<int> tl(100); // 100 is the default value for each thread. +// +// // Thread 2 +// tl.set(150); // Changes the value for thread 2 only. +// EXPECT_EQ(150, tl.get()); +// +// // Thread 1 +// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value. +// tl.set(200); +// EXPECT_EQ(200, tl.get()); +// +// The template type argument T must have a public copy constructor. +// In addition, the default ThreadLocal constructor requires T to have +// a public default constructor. +// +// The users of a TheadLocal instance have to make sure that all but one +// threads (including the main one) using that instance have exited before +// destroying it. Otherwise, the per-thread objects managed for them by the +// ThreadLocal instance are not guaranteed to be destroyed on all platforms. +// +// Google Test only uses global ThreadLocal objects. That means they +// will die after main() has returned. Therefore, no per-thread +// object managed by Google Test will be leaked as long as all threads +// using Google Test have exited when main() returns. +template <typename T> +class ThreadLocal : public ThreadLocalBase { + public: + ThreadLocal() : default_factory_(new DefaultValueHolderFactory()) {} + explicit ThreadLocal(const T& value) + : default_factory_(new InstanceValueHolderFactory(value)) {} + + ~ThreadLocal() { ThreadLocalRegistry::OnThreadLocalDestroyed(this); } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of T. Can be deleted via its base class without the caller + // knowing the type of T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + ValueHolder() : value_() {} + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder); + }; + + + T* GetOrCreateValue() const { + return static_cast<ValueHolder*>( + ThreadLocalRegistry::GetValueOnCurrentThread(this))->pointer(); + } + + virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const { + return default_factory_->MakeNewHolder(); + } + + class ValueHolderFactory { + public: + ValueHolderFactory() {} + virtual ~ValueHolderFactory() {} + virtual ValueHolder* MakeNewHolder() const = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolderFactory); + }; + + class DefaultValueHolderFactory : public ValueHolderFactory { + public: + DefaultValueHolderFactory() {} + ValueHolder* MakeNewHolder() const override { return new ValueHolder(); } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory); + }; + + class InstanceValueHolderFactory : public ValueHolderFactory { + public: + explicit InstanceValueHolderFactory(const T& value) : value_(value) {} + ValueHolder* MakeNewHolder() const override { + return new ValueHolder(value_); + } + + private: + const T value_; // The value for each thread. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InstanceValueHolderFactory); + }; + + std::unique_ptr<ValueHolderFactory> default_factory_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); +}; + +# elif GTEST_HAS_PTHREAD + +// MutexBase and Mutex implement mutex on pthreads-based platforms. +class MutexBase { + public: + // Acquires this mutex. + void Lock() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_)); + owner_ = pthread_self(); + has_owner_ = true; + } + + // Releases this mutex. + void Unlock() { + // Since the lock is being released the owner_ field should no longer be + // considered valid. We don't protect writing to has_owner_ here, as it's + // the caller's responsibility to ensure that the current thread holds the + // mutex when this is called. + has_owner_ = false; + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_)); + } + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld() const { + GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self())) + << "The current thread is not holding the mutex @" << this; + } + + // A static mutex may be used before main() is entered. It may even + // be used before the dynamic initialization stage. Therefore we + // must be able to initialize a static mutex object at link time. + // This means MutexBase has to be a POD and its member variables + // have to be public. + public: + pthread_mutex_t mutex_; // The underlying pthread mutex. + // has_owner_ indicates whether the owner_ field below contains a valid thread + // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All + // accesses to the owner_ field should be protected by a check of this field. + // An alternative might be to memset() owner_ to all zeros, but there's no + // guarantee that a zero'd pthread_t is necessarily invalid or even different + // from pthread_self(). + bool has_owner_; + pthread_t owner_; // The thread holding the mutex. +}; + +// Forward-declares a static mutex. +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::MutexBase mutex + +// Defines and statically (i.e. at link time) initializes a static mutex. +// The initialization list here does not explicitly initialize each field, +// instead relying on default initialization for the unspecified fields. In +// particular, the owner_ field (a pthread_t) is not explicitly initialized. +// This allows initialization to work whether pthread_t is a scalar or struct. +// The flag -Wmissing-field-initializers must not be specified for this to work. +#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::MutexBase mutex = {PTHREAD_MUTEX_INITIALIZER, false, 0} + +// The Mutex class can only be used for mutexes created at runtime. It +// shares its API with MutexBase otherwise. +class Mutex : public MutexBase { + public: + Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, nullptr)); + has_owner_ = false; + } + ~Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); +}; + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(MutexBase* mutex) + : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + MutexBase* const mutex_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock); +}; + +typedef GTestMutexLock MutexLock; + +// Helpers for ThreadLocal. + +// pthread_key_create() requires DeleteThreadLocalValue() to have +// C-linkage. Therefore it cannot be templatized to access +// ThreadLocal<T>. Hence the need for class +// ThreadLocalValueHolderBase. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Called by pthread to delete thread-local data stored by +// pthread_setspecific(). +extern "C" inline void DeleteThreadLocalValue(void* value_holder) { + delete static_cast<ThreadLocalValueHolderBase*>(value_holder); +} + +// Implements thread-local storage on pthreads-based systems. +template <typename T> +class GTEST_API_ ThreadLocal { + public: + ThreadLocal() + : key_(CreateKey()), default_factory_(new DefaultValueHolderFactory()) {} + explicit ThreadLocal(const T& value) + : key_(CreateKey()), + default_factory_(new InstanceValueHolderFactory(value)) {} + + ~ThreadLocal() { + // Destroys the managed object for the current thread, if any. + DeleteThreadLocalValue(pthread_getspecific(key_)); + + // Releases resources associated with the key. This will *not* + // delete managed objects for other threads. + GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_)); + } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of type T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + ValueHolder() : value_() {} + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder); + }; + + static pthread_key_t CreateKey() { + pthread_key_t key; + // When a thread exits, DeleteThreadLocalValue() will be called on + // the object managed for that thread. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_key_create(&key, &DeleteThreadLocalValue)); + return key; + } + + T* GetOrCreateValue() const { + ThreadLocalValueHolderBase* const holder = + static_cast<ThreadLocalValueHolderBase*>(pthread_getspecific(key_)); + if (holder != nullptr) { + return CheckedDowncastToActualType<ValueHolder>(holder)->pointer(); + } + + ValueHolder* const new_holder = default_factory_->MakeNewHolder(); + ThreadLocalValueHolderBase* const holder_base = new_holder; + GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base)); + return new_holder->pointer(); + } + + class ValueHolderFactory { + public: + ValueHolderFactory() {} + virtual ~ValueHolderFactory() {} + virtual ValueHolder* MakeNewHolder() const = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolderFactory); + }; + + class DefaultValueHolderFactory : public ValueHolderFactory { + public: + DefaultValueHolderFactory() {} + ValueHolder* MakeNewHolder() const override { return new ValueHolder(); } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory); + }; + + class InstanceValueHolderFactory : public ValueHolderFactory { + public: + explicit InstanceValueHolderFactory(const T& value) : value_(value) {} + ValueHolder* MakeNewHolder() const override { + return new ValueHolder(value_); + } + + private: + const T value_; // The value for each thread. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InstanceValueHolderFactory); + }; + + // A key pthreads uses for looking up per-thread values. + const pthread_key_t key_; + std::unique_ptr<ValueHolderFactory> default_factory_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); +}; + +# endif // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ + +#else // GTEST_IS_THREADSAFE + +// A dummy implementation of synchronization primitives (mutex, lock, +// and thread-local variable). Necessary for compiling Google Test where +// mutex is not supported - using Google Test in multiple threads is not +// supported on such platforms. + +class Mutex { + public: + Mutex() {} + void Lock() {} + void Unlock() {} + void AssertHeld() const {} +}; + +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex*) {} // NOLINT +}; + +typedef GTestMutexLock MutexLock; + +template <typename T> +class GTEST_API_ ThreadLocal { + public: + ThreadLocal() : value_() {} + explicit ThreadLocal(const T& value) : value_(value) {} + T* pointer() { return &value_; } + const T* pointer() const { return &value_; } + const T& get() const { return value_; } + void set(const T& value) { value_ = value; } + private: + T value_; +}; + +#endif // GTEST_IS_THREADSAFE + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +GTEST_API_ size_t GetThreadCount(); + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_SEP_ "\\" +# define GTEST_HAS_ALT_PATH_SEP_ 1 +#else +# define GTEST_PATH_SEP_ "/" +# define GTEST_HAS_ALT_PATH_SEP_ 0 +#endif // GTEST_OS_WINDOWS + +// Utilities for char. + +// isspace(int ch) and friends accept an unsigned char or EOF. char +// may be signed, depending on the compiler (or compiler flags). +// Therefore we need to cast a char to unsigned char before calling +// isspace(), etc. + +inline bool IsAlpha(char ch) { + return isalpha(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsAlNum(char ch) { + return isalnum(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsDigit(char ch) { + return isdigit(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsLower(char ch) { + return islower(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsSpace(char ch) { + return isspace(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsUpper(char ch) { + return isupper(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsXDigit(char ch) { + return isxdigit(static_cast<unsigned char>(ch)) != 0; +} +#ifdef __cpp_char8_t +inline bool IsXDigit(char8_t ch) { + return isxdigit(static_cast<unsigned char>(ch)) != 0; +} +#endif +inline bool IsXDigit(char16_t ch) { + const unsigned char low_byte = static_cast<unsigned char>(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} +inline bool IsXDigit(char32_t ch) { + const unsigned char low_byte = static_cast<unsigned char>(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} +inline bool IsXDigit(wchar_t ch) { + const unsigned char low_byte = static_cast<unsigned char>(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} + +inline char ToLower(char ch) { + return static_cast<char>(tolower(static_cast<unsigned char>(ch))); +} +inline char ToUpper(char ch) { + return static_cast<char>(toupper(static_cast<unsigned char>(ch))); +} + +inline std::string StripTrailingSpaces(std::string str) { + std::string::iterator it = str.end(); + while (it != str.begin() && IsSpace(*--it)) + it = str.erase(it); + return str; +} + +// The testing::internal::posix namespace holds wrappers for common +// POSIX functions. These wrappers hide the differences between +// Windows/MSVC and POSIX systems. Since some compilers define these +// standard functions as macros, the wrapper cannot have the same name +// as the wrapped function. + +namespace posix { + +// Functions with a different name on Windows. + +#if GTEST_OS_WINDOWS + +typedef struct _stat StatStruct; + +# ifdef __BORLANDC__ +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +# else // !__BORLANDC__ +# if GTEST_OS_WINDOWS_MOBILE +inline int DoIsATTY(int /* fd */) { return 0; } +# else +inline int DoIsATTY(int fd) { return _isatty(fd); } +# endif // GTEST_OS_WINDOWS_MOBILE +inline int StrCaseCmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return _strdup(src); } +# endif // __BORLANDC__ + +# if GTEST_OS_WINDOWS_MOBILE +inline int FileNo(FILE* file) { return reinterpret_cast<int>(_fileno(file)); } +// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this +// time and thus not defined there. +# else +inline int FileNo(FILE* file) { return _fileno(file); } +inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } +inline int RmDir(const char* dir) { return _rmdir(dir); } +inline bool IsDir(const StatStruct& st) { + return (_S_IFDIR & st.st_mode) != 0; +} +# endif // GTEST_OS_WINDOWS_MOBILE + +#elif GTEST_OS_ESP8266 +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int Stat(const char* path, StatStruct* buf) { + // stat function not implemented on ESP8266 + return 0; +} +inline int StrCaseCmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +inline int RmDir(const char* dir) { return rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } + +#else + +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +inline int RmDir(const char* dir) { return rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } + +#endif // GTEST_OS_WINDOWS + +inline int IsATTY(int fd) { + // DoIsATTY might change errno (for example ENOTTY in case you redirect stdout + // to a file on Linux), which is unexpected, so save the previous value, and + // restore it after the call. + int savedErrno = errno; + int isAttyValue = DoIsATTY(fd); + errno = savedErrno; + + return isAttyValue; +} + +// Functions deprecated by MSVC 8.0. + +GTEST_DISABLE_MSC_DEPRECATED_PUSH_() + +// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and +// StrError() aren't needed on Windows CE at this time and thus not +// defined there. + +#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && \ + !GTEST_OS_WINDOWS_RT && !GTEST_OS_ESP8266 && !GTEST_OS_XTENSA +inline int ChDir(const char* dir) { return chdir(dir); } +#endif +inline FILE* FOpen(const char* path, const char* mode) { +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW + struct wchar_codecvt : public std::codecvt<wchar_t, char, std::mbstate_t> {}; + std::wstring_convert<wchar_codecvt> converter; + std::wstring wide_path = converter.from_bytes(path); + std::wstring wide_mode = converter.from_bytes(mode); + return _wfopen(wide_path.c_str(), wide_mode.c_str()); +#else // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW + return fopen(path, mode); +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW +} +#if !GTEST_OS_WINDOWS_MOBILE +inline FILE *FReopen(const char* path, const char* mode, FILE* stream) { + return freopen(path, mode, stream); +} +inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); } +#endif +inline int FClose(FILE* fp) { return fclose(fp); } +#if !GTEST_OS_WINDOWS_MOBILE +inline int Read(int fd, void* buf, unsigned int count) { + return static_cast<int>(read(fd, buf, count)); +} +inline int Write(int fd, const void* buf, unsigned int count) { + return static_cast<int>(write(fd, buf, count)); +} +inline int Close(int fd) { return close(fd); } +inline const char* StrError(int errnum) { return strerror(errnum); } +#endif +inline const char* GetEnv(const char* name) { +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \ + GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_XTENSA + // We are on an embedded platform, which has no environment variables. + static_cast<void>(name); // To prevent 'unused argument' warning. + return nullptr; +#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) + // Environment variables which we programmatically clear will be set to the + // empty string rather than unset (NULL). Handle that case. + const char* const env = getenv(name); + return (env != nullptr && env[0] != '\0') ? env : nullptr; +#else + return getenv(name); +#endif +} + +GTEST_DISABLE_MSC_DEPRECATED_POP_() + +#if GTEST_OS_WINDOWS_MOBILE +// Windows CE has no C library. The abort() function is used in +// several places in Google Test. This implementation provides a reasonable +// imitation of standard behaviour. +[[noreturn]] void Abort(); +#else +[[noreturn]] inline void Abort() { abort(); } +#endif // GTEST_OS_WINDOWS_MOBILE + +} // namespace posix + +// MSVC "deprecates" snprintf and issues warnings wherever it is used. In +// order to avoid these warnings, we need to use _snprintf or _snprintf_s on +// MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate +// function in order to achieve that. We use macro definition here because +// snprintf is a variadic function. +#if _MSC_VER && !GTEST_OS_WINDOWS_MOBILE +// MSVC 2005 and above support variadic macros. +# define GTEST_SNPRINTF_(buffer, size, format, ...) \ + _snprintf_s(buffer, size, size, format, __VA_ARGS__) +#elif defined(_MSC_VER) +// Windows CE does not define _snprintf_s +# define GTEST_SNPRINTF_ _snprintf +#else +# define GTEST_SNPRINTF_ snprintf +#endif + +// The biggest signed integer type the compiler supports. +// +// long long is guaranteed to be at least 64-bits in C++11. +using BiggestInt = long long; // NOLINT + +// The maximum number a BiggestInt can represent. +constexpr BiggestInt kMaxBiggestInt = (std::numeric_limits<BiggestInt>::max)(); + +// This template class serves as a compile-time function from size to +// type. It maps a size in bytes to a primitive type with that +// size. e.g. +// +// TypeWithSize<4>::UInt +// +// is typedef-ed to be unsigned int (unsigned integer made up of 4 +// bytes). +// +// Such functionality should belong to STL, but I cannot find it +// there. +// +// Google Test uses this class in the implementation of floating-point +// comparison. +// +// For now it only handles UInt (unsigned int) as that's all Google Test +// needs. Other types can be easily added in the future if need +// arises. +template <size_t size> +class TypeWithSize { + public: + // This prevents the user from using TypeWithSize<N> with incorrect + // values of N. + using UInt = void; +}; + +// The specialization for size 4. +template <> +class TypeWithSize<4> { + public: + using Int = std::int32_t; + using UInt = std::uint32_t; +}; + +// The specialization for size 8. +template <> +class TypeWithSize<8> { + public: + using Int = std::int64_t; + using UInt = std::uint64_t; +}; + +// Integer types of known sizes. +using TimeInMillis = int64_t; // Represents time in milliseconds. + +// Utilities for command line flags and environment variables. + +// Macro for referencing flags. +#if !defined(GTEST_FLAG) +# define GTEST_FLAG(name) FLAGS_gtest_##name +#endif // !defined(GTEST_FLAG) + +#if !defined(GTEST_USE_OWN_FLAGFILE_FLAG_) +# define GTEST_USE_OWN_FLAGFILE_FLAG_ 1 +#endif // !defined(GTEST_USE_OWN_FLAGFILE_FLAG_) + +#if !defined(GTEST_DECLARE_bool_) +# define GTEST_FLAG_SAVER_ ::testing::internal::GTestFlagSaver + +// Macros for declaring flags. +# define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name) +# define GTEST_DECLARE_int32_(name) \ + GTEST_API_ extern std::int32_t GTEST_FLAG(name) +# define GTEST_DECLARE_string_(name) \ + GTEST_API_ extern ::std::string GTEST_FLAG(name) + +// Macros for defining flags. +# define GTEST_DEFINE_bool_(name, default_val, doc) \ + GTEST_API_ bool GTEST_FLAG(name) = (default_val) +# define GTEST_DEFINE_int32_(name, default_val, doc) \ + GTEST_API_ std::int32_t GTEST_FLAG(name) = (default_val) +# define GTEST_DEFINE_string_(name, default_val, doc) \ + GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val) + +#endif // !defined(GTEST_DECLARE_bool_) + +// Thread annotations +#if !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_) +# define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks) +# define GTEST_LOCK_EXCLUDED_(locks) +#endif // !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_) + +// Parses 'str' for a 32-bit signed integer. If successful, writes the result +// to *value and returns true; otherwise leaves *value unchanged and returns +// false. +GTEST_API_ bool ParseInt32(const Message& src_text, const char* str, + int32_t* value); + +// Parses a bool/int32_t/string from the environment variable +// corresponding to the given Google Test flag. +bool BoolFromGTestEnv(const char* flag, bool default_val); +GTEST_API_ int32_t Int32FromGTestEnv(const char* flag, int32_t default_val); +std::string OutputFlagAlsoCheckEnvVar(); +const char* StringFromGTestEnv(const char* flag, const char* default_val); + +} // namespace internal +} // namespace testing + +#if !defined(GTEST_INTERNAL_DEPRECATED) + +// Internal Macro to mark an API deprecated, for googletest usage only +// Usage: class GTEST_INTERNAL_DEPRECATED(message) MyClass or +// GTEST_INTERNAL_DEPRECATED(message) <return_type> myFunction(); Every usage of +// a deprecated entity will trigger a warning when compiled with +// `-Wdeprecated-declarations` option (clang, gcc, any __GNUC__ compiler). +// For msvc /W3 option will need to be used +// Note that for 'other' compilers this macro evaluates to nothing to prevent +// compilations errors. +#if defined(_MSC_VER) +#define GTEST_INTERNAL_DEPRECATED(message) __declspec(deprecated(message)) +#elif defined(__GNUC__) +#define GTEST_INTERNAL_DEPRECATED(message) __attribute__((deprecated(message))) +#else +#define GTEST_INTERNAL_DEPRECATED(message) +#endif + +#endif // !defined(GTEST_INTERNAL_DEPRECATED) + +#if GTEST_HAS_ABSL +// Always use absl::any for UniversalPrinter<> specializations if googletest +// is built with absl support. +#define GTEST_INTERNAL_HAS_ANY 1 +#include "absl/types/any.h" +namespace testing { +namespace internal { +using Any = ::absl::any; +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include(<any>) && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::any for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_ANY 1 +#include <any> +namespace testing { +namespace internal { +using Any = ::std::any; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::any is not +// supported. +#endif // __has_include(<any>) && __cplusplus >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#if GTEST_HAS_ABSL +// Always use absl::optional for UniversalPrinter<> specializations if +// googletest is built with absl support. +#define GTEST_INTERNAL_HAS_OPTIONAL 1 +#include "absl/types/optional.h" +namespace testing { +namespace internal { +template <typename T> +using Optional = ::absl::optional<T>; +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include(<optional>) && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::optional for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_OPTIONAL 1 +#include <optional> +namespace testing { +namespace internal { +template <typename T> +using Optional = ::std::optional<T>; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::optional is not +// supported. +#endif // __has_include(<optional>) && __cplusplus >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#if GTEST_HAS_ABSL +// Always use absl::string_view for Matcher<> specializations if googletest +// is built with absl support. +# define GTEST_INTERNAL_HAS_STRING_VIEW 1 +#include "absl/strings/string_view.h" +namespace testing { +namespace internal { +using StringView = ::absl::string_view; +} // namespace internal +} // namespace testing +#else +# ifdef __has_include +# if __has_include(<string_view>) && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::string_view for Matcher<> +// specializations. +# define GTEST_INTERNAL_HAS_STRING_VIEW 1 +#include <string_view> +namespace testing { +namespace internal { +using StringView = ::std::string_view; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::string_view is not +// supported. +# endif // __has_include(<string_view>) && __cplusplus >= 201703L +# endif // __has_include +#endif // GTEST_HAS_ABSL + +#if GTEST_HAS_ABSL +// Always use absl::variant for UniversalPrinter<> specializations if googletest +// is built with absl support. +#define GTEST_INTERNAL_HAS_VARIANT 1 +#include "absl/types/variant.h" +namespace testing { +namespace internal { +template <typename... T> +using Variant = ::absl::variant<T...>; +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include(<variant>) && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::variant for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_VARIANT 1 +#include <variant> +namespace testing { +namespace internal { +template <typename... T> +using Variant = ::std::variant<T...>; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::variant is not supported. +#endif // __has_include(<variant>) && __cplusplus >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +#if GTEST_OS_LINUX +# include <stdlib.h> +# include <sys/types.h> +# include <sys/wait.h> +# include <unistd.h> +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include <stdexcept> +#endif + +#include <ctype.h> +#include <float.h> +#include <string.h> +#include <cstdint> +#include <iomanip> +#include <limits> +#include <map> +#include <set> +#include <string> +#include <type_traits> +#include <vector> + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the Message class. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ + +#include <limits> +#include <memory> +#include <sstream> + + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// Ensures that there is at least one operator<< in the global namespace. +// See Message& operator<<(...) below for why. +void operator<<(const testing::internal::Secret&, int); + +namespace testing { + +// The Message class works like an ostream repeater. +// +// Typical usage: +// +// 1. You stream a bunch of values to a Message object. +// It will remember the text in a stringstream. +// 2. Then you stream the Message object to an ostream. +// This causes the text in the Message to be streamed +// to the ostream. +// +// For example; +// +// testing::Message foo; +// foo << 1 << " != " << 2; +// std::cout << foo; +// +// will print "1 != 2". +// +// Message is not intended to be inherited from. In particular, its +// destructor is not virtual. +// +// Note that stringstream behaves differently in gcc and in MSVC. You +// can stream a NULL char pointer to it in the former, but not in the +// latter (it causes an access violation if you do). The Message +// class hides this difference by treating a NULL char pointer as +// "(null)". +class GTEST_API_ Message { + private: + // The type of basic IO manipulators (endl, ends, and flush) for + // narrow streams. + typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); + + public: + // Constructs an empty Message. + Message(); + + // Copy constructor. + Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT + *ss_ << msg.GetString(); + } + + // Constructs a Message from a C-string. + explicit Message(const char* str) : ss_(new ::std::stringstream) { + *ss_ << str; + } + + // Streams a non-pointer value to this object. + template <typename T> + inline Message& operator <<(const T& val) { + // Some libraries overload << for STL containers. These + // overloads are defined in the global namespace instead of ::std. + // + // C++'s symbol lookup rule (i.e. Koenig lookup) says that these + // overloads are visible in either the std namespace or the global + // namespace, but not other namespaces, including the testing + // namespace which Google Test's Message class is in. + // + // To allow STL containers (and other types that has a << operator + // defined in the global namespace) to be used in Google Test + // assertions, testing::Message must access the custom << operator + // from the global namespace. With this using declaration, + // overloads of << defined in the global namespace and those + // visible via Koenig lookup are both exposed in this function. + using ::operator <<; + *ss_ << val; + return *this; + } + + // Streams a pointer value to this object. + // + // This function is an overload of the previous one. When you + // stream a pointer to a Message, this definition will be used as it + // is more specialized. (The C++ Standard, section + // [temp.func.order].) If you stream a non-pointer, then the + // previous definition will be used. + // + // The reason for this overload is that streaming a NULL pointer to + // ostream is undefined behavior. Depending on the compiler, you + // may get "0", "(nil)", "(null)", or an access violation. To + // ensure consistent result across compilers, we always treat NULL + // as "(null)". + template <typename T> + inline Message& operator <<(T* const& pointer) { // NOLINT + if (pointer == nullptr) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + return *this; + } + + // Since the basic IO manipulators are overloaded for both narrow + // and wide streams, we have to provide this specialized definition + // of operator <<, even though its body is the same as the + // templatized version above. Without this definition, streaming + // endl or other basic IO manipulators to Message will confuse the + // compiler. + Message& operator <<(BasicNarrowIoManip val) { + *ss_ << val; + return *this; + } + + // Instead of 1/0, we want to see true/false for bool values. + Message& operator <<(bool b) { + return *this << (b ? "true" : "false"); + } + + // These two overloads allow streaming a wide C string to a Message + // using the UTF-8 encoding. + Message& operator <<(const wchar_t* wide_c_str); + Message& operator <<(wchar_t* wide_c_str); + +#if GTEST_HAS_STD_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::std::wstring& wstr); +#endif // GTEST_HAS_STD_WSTRING + + // Gets the text streamed to this object so far as an std::string. + // Each '\0' character in the buffer is replaced with "\\0". + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + std::string GetString() const; + + private: + // We'll hold the text streamed to this object here. + const std::unique_ptr< ::std::stringstream> ss_; + + // We declare (but don't implement) this to prevent the compiler + // from implementing the assignment operator. + void operator=(const Message&); +}; + +// Streams a Message to an ostream. +inline std::ostream& operator <<(std::ostream& os, const Message& sb) { + return os << sb.GetString(); +} + +namespace internal { + +// Converts a streamable value to an std::string. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +template <typename T> +std::string StreamableToString(const T& streamable) { + return (Message() << streamable).GetString(); +} + +} // namespace internal +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. +// +// Google Test filepath utilities +// +// This header file declares classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included in gtest/internal/gtest-internal.h. +// Do not include this header file separately! + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file declares the String class and functions used internally by +// Google Test. They are subject to change without notice. They should not used +// by code external to Google Test. +// +// This header file is #included by gtest-internal.h. +// It should not be #included by other files. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +#ifdef __BORLANDC__ +// string.h is not guaranteed to provide strcpy on C++ Builder. +# include <mem.h> +#endif + +#include <string.h> +#include <cstdint> +#include <string> + + +namespace testing { +namespace internal { + +// String - an abstract class holding static string utilities. +class GTEST_API_ String { + public: + // Static utility methods + + // Clones a 0-terminated C string, allocating memory using new. The + // caller is responsible for deleting the return value using + // delete[]. Returns the cloned string, or NULL if the input is + // NULL. + // + // This is different from strdup() in string.h, which allocates + // memory using malloc(). + static const char* CloneCString(const char* c_str); + +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be + // able to pass strings to Win32 APIs on CE we need to convert them + // to 'Unicode', UTF-16. + + // Creates a UTF-16 wide string from the given ANSI string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the wide string, or NULL if the + // input is NULL. + // + // The wide string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static LPCWSTR AnsiToUtf16(const char* c_str); + + // Creates an ANSI string from the given wide string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the ANSI string, or NULL if the + // input is NULL. + // + // The returned string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static const char* Utf16ToAnsi(LPCWSTR utf16_str); +#endif + + // Compares two C strings. Returns true if and only if they have the same + // content. + // + // Unlike strcmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CStringEquals(const char* lhs, const char* rhs); + + // Converts a wide C string to a String using the UTF-8 encoding. + // NULL will be converted to "(null)". If an error occurred during + // the conversion, "(failed to convert from wide string)" is + // returned. + static std::string ShowWideCString(const wchar_t* wide_c_str); + + // Compares two wide C strings. Returns true if and only if they have the + // same content. + // + // Unlike wcscmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); + + // Compares two C strings, ignoring case. Returns true if and only if + // they have the same content. + // + // Unlike strcasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CaseInsensitiveCStringEquals(const char* lhs, + const char* rhs); + + // Compares two wide C strings, ignoring case. Returns true if and only if + // they have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. + static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs); + + // Returns true if and only if the given string ends with the given suffix, + // ignoring case. Any string is considered to end with an empty suffix. + static bool EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix); + + // Formats an int value as "%02d". + static std::string FormatIntWidth2(int value); // "%02d" for width == 2 + + // Formats an int value to given width with leading zeros. + static std::string FormatIntWidthN(int value, int width); + + // Formats an int value as "%X". + static std::string FormatHexInt(int value); + + // Formats an int value as "%X". + static std::string FormatHexUInt32(uint32_t value); + + // Formats a byte as "%02X". + static std::string FormatByte(unsigned char value); + + private: + String(); // Not meant to be instantiated. +}; // class String + +// Gets the content of the stringstream's buffer as an std::string. Each '\0' +// character in the buffer is replaced with "\\0". +GTEST_API_ std::string StringStreamToString(::std::stringstream* stream); + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { +namespace internal { + +// FilePath - a class for file and directory pathname manipulation which +// handles platform-specific conventions (like the pathname separator). +// Used for helper functions for naming files in a directory for xml output. +// Except for Set methods, all methods are const or static, which provides an +// "immutable value object" -- useful for peace of mind. +// A FilePath with a value ending in a path separator ("like/this/") represents +// a directory, otherwise it is assumed to represent a file. In either case, +// it may or may not represent an actual file or directory in the file system. +// Names are NOT checked for syntax correctness -- no checking for illegal +// characters, malformed paths, etc. + +class GTEST_API_ FilePath { + public: + FilePath() : pathname_("") { } + FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { } + + explicit FilePath(const std::string& pathname) : pathname_(pathname) { + Normalize(); + } + + FilePath& operator=(const FilePath& rhs) { + Set(rhs); + return *this; + } + + void Set(const FilePath& rhs) { + pathname_ = rhs.pathname_; + } + + const std::string& string() const { return pathname_; } + const char* c_str() const { return pathname_.c_str(); } + + // Returns the current working directory, or "" if unsuccessful. + static FilePath GetCurrentDir(); + + // Given directory = "dir", base_name = "test", number = 0, + // extension = "xml", returns "dir/test.xml". If number is greater + // than zero (e.g., 12), returns "dir/test_12.xml". + // On Windows platform, uses \ as the separator rather than /. + static FilePath MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension); + + // Given directory = "dir", relative_path = "test.xml", + // returns "dir/test.xml". + // On Windows, uses \ as the separator rather than /. + static FilePath ConcatPaths(const FilePath& directory, + const FilePath& relative_path); + + // Returns a pathname for a file that does not currently exist. The pathname + // will be directory/base_name.extension or + // directory/base_name_<number>.extension if directory/base_name.extension + // already exists. The number will be incremented until a pathname is found + // that does not already exist. + // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. + // There could be a race condition if two or more processes are calling this + // function at the same time -- they could both pick the same filename. + static FilePath GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension); + + // Returns true if and only if the path is "". + bool IsEmpty() const { return pathname_.empty(); } + + // If input name has a trailing separator character, removes it and returns + // the name, otherwise return the name string unmodified. + // On Windows platform, uses \ as the separator, other platforms use /. + FilePath RemoveTrailingPathSeparator() const; + + // Returns a copy of the FilePath with the directory part removed. + // Example: FilePath("path/to/file").RemoveDirectoryName() returns + // FilePath("file"). If there is no directory part ("just_a_file"), it returns + // the FilePath unmodified. If there is no file part ("just_a_dir/") it + // returns an empty FilePath (""). + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveDirectoryName() const; + + // RemoveFileName returns the directory path with the filename removed. + // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". + // If the FilePath is "a_file" or "/a_file", RemoveFileName returns + // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does + // not have a file, like "just/a/dir/", it returns the FilePath unmodified. + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveFileName() const; + + // Returns a copy of the FilePath with the case-insensitive extension removed. + // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns + // FilePath("dir/file"). If a case-insensitive extension is not + // found, returns a copy of the original FilePath. + FilePath RemoveExtension(const char* extension) const; + + // Creates directories so that path exists. Returns true if successful or if + // the directories already exist; returns false if unable to create + // directories for any reason. Will also return false if the FilePath does + // not represent a directory (that is, it doesn't end with a path separator). + bool CreateDirectoriesRecursively() const; + + // Create the directory so that path exists. Returns true if successful or + // if the directory already exists; returns false if unable to create the + // directory for any reason, including if the parent directory does not + // exist. Not named "CreateDirectory" because that's a macro on Windows. + bool CreateFolder() const; + + // Returns true if FilePath describes something in the file-system, + // either a file, directory, or whatever, and that something exists. + bool FileOrDirectoryExists() const; + + // Returns true if pathname describes a directory in the file-system + // that exists. + bool DirectoryExists() const; + + // Returns true if FilePath ends with a path separator, which indicates that + // it is intended to represent a directory. Returns false otherwise. + // This does NOT check that a directory (or file) actually exists. + bool IsDirectory() const; + + // Returns true if pathname describes a root directory. (Windows has one + // root directory per disk drive.) + bool IsRootDirectory() const; + + // Returns true if pathname describes an absolute path. + bool IsAbsolutePath() const; + + private: + // Replaces multiple consecutive separators with a single separator. + // For example, "bar///foo" becomes "bar/foo". Does not eliminate other + // redundancies that might be in a pathname involving "." or "..". + // + // A pathname with multiple consecutive separators may occur either through + // user error or as a result of some scripts or APIs that generate a pathname + // with a trailing separator. On other platforms the same API or script + // may NOT generate a pathname with a trailing "/". Then elsewhere that + // pathname may have another "/" and pathname components added to it, + // without checking for the separator already being there. + // The script language and operating system may allow paths like "foo//bar" + // but some of the functions in FilePath will not handle that correctly. In + // particular, RemoveTrailingPathSeparator() only removes one separator, and + // it is called in CreateDirectoriesRecursively() assuming that it will change + // a pathname from directory syntax (trailing separator) to filename syntax. + // + // On Windows this method also replaces the alternate path separator '/' with + // the primary path separator '\\', so that for example "bar\\/\\foo" becomes + // "bar\\foo". + + void Normalize(); + + // Returns a pointer to the last occurrence of a valid path separator in + // the FilePath. On Windows, for example, both '/' and '\' are valid path + // separators. Returns NULL if no path separator was found. + const char* FindLastPathSeparator() const; + + std::string pathname_; +}; // class FilePath + +} // namespace internal +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// Type utilities needed for implementing typed and type-parameterized +// tests. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + + +// #ifdef __GNUC__ is too general here. It is possible to use gcc without using +// libstdc++ (which is where cxxabi.h comes from). +# if GTEST_HAS_CXXABI_H_ +# include <cxxabi.h> +# elif defined(__HP_aCC) +# include <acxx_demangle.h> +# endif // GTEST_HASH_CXXABI_H_ + +namespace testing { +namespace internal { + +// Canonicalizes a given name with respect to the Standard C++ Library. +// This handles removing the inline namespace within `std` that is +// used by various standard libraries (e.g., `std::__1`). Names outside +// of namespace std are returned unmodified. +inline std::string CanonicalizeForStdLibVersioning(std::string s) { + static const char prefix[] = "std::__"; + if (s.compare(0, strlen(prefix), prefix) == 0) { + std::string::size_type end = s.find("::", strlen(prefix)); + if (end != s.npos) { + // Erase everything between the initial `std` and the second `::`. + s.erase(strlen("std"), end - strlen("std")); + } + } + return s; +} + +#if GTEST_HAS_RTTI +// GetTypeName(const std::type_info&) returns a human-readable name of type T. +inline std::string GetTypeName(const std::type_info& type) { + const char* const name = type.name(); +#if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) + int status = 0; + // gcc's implementation of typeid(T).name() mangles the type name, + // so we have to demangle it. +#if GTEST_HAS_CXXABI_H_ + using abi::__cxa_demangle; +#endif // GTEST_HAS_CXXABI_H_ + char* const readable_name = __cxa_demangle(name, nullptr, nullptr, &status); + const std::string name_str(status == 0 ? readable_name : name); + free(readable_name); + return CanonicalizeForStdLibVersioning(name_str); +#else + return name; +#endif // GTEST_HAS_CXXABI_H_ || __HP_aCC +} +#endif // GTEST_HAS_RTTI + +// GetTypeName<T>() returns a human-readable name of type T if and only if +// RTTI is enabled, otherwise it returns a dummy type name. +// NB: This function is also used in Google Mock, so don't move it inside of +// the typed-test-only section below. +template <typename T> +std::string GetTypeName() { +#if GTEST_HAS_RTTI + return GetTypeName(typeid(T)); +#else + return "<type>"; +#endif // GTEST_HAS_RTTI +} + +// A unique type indicating an empty node +struct None {}; + +# define GTEST_TEMPLATE_ template <typename T> class + +// The template "selector" struct TemplateSel<Tmpl> is used to +// represent Tmpl, which must be a class template with one type +// parameter, as a type. TemplateSel<Tmpl>::Bind<T>::type is defined +// as the type Tmpl<T>. This allows us to actually instantiate the +// template "selected" by TemplateSel<Tmpl>. +// +// This trick is necessary for simulating typedef for class templates, +// which C++ doesn't support directly. +template <GTEST_TEMPLATE_ Tmpl> +struct TemplateSel { + template <typename T> + struct Bind { + typedef Tmpl<T> type; + }; +}; + +# define GTEST_BIND_(TmplSel, T) \ + TmplSel::template Bind<T>::type + +template <GTEST_TEMPLATE_ Head_, GTEST_TEMPLATE_... Tail_> +struct Templates { + using Head = TemplateSel<Head_>; + using Tail = Templates<Tail_...>; +}; + +template <GTEST_TEMPLATE_ Head_> +struct Templates<Head_> { + using Head = TemplateSel<Head_>; + using Tail = None; +}; + +// Tuple-like type lists +template <typename Head_, typename... Tail_> +struct Types { + using Head = Head_; + using Tail = Types<Tail_...>; +}; + +template <typename Head_> +struct Types<Head_> { + using Head = Head_; + using Tail = None; +}; + +// Helper metafunctions to tell apart a single type from types +// generated by ::testing::Types +template <typename... Ts> +struct ProxyTypeList { + using type = Types<Ts...>; +}; + +template <typename> +struct is_proxy_type_list : std::false_type {}; + +template <typename... Ts> +struct is_proxy_type_list<ProxyTypeList<Ts...>> : std::true_type {}; + +// Generator which conditionally creates type lists. +// It recognizes if a requested type list should be created +// and prevents creating a new type list nested within another one. +template <typename T> +struct GenerateTypeList { + private: + using proxy = typename std::conditional<is_proxy_type_list<T>::value, T, + ProxyTypeList<T>>::type; + + public: + using type = typename proxy::type; +}; + +} // namespace internal + +template <typename... Ts> +using Types = internal::ProxyTypeList<Ts...>; + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + +// Due to C++ preprocessor weirdness, we need double indirection to +// concatenate two tokens when one of them is __LINE__. Writing +// +// foo ## __LINE__ +// +// will result in the token foo__LINE__, instead of foo followed by +// the current line number. For more details, see +// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 +#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) +#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar + +// Stringifies its argument. +// Work around a bug in visual studio which doesn't accept code like this: +// +// #define GTEST_STRINGIFY_(name) #name +// #define MACRO(a, b, c) ... GTEST_STRINGIFY_(a) ... +// MACRO(, x, y) +// +// Complaining about the argument to GTEST_STRINGIFY_ being empty. +// This is allowed by the spec. +#define GTEST_STRINGIFY_HELPER_(name, ...) #name +#define GTEST_STRINGIFY_(...) GTEST_STRINGIFY_HELPER_(__VA_ARGS__, ) + +namespace proto2 { +class MessageLite; +} + +namespace testing { + +// Forward declarations. + +class AssertionResult; // Result of an assertion. +class Message; // Represents a failure message. +class Test; // Represents a test. +class TestInfo; // Information about a test. +class TestPartResult; // Result of a test part. +class UnitTest; // A collection of test suites. + +template <typename T> +::std::string PrintToString(const T& value); + +namespace internal { + +struct TraceInfo; // Information about a trace point. +class TestInfoImpl; // Opaque implementation of TestInfo +class UnitTestImpl; // Opaque implementation of UnitTest + +// The text used in failure messages to indicate the start of the +// stack trace. +GTEST_API_ extern const char kStackTraceMarker[]; + +// An IgnoredValue object can be implicitly constructed from ANY value. +class IgnoredValue { + struct Sink {}; + public: + // This constructor template allows any value to be implicitly + // converted to IgnoredValue. The object has no data member and + // doesn't try to remember anything about the argument. We + // deliberately omit the 'explicit' keyword in order to allow the + // conversion to be implicit. + // Disable the conversion if T already has a magical conversion operator. + // Otherwise we get ambiguity. + template <typename T, + typename std::enable_if<!std::is_convertible<T, Sink>::value, + int>::type = 0> + IgnoredValue(const T& /* ignored */) {} // NOLINT(runtime/explicit) +}; + +// Appends the user-supplied message to the Google-Test-generated message. +GTEST_API_ std::string AppendUserMessage( + const std::string& gtest_msg, const Message& user_msg); + +#if GTEST_HAS_EXCEPTIONS + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4275 \ +/* an exported class was derived from a class that was not exported */) + +// This exception is thrown by (and only by) a failed Google Test +// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions +// are enabled). We derive it from std::runtime_error, which is for +// errors presumably detectable only at run time. Since +// std::runtime_error inherits from std::exception, many testing +// frameworks know how to extract and print the message inside it. +class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error { + public: + explicit GoogleTestFailureException(const TestPartResult& failure); +}; + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4275 + +#endif // GTEST_HAS_EXCEPTIONS + +namespace edit_distance { +// Returns the optimal edits to go from 'left' to 'right'. +// All edits cost the same, with replace having lower priority than +// add/remove. +// Simple implementation of the Wagner-Fischer algorithm. +// See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm +enum EditType { kMatch, kAdd, kRemove, kReplace }; +GTEST_API_ std::vector<EditType> CalculateOptimalEdits( + const std::vector<size_t>& left, const std::vector<size_t>& right); + +// Same as above, but the input is represented as strings. +GTEST_API_ std::vector<EditType> CalculateOptimalEdits( + const std::vector<std::string>& left, + const std::vector<std::string>& right); + +// Create a diff of the input strings in Unified diff format. +GTEST_API_ std::string CreateUnifiedDiff(const std::vector<std::string>& left, + const std::vector<std::string>& right, + size_t context = 2); + +} // namespace edit_distance + +// Calculate the diff between 'left' and 'right' and return it in unified diff +// format. +// If not null, stores in 'total_line_count' the total number of lines found +// in left + right. +GTEST_API_ std::string DiffStrings(const std::string& left, + const std::string& right, + size_t* total_line_count); + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true if and only if the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +GTEST_API_ AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const std::string& expected_value, + const std::string& actual_value, + bool ignoring_case); + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +GTEST_API_ std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value); + +// This template class represents an IEEE floating-point number +// (either single-precision or double-precision, depending on the +// template parameters). +// +// The purpose of this class is to do more sophisticated number +// comparison. (Due to round-off error, etc, it's very unlikely that +// two floating-points will be equal exactly. Hence a naive +// comparison by the == operation often doesn't work.) +// +// Format of IEEE floating-point: +// +// The most-significant bit being the leftmost, an IEEE +// floating-point looks like +// +// sign_bit exponent_bits fraction_bits +// +// Here, sign_bit is a single bit that designates the sign of the +// number. +// +// For float, there are 8 exponent bits and 23 fraction bits. +// +// For double, there are 11 exponent bits and 52 fraction bits. +// +// More details can be found at +// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +template <typename RawType> +class FloatingPoint { + public: + // Defines the unsigned integer type that has the same size as the + // floating point number. + typedef typename TypeWithSize<sizeof(RawType)>::UInt Bits; + + // Constants. + + // # of bits in a number. + static const size_t kBitCount = 8*sizeof(RawType); + + // # of fraction bits in a number. + static const size_t kFractionBitCount = + std::numeric_limits<RawType>::digits - 1; + + // # of exponent bits in a number. + static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; + + // The mask for the sign bit. + static const Bits kSignBitMask = static_cast<Bits>(1) << (kBitCount - 1); + + // The mask for the fraction bits. + static const Bits kFractionBitMask = + ~static_cast<Bits>(0) >> (kExponentBitCount + 1); + + // The mask for the exponent bits. + static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); + + // How many ULP's (Units in the Last Place) we want to tolerate when + // comparing two numbers. The larger the value, the more error we + // allow. A 0 value means that two numbers must be exactly the same + // to be considered equal. + // + // The maximum error of a single floating-point operation is 0.5 + // units in the last place. On Intel CPU's, all floating-point + // calculations are done with 80-bit precision, while double has 64 + // bits. Therefore, 4 should be enough for ordinary use. + // + // See the following article for more details on ULP: + // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + static const uint32_t kMaxUlps = 4; + + // Constructs a FloatingPoint from a raw floating-point number. + // + // On an Intel CPU, passing a non-normalized NAN (Not a Number) + // around may change its bits, although the new value is guaranteed + // to be also a NAN. Therefore, don't expect this constructor to + // preserve the bits in x when x is a NAN. + explicit FloatingPoint(const RawType& x) { u_.value_ = x; } + + // Static methods + + // Reinterprets a bit pattern as a floating-point number. + // + // This function is needed to test the AlmostEquals() method. + static RawType ReinterpretBits(const Bits bits) { + FloatingPoint fp(0); + fp.u_.bits_ = bits; + return fp.u_.value_; + } + + // Returns the floating-point number that represent positive infinity. + static RawType Infinity() { + return ReinterpretBits(kExponentBitMask); + } + + // Returns the maximum representable finite floating-point number. + static RawType Max(); + + // Non-static methods + + // Returns the bits that represents this number. + const Bits &bits() const { return u_.bits_; } + + // Returns the exponent bits of this number. + Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } + + // Returns the fraction bits of this number. + Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } + + // Returns the sign bit of this number. + Bits sign_bit() const { return kSignBitMask & u_.bits_; } + + // Returns true if and only if this is NAN (not a number). + bool is_nan() const { + // It's a NAN if the exponent bits are all ones and the fraction + // bits are not entirely zeros. + return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); + } + + // Returns true if and only if this number is at most kMaxUlps ULP's away + // from rhs. In particular, this function: + // + // - returns false if either number is (or both are) NAN. + // - treats really large numbers as almost equal to infinity. + // - thinks +0.0 and -0.0 are 0 DLP's apart. + bool AlmostEquals(const FloatingPoint& rhs) const { + // The IEEE standard says that any comparison operation involving + // a NAN must return false. + if (is_nan() || rhs.is_nan()) return false; + + return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) + <= kMaxUlps; + } + + private: + // The data type used to store the actual floating-point number. + union FloatingPointUnion { + RawType value_; // The raw floating-point number. + Bits bits_; // The bits that represent the number. + }; + + // Converts an integer from the sign-and-magnitude representation to + // the biased representation. More precisely, let N be 2 to the + // power of (kBitCount - 1), an integer x is represented by the + // unsigned number x + N. + // + // For instance, + // + // -N + 1 (the most negative number representable using + // sign-and-magnitude) is represented by 1; + // 0 is represented by N; and + // N - 1 (the biggest number representable using + // sign-and-magnitude) is represented by 2N - 1. + // + // Read http://en.wikipedia.org/wiki/Signed_number_representations + // for more details on signed number representations. + static Bits SignAndMagnitudeToBiased(const Bits &sam) { + if (kSignBitMask & sam) { + // sam represents a negative number. + return ~sam + 1; + } else { + // sam represents a positive number. + return kSignBitMask | sam; + } + } + + // Given two numbers in the sign-and-magnitude representation, + // returns the distance between them as an unsigned number. + static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1, + const Bits &sam2) { + const Bits biased1 = SignAndMagnitudeToBiased(sam1); + const Bits biased2 = SignAndMagnitudeToBiased(sam2); + return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); + } + + FloatingPointUnion u_; +}; + +// We cannot use std::numeric_limits<T>::max() as it clashes with the max() +// macro defined by <windows.h>. +template <> +inline float FloatingPoint<float>::Max() { return FLT_MAX; } +template <> +inline double FloatingPoint<double>::Max() { return DBL_MAX; } + +// Typedefs the instances of the FloatingPoint template class that we +// care to use. +typedef FloatingPoint<float> Float; +typedef FloatingPoint<double> Double; + +// In order to catch the mistake of putting tests that use different +// test fixture classes in the same test suite, we need to assign +// unique IDs to fixture classes and compare them. The TypeId type is +// used to hold such IDs. The user should treat TypeId as an opaque +// type: the only operation allowed on TypeId values is to compare +// them for equality using the == operator. +typedef const void* TypeId; + +template <typename T> +class TypeIdHelper { + public: + // dummy_ must not have a const type. Otherwise an overly eager + // compiler (e.g. MSVC 7.1 & 8.0) may try to merge + // TypeIdHelper<T>::dummy_ for different Ts as an "optimization". + static bool dummy_; +}; + +template <typename T> +bool TypeIdHelper<T>::dummy_ = false; + +// GetTypeId<T>() returns the ID of type T. Different values will be +// returned for different types. Calling the function twice with the +// same type argument is guaranteed to return the same ID. +template <typename T> +TypeId GetTypeId() { + // The compiler is required to allocate a different + // TypeIdHelper<T>::dummy_ variable for each T used to instantiate + // the template. Therefore, the address of dummy_ is guaranteed to + // be unique. + return &(TypeIdHelper<T>::dummy_); +} + +// Returns the type ID of ::testing::Test. Always call this instead +// of GetTypeId< ::testing::Test>() to get the type ID of +// ::testing::Test, as the latter may give the wrong result due to a +// suspected linker bug when compiling Google Test as a Mac OS X +// framework. +GTEST_API_ TypeId GetTestTypeId(); + +// Defines the abstract factory interface that creates instances +// of a Test object. +class TestFactoryBase { + public: + virtual ~TestFactoryBase() {} + + // Creates a test instance to run. The instance is both created and destroyed + // within TestInfoImpl::Run() + virtual Test* CreateTest() = 0; + + protected: + TestFactoryBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase); +}; + +// This class provides implementation of TeastFactoryBase interface. +// It is used in TEST and TEST_F macros. +template <class TestClass> +class TestFactoryImpl : public TestFactoryBase { + public: + Test* CreateTest() override { return new TestClass; } +}; + +#if GTEST_OS_WINDOWS + +// Predicate-formatters for implementing the HRESULT checking macros +// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} +// We pass a long instead of HRESULT to avoid causing an +// include dependency for the HRESULT type. +GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr, + long hr); // NOLINT +GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr, + long hr); // NOLINT + +#endif // GTEST_OS_WINDOWS + +// Types of SetUpTestSuite() and TearDownTestSuite() functions. +using SetUpTestSuiteFunc = void (*)(); +using TearDownTestSuiteFunc = void (*)(); + +struct CodeLocation { + CodeLocation(const std::string& a_file, int a_line) + : file(a_file), line(a_line) {} + + std::string file; + int line; +}; + +// Helper to identify which setup function for TestCase / TestSuite to call. +// Only one function is allowed, either TestCase or TestSute but not both. + +// Utility functions to help SuiteApiResolver +using SetUpTearDownSuiteFuncType = void (*)(); + +inline SetUpTearDownSuiteFuncType GetNotDefaultOrNull( + SetUpTearDownSuiteFuncType a, SetUpTearDownSuiteFuncType def) { + return a == def ? nullptr : a; +} + +template <typename T> +// Note that SuiteApiResolver inherits from T because +// SetUpTestSuite()/TearDownTestSuite() could be protected. Ths way +// SuiteApiResolver can access them. +struct SuiteApiResolver : T { + // testing::Test is only forward declared at this point. So we make it a + // dependend class for the compiler to be OK with it. + using Test = + typename std::conditional<sizeof(T) != 0, ::testing::Test, void>::type; + + static SetUpTearDownSuiteFuncType GetSetUpCaseOrSuite(const char* filename, + int line_num) { +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + SetUpTearDownSuiteFuncType test_case_fp = + GetNotDefaultOrNull(&T::SetUpTestCase, &Test::SetUpTestCase); + SetUpTearDownSuiteFuncType test_suite_fp = + GetNotDefaultOrNull(&T::SetUpTestSuite, &Test::SetUpTestSuite); + + GTEST_CHECK_(!test_case_fp || !test_suite_fp) + << "Test can not provide both SetUpTestSuite and SetUpTestCase, please " + "make sure there is only one present at " + << filename << ":" << line_num; + + return test_case_fp != nullptr ? test_case_fp : test_suite_fp; +#else + (void)(filename); + (void)(line_num); + return &T::SetUpTestSuite; +#endif + } + + static SetUpTearDownSuiteFuncType GetTearDownCaseOrSuite(const char* filename, + int line_num) { +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + SetUpTearDownSuiteFuncType test_case_fp = + GetNotDefaultOrNull(&T::TearDownTestCase, &Test::TearDownTestCase); + SetUpTearDownSuiteFuncType test_suite_fp = + GetNotDefaultOrNull(&T::TearDownTestSuite, &Test::TearDownTestSuite); + + GTEST_CHECK_(!test_case_fp || !test_suite_fp) + << "Test can not provide both TearDownTestSuite and TearDownTestCase," + " please make sure there is only one present at" + << filename << ":" << line_num; + + return test_case_fp != nullptr ? test_case_fp : test_suite_fp; +#else + (void)(filename); + (void)(line_num); + return &T::TearDownTestSuite; +#endif + } +}; + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_suite_name: name of the test suite +// name: name of the test +// type_param: the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param: text representation of the test's value parameter, +// or NULL if this is not a type-parameterized test. +// code_location: code location where the test is defined +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test suite +// tear_down_tc: pointer to the function that tears down the test suite +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +GTEST_API_ TestInfo* MakeAndRegisterTestInfo( + const char* test_suite_name, const char* name, const char* type_param, + const char* value_param, CodeLocation code_location, + TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc, + TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory); + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr); + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// State of the definition of a type-parameterized test suite. +class GTEST_API_ TypedTestSuitePState { + public: + TypedTestSuitePState() : registered_(false) {} + + // Adds the given test name to defined_test_names_ and return true + // if the test suite hasn't been registered; otherwise aborts the + // program. + bool AddTestName(const char* file, int line, const char* case_name, + const char* test_name) { + if (registered_) { + fprintf(stderr, + "%s Test %s must be defined before " + "REGISTER_TYPED_TEST_SUITE_P(%s, ...).\n", + FormatFileLocation(file, line).c_str(), test_name, case_name); + fflush(stderr); + posix::Abort(); + } + registered_tests_.insert( + ::std::make_pair(test_name, CodeLocation(file, line))); + return true; + } + + bool TestExists(const std::string& test_name) const { + return registered_tests_.count(test_name) > 0; + } + + const CodeLocation& GetCodeLocation(const std::string& test_name) const { + RegisteredTestsMap::const_iterator it = registered_tests_.find(test_name); + GTEST_CHECK_(it != registered_tests_.end()); + return it->second; + } + + // Verifies that registered_tests match the test names in + // defined_test_names_; returns registered_tests if successful, or + // aborts the program otherwise. + const char* VerifyRegisteredTestNames(const char* test_suite_name, + const char* file, int line, + const char* registered_tests); + + private: + typedef ::std::map<std::string, CodeLocation> RegisteredTestsMap; + + bool registered_; + RegisteredTestsMap registered_tests_; +}; + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +using TypedTestCasePState = TypedTestSuitePState; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// Skips to the first non-space char after the first comma in 'str'; +// returns NULL if no comma is found in 'str'. +inline const char* SkipComma(const char* str) { + const char* comma = strchr(str, ','); + if (comma == nullptr) { + return nullptr; + } + while (IsSpace(*(++comma))) {} + return comma; +} + +// Returns the prefix of 'str' before the first comma in it; returns +// the entire string if it contains no comma. +inline std::string GetPrefixUntilComma(const char* str) { + const char* comma = strchr(str, ','); + return comma == nullptr ? str : std::string(str, comma); +} + +// Splits a given string on a given delimiter, populating a given +// vector with the fields. +void SplitString(const ::std::string& str, char delimiter, + ::std::vector< ::std::string>* dest); + +// The default argument to the template below for the case when the user does +// not provide a name generator. +struct DefaultNameGenerator { + template <typename T> + static std::string GetName(int i) { + return StreamableToString(i); + } +}; + +template <typename Provided = DefaultNameGenerator> +struct NameGeneratorSelector { + typedef Provided type; +}; + +template <typename NameGenerator> +void GenerateNamesRecursively(internal::None, std::vector<std::string>*, int) {} + +template <typename NameGenerator, typename Types> +void GenerateNamesRecursively(Types, std::vector<std::string>* result, int i) { + result->push_back(NameGenerator::template GetName<typename Types::Head>(i)); + GenerateNamesRecursively<NameGenerator>(typename Types::Tail(), result, + i + 1); +} + +template <typename NameGenerator, typename Types> +std::vector<std::string> GenerateNames() { + std::vector<std::string> result; + GenerateNamesRecursively<NameGenerator>(Types(), &result, 0); + return result; +} + +// TypeParameterizedTest<Fixture, TestSel, Types>::Register() +// registers a list of type-parameterized tests with Google Test. The +// return value is insignificant - we just need to return something +// such that we can call this function in a namespace scope. +// +// Implementation note: The GTEST_TEMPLATE_ macro declares a template +// template parameter. It's defined in gtest-type-util.h. +template <GTEST_TEMPLATE_ Fixture, class TestSel, typename Types> +class TypeParameterizedTest { + public: + // 'index' is the index of the test in the type list 'Types' + // specified in INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, TestSuite, + // Types). Valid values for 'index' are [0, N - 1] where N is the + // length of Types. + static bool Register(const char* prefix, const CodeLocation& code_location, + const char* case_name, const char* test_names, int index, + const std::vector<std::string>& type_names = + GenerateNames<DefaultNameGenerator, Types>()) { + typedef typename Types::Head Type; + typedef Fixture<Type> FixtureClass; + typedef typename GTEST_BIND_(TestSel, Type) TestClass; + + // First, registers the first type-parameterized test in the type + // list. + MakeAndRegisterTestInfo( + (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + + "/" + type_names[static_cast<size_t>(index)]) + .c_str(), + StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(), + GetTypeName<Type>().c_str(), + nullptr, // No value parameter. + code_location, GetTypeId<FixtureClass>(), + SuiteApiResolver<TestClass>::GetSetUpCaseOrSuite( + code_location.file.c_str(), code_location.line), + SuiteApiResolver<TestClass>::GetTearDownCaseOrSuite( + code_location.file.c_str(), code_location.line), + new TestFactoryImpl<TestClass>); + + // Next, recurses (at compile time) with the tail of the type list. + return TypeParameterizedTest<Fixture, TestSel, + typename Types::Tail>::Register(prefix, + code_location, + case_name, + test_names, + index + 1, + type_names); + } +}; + +// The base case for the compile time recursion. +template <GTEST_TEMPLATE_ Fixture, class TestSel> +class TypeParameterizedTest<Fixture, TestSel, internal::None> { + public: + static bool Register(const char* /*prefix*/, const CodeLocation&, + const char* /*case_name*/, const char* /*test_names*/, + int /*index*/, + const std::vector<std::string>& = + std::vector<std::string>() /*type_names*/) { + return true; + } +}; + +GTEST_API_ void RegisterTypeParameterizedTestSuite(const char* test_suite_name, + CodeLocation code_location); +GTEST_API_ void RegisterTypeParameterizedTestSuiteInstantiation( + const char* case_name); + +// TypeParameterizedTestSuite<Fixture, Tests, Types>::Register() +// registers *all combinations* of 'Tests' and 'Types' with Google +// Test. The return value is insignificant - we just need to return +// something such that we can call this function in a namespace scope. +template <GTEST_TEMPLATE_ Fixture, typename Tests, typename Types> +class TypeParameterizedTestSuite { + public: + static bool Register(const char* prefix, CodeLocation code_location, + const TypedTestSuitePState* state, const char* case_name, + const char* test_names, + const std::vector<std::string>& type_names = + GenerateNames<DefaultNameGenerator, Types>()) { + RegisterTypeParameterizedTestSuiteInstantiation(case_name); + std::string test_name = StripTrailingSpaces( + GetPrefixUntilComma(test_names)); + if (!state->TestExists(test_name)) { + fprintf(stderr, "Failed to get code location for test %s.%s at %s.", + case_name, test_name.c_str(), + FormatFileLocation(code_location.file.c_str(), + code_location.line).c_str()); + fflush(stderr); + posix::Abort(); + } + const CodeLocation& test_location = state->GetCodeLocation(test_name); + + typedef typename Tests::Head Head; + + // First, register the first test in 'Test' for each type in 'Types'. + TypeParameterizedTest<Fixture, Head, Types>::Register( + prefix, test_location, case_name, test_names, 0, type_names); + + // Next, recurses (at compile time) with the tail of the test list. + return TypeParameterizedTestSuite<Fixture, typename Tests::Tail, + Types>::Register(prefix, code_location, + state, case_name, + SkipComma(test_names), + type_names); + } +}; + +// The base case for the compile time recursion. +template <GTEST_TEMPLATE_ Fixture, typename Types> +class TypeParameterizedTestSuite<Fixture, internal::None, Types> { + public: + static bool Register(const char* /*prefix*/, const CodeLocation&, + const TypedTestSuitePState* /*state*/, + const char* /*case_name*/, const char* /*test_names*/, + const std::vector<std::string>& = + std::vector<std::string>() /*type_names*/) { + return true; + } +}; + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +GTEST_API_ std::string GetCurrentOsStackTraceExceptTop( + UnitTest* unit_test, int skip_count); + +// Helpers for suppressing warnings on unreachable code or constant +// condition. + +// Always returns true. +GTEST_API_ bool AlwaysTrue(); + +// Always returns false. +inline bool AlwaysFalse() { return !AlwaysTrue(); } + +// Helper for suppressing false warning from Clang on a const char* +// variable declared in a conditional expression always being NULL in +// the else branch. +struct GTEST_API_ ConstCharPtr { + ConstCharPtr(const char* str) : value(str) {} + operator bool() const { return true; } + const char* value; +}; + +// Helper for declaring std::string within 'if' statement +// in pre C++17 build environment. +struct TrueWithString { + TrueWithString() = default; + explicit TrueWithString(const char* str) : value(str) {} + explicit TrueWithString(const std::string& str) : value(str) {} + explicit operator bool() const { return true; } + std::string value; +}; + +// A simple Linear Congruential Generator for generating random +// numbers with a uniform distribution. Unlike rand() and srand(), it +// doesn't use global state (and therefore can't interfere with user +// code). Unlike rand_r(), it's portable. An LCG isn't very random, +// but it's good enough for our purposes. +class GTEST_API_ Random { + public: + static const uint32_t kMaxRange = 1u << 31; + + explicit Random(uint32_t seed) : state_(seed) {} + + void Reseed(uint32_t seed) { state_ = seed; } + + // Generates a random number from [0, range). Crashes if 'range' is + // 0 or greater than kMaxRange. + uint32_t Generate(uint32_t range); + + private: + uint32_t state_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(Random); +}; + +// Turns const U&, U&, const U, and U all into U. +#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ + typename std::remove_const<typename std::remove_reference<T>::type>::type + +// HasDebugStringAndShortDebugString<T>::value is a compile-time bool constant +// that's true if and only if T has methods DebugString() and ShortDebugString() +// that return std::string. +template <typename T> +class HasDebugStringAndShortDebugString { + private: + template <typename C> + static auto CheckDebugString(C*) -> typename std::is_same< + std::string, decltype(std::declval<const C>().DebugString())>::type; + template <typename> + static std::false_type CheckDebugString(...); + + template <typename C> + static auto CheckShortDebugString(C*) -> typename std::is_same< + std::string, decltype(std::declval<const C>().ShortDebugString())>::type; + template <typename> + static std::false_type CheckShortDebugString(...); + + using HasDebugStringType = decltype(CheckDebugString<T>(nullptr)); + using HasShortDebugStringType = decltype(CheckShortDebugString<T>(nullptr)); + + public: + static constexpr bool value = + HasDebugStringType::value && HasShortDebugStringType::value; +}; + +template <typename T> +constexpr bool HasDebugStringAndShortDebugString<T>::value; + +// When the compiler sees expression IsContainerTest<C>(0), if C is an +// STL-style container class, the first overload of IsContainerTest +// will be viable (since both C::iterator* and C::const_iterator* are +// valid types and NULL can be implicitly converted to them). It will +// be picked over the second overload as 'int' is a perfect match for +// the type of argument 0. If C::iterator or C::const_iterator is not +// a valid type, the first overload is not viable, and the second +// overload will be picked. Therefore, we can determine whether C is +// a container class by checking the type of IsContainerTest<C>(0). +// The value of the expression is insignificant. +// +// In C++11 mode we check the existence of a const_iterator and that an +// iterator is properly implemented for the container. +// +// For pre-C++11 that we look for both C::iterator and C::const_iterator. +// The reason is that C++ injects the name of a class as a member of the +// class itself (e.g. you can refer to class iterator as either +// 'iterator' or 'iterator::iterator'). If we look for C::iterator +// only, for example, we would mistakenly think that a class named +// iterator is an STL container. +// +// Also note that the simpler approach of overloading +// IsContainerTest(typename C::const_iterator*) and +// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++. +typedef int IsContainer; +template <class C, + class Iterator = decltype(::std::declval<const C&>().begin()), + class = decltype(::std::declval<const C&>().end()), + class = decltype(++::std::declval<Iterator&>()), + class = decltype(*::std::declval<Iterator>()), + class = typename C::const_iterator> +IsContainer IsContainerTest(int /* dummy */) { + return 0; +} + +typedef char IsNotContainer; +template <class C> +IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; } + +// Trait to detect whether a type T is a hash table. +// The heuristic used is that the type contains an inner type `hasher` and does +// not contain an inner type `reverse_iterator`. +// If the container is iterable in reverse, then order might actually matter. +template <typename T> +struct IsHashTable { + private: + template <typename U> + static char test(typename U::hasher*, typename U::reverse_iterator*); + template <typename U> + static int test(typename U::hasher*, ...); + template <typename U> + static char test(...); + + public: + static const bool value = sizeof(test<T>(nullptr, nullptr)) == sizeof(int); +}; + +template <typename T> +const bool IsHashTable<T>::value; + +template <typename C, + bool = sizeof(IsContainerTest<C>(0)) == sizeof(IsContainer)> +struct IsRecursiveContainerImpl; + +template <typename C> +struct IsRecursiveContainerImpl<C, false> : public std::false_type {}; + +// Since the IsRecursiveContainerImpl depends on the IsContainerTest we need to +// obey the same inconsistencies as the IsContainerTest, namely check if +// something is a container is relying on only const_iterator in C++11 and +// is relying on both const_iterator and iterator otherwise +template <typename C> +struct IsRecursiveContainerImpl<C, true> { + using value_type = decltype(*std::declval<typename C::const_iterator>()); + using type = + std::is_same<typename std::remove_const< + typename std::remove_reference<value_type>::type>::type, + C>; +}; + +// IsRecursiveContainer<Type> is a unary compile-time predicate that +// evaluates whether C is a recursive container type. A recursive container +// type is a container type whose value_type is equal to the container type +// itself. An example for a recursive container type is +// boost::filesystem::path, whose iterator has a value_type that is equal to +// boost::filesystem::path. +template <typename C> +struct IsRecursiveContainer : public IsRecursiveContainerImpl<C>::type {}; + +// Utilities for native arrays. + +// ArrayEq() compares two k-dimensional native arrays using the +// elements' operator==, where k can be any integer >= 0. When k is +// 0, ArrayEq() degenerates into comparing a single pair of values. + +template <typename T, typename U> +bool ArrayEq(const T* lhs, size_t size, const U* rhs); + +// This generic version is used when k is 0. +template <typename T, typename U> +inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; } + +// This overload is used when k >= 1. +template <typename T, typename U, size_t N> +inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) { + return internal::ArrayEq(lhs, N, rhs); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous ArrayEq() function, arrays with different sizes would +// lead to different copies of the template code. +template <typename T, typename U> +bool ArrayEq(const T* lhs, size_t size, const U* rhs) { + for (size_t i = 0; i != size; i++) { + if (!internal::ArrayEq(lhs[i], rhs[i])) + return false; + } + return true; +} + +// Finds the first element in the iterator range [begin, end) that +// equals elem. Element may be a native array type itself. +template <typename Iter, typename Element> +Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) { + for (Iter it = begin; it != end; ++it) { + if (internal::ArrayEq(*it, elem)) + return it; + } + return end; +} + +// CopyArray() copies a k-dimensional native array using the elements' +// operator=, where k can be any integer >= 0. When k is 0, +// CopyArray() degenerates into copying a single value. + +template <typename T, typename U> +void CopyArray(const T* from, size_t size, U* to); + +// This generic version is used when k is 0. +template <typename T, typename U> +inline void CopyArray(const T& from, U* to) { *to = from; } + +// This overload is used when k >= 1. +template <typename T, typename U, size_t N> +inline void CopyArray(const T(&from)[N], U(*to)[N]) { + internal::CopyArray(from, N, *to); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous CopyArray() function, arrays with different sizes +// would lead to different copies of the template code. +template <typename T, typename U> +void CopyArray(const T* from, size_t size, U* to) { + for (size_t i = 0; i != size; i++) { + internal::CopyArray(from[i], to + i); + } +} + +// The relation between an NativeArray object (see below) and the +// native array it represents. +// We use 2 different structs to allow non-copyable types to be used, as long +// as RelationToSourceReference() is passed. +struct RelationToSourceReference {}; +struct RelationToSourceCopy {}; + +// Adapts a native array to a read-only STL-style container. Instead +// of the complete STL container concept, this adaptor only implements +// members useful for Google Mock's container matchers. New members +// should be added as needed. To simplify the implementation, we only +// support Element being a raw type (i.e. having no top-level const or +// reference modifier). It's the client's responsibility to satisfy +// this requirement. Element can be an array type itself (hence +// multi-dimensional arrays are supported). +template <typename Element> +class NativeArray { + public: + // STL-style container typedefs. + typedef Element value_type; + typedef Element* iterator; + typedef const Element* const_iterator; + + // Constructs from a native array. References the source. + NativeArray(const Element* array, size_t count, RelationToSourceReference) { + InitRef(array, count); + } + + // Constructs from a native array. Copies the source. + NativeArray(const Element* array, size_t count, RelationToSourceCopy) { + InitCopy(array, count); + } + + // Copy constructor. + NativeArray(const NativeArray& rhs) { + (this->*rhs.clone_)(rhs.array_, rhs.size_); + } + + ~NativeArray() { + if (clone_ != &NativeArray::InitRef) + delete[] array_; + } + + // STL-style container methods. + size_t size() const { return size_; } + const_iterator begin() const { return array_; } + const_iterator end() const { return array_ + size_; } + bool operator==(const NativeArray& rhs) const { + return size() == rhs.size() && + ArrayEq(begin(), size(), rhs.begin()); + } + + private: + static_assert(!std::is_const<Element>::value, "Type must not be const"); + static_assert(!std::is_reference<Element>::value, + "Type must not be a reference"); + + // Initializes this object with a copy of the input. + void InitCopy(const Element* array, size_t a_size) { + Element* const copy = new Element[a_size]; + CopyArray(array, a_size, copy); + array_ = copy; + size_ = a_size; + clone_ = &NativeArray::InitCopy; + } + + // Initializes this object with a reference of the input. + void InitRef(const Element* array, size_t a_size) { + array_ = array; + size_ = a_size; + clone_ = &NativeArray::InitRef; + } + + const Element* array_; + size_t size_; + void (NativeArray::*clone_)(const Element*, size_t); +}; + +// Backport of std::index_sequence. +template <size_t... Is> +struct IndexSequence { + using type = IndexSequence; +}; + +// Double the IndexSequence, and one if plus_one is true. +template <bool plus_one, typename T, size_t sizeofT> +struct DoubleSequence; +template <size_t... I, size_t sizeofT> +struct DoubleSequence<true, IndexSequence<I...>, sizeofT> { + using type = IndexSequence<I..., (sizeofT + I)..., 2 * sizeofT>; +}; +template <size_t... I, size_t sizeofT> +struct DoubleSequence<false, IndexSequence<I...>, sizeofT> { + using type = IndexSequence<I..., (sizeofT + I)...>; +}; + +// Backport of std::make_index_sequence. +// It uses O(ln(N)) instantiation depth. +template <size_t N> +struct MakeIndexSequenceImpl + : DoubleSequence<N % 2 == 1, typename MakeIndexSequenceImpl<N / 2>::type, + N / 2>::type {}; + +template <> +struct MakeIndexSequenceImpl<0> : IndexSequence<> {}; + +template <size_t N> +using MakeIndexSequence = typename MakeIndexSequenceImpl<N>::type; + +template <typename... T> +using IndexSequenceFor = typename MakeIndexSequence<sizeof...(T)>::type; + +template <size_t> +struct Ignore { + Ignore(...); // NOLINT +}; + +template <typename> +struct ElemFromListImpl; +template <size_t... I> +struct ElemFromListImpl<IndexSequence<I...>> { + // We make Ignore a template to solve a problem with MSVC. + // A non-template Ignore would work fine with `decltype(Ignore(I))...`, but + // MSVC doesn't understand how to deal with that pack expansion. + // Use `0 * I` to have a single instantiation of Ignore. + template <typename R> + static R Apply(Ignore<0 * I>..., R (*)(), ...); +}; + +template <size_t N, typename... T> +struct ElemFromList { + using type = + decltype(ElemFromListImpl<typename MakeIndexSequence<N>::type>::Apply( + static_cast<T (*)()>(nullptr)...)); +}; + +struct FlatTupleConstructTag {}; + +template <typename... T> +class FlatTuple; + +template <typename Derived, size_t I> +struct FlatTupleElemBase; + +template <typename... T, size_t I> +struct FlatTupleElemBase<FlatTuple<T...>, I> { + using value_type = typename ElemFromList<I, T...>::type; + FlatTupleElemBase() = default; + template <typename Arg> + explicit FlatTupleElemBase(FlatTupleConstructTag, Arg&& t) + : value(std::forward<Arg>(t)) {} + value_type value; +}; + +template <typename Derived, typename Idx> +struct FlatTupleBase; + +template <size_t... Idx, typename... T> +struct FlatTupleBase<FlatTuple<T...>, IndexSequence<Idx...>> + : FlatTupleElemBase<FlatTuple<T...>, Idx>... { + using Indices = IndexSequence<Idx...>; + FlatTupleBase() = default; + template <typename... Args> + explicit FlatTupleBase(FlatTupleConstructTag, Args&&... args) + : FlatTupleElemBase<FlatTuple<T...>, Idx>(FlatTupleConstructTag{}, + std::forward<Args>(args))... {} + + template <size_t I> + const typename ElemFromList<I, T...>::type& Get() const { + return FlatTupleElemBase<FlatTuple<T...>, I>::value; + } + + template <size_t I> + typename ElemFromList<I, T...>::type& Get() { + return FlatTupleElemBase<FlatTuple<T...>, I>::value; + } + + template <typename F> + auto Apply(F&& f) -> decltype(std::forward<F>(f)(this->Get<Idx>()...)) { + return std::forward<F>(f)(Get<Idx>()...); + } + + template <typename F> + auto Apply(F&& f) const -> decltype(std::forward<F>(f)(this->Get<Idx>()...)) { + return std::forward<F>(f)(Get<Idx>()...); + } +}; + +// Analog to std::tuple but with different tradeoffs. +// This class minimizes the template instantiation depth, thus allowing more +// elements than std::tuple would. std::tuple has been seen to require an +// instantiation depth of more than 10x the number of elements in some +// implementations. +// FlatTuple and ElemFromList are not recursive and have a fixed depth +// regardless of T... +// MakeIndexSequence, on the other hand, it is recursive but with an +// instantiation depth of O(ln(N)). +template <typename... T> +class FlatTuple + : private FlatTupleBase<FlatTuple<T...>, + typename MakeIndexSequence<sizeof...(T)>::type> { + using Indices = typename FlatTupleBase< + FlatTuple<T...>, typename MakeIndexSequence<sizeof...(T)>::type>::Indices; + + public: + FlatTuple() = default; + template <typename... Args> + explicit FlatTuple(FlatTupleConstructTag tag, Args&&... args) + : FlatTuple::FlatTupleBase(tag, std::forward<Args>(args)...) {} + + using FlatTuple::FlatTupleBase::Apply; + using FlatTuple::FlatTupleBase::Get; +}; + +// Utility functions to be called with static_assert to induce deprecation +// warnings. +GTEST_INTERNAL_DEPRECATED( + "INSTANTIATE_TEST_CASE_P is deprecated, please use " + "INSTANTIATE_TEST_SUITE_P") +constexpr bool InstantiateTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "TYPED_TEST_CASE_P is deprecated, please use " + "TYPED_TEST_SUITE_P") +constexpr bool TypedTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "TYPED_TEST_CASE is deprecated, please use " + "TYPED_TEST_SUITE") +constexpr bool TypedTestCaseIsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "REGISTER_TYPED_TEST_CASE_P is deprecated, please use " + "REGISTER_TYPED_TEST_SUITE_P") +constexpr bool RegisterTypedTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "INSTANTIATE_TYPED_TEST_CASE_P is deprecated, please use " + "INSTANTIATE_TYPED_TEST_SUITE_P") +constexpr bool InstantiateTypedTestCase_P_IsDeprecated() { return true; } + +} // namespace internal +} // namespace testing + +namespace std { +// Some standard library implementations use `struct tuple_size` and some use +// `class tuple_size`. Clang warns about the mismatch. +// https://reviews.llvm.org/D55466 +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template <typename... Ts> +struct tuple_size<testing::internal::FlatTuple<Ts...>> + : std::integral_constant<size_t, sizeof...(Ts)> {}; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +} // namespace std + +#define GTEST_MESSAGE_AT_(file, line, message, result_type) \ + ::testing::internal::AssertHelper(result_type, file, line, message) \ + = ::testing::Message() + +#define GTEST_MESSAGE_(message, result_type) \ + GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type) + +#define GTEST_FATAL_FAILURE_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure) + +#define GTEST_NONFATAL_FAILURE_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure) + +#define GTEST_SUCCESS_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess) + +#define GTEST_SKIP_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kSkip) + +// Suppress MSVC warning 4072 (unreachable code) for the code following +// statement if it returns or throws (or doesn't return or throw in some +// situations). +// NOTE: The "else" is important to keep this expansion to prevent a top-level +// "else" from attaching to our "if". +#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ + if (::testing::internal::AlwaysTrue()) { \ + statement; \ + } else /* NOLINT */ \ + static_assert(true, "") // User must have a semicolon after expansion. + +#if GTEST_HAS_EXCEPTIONS + +namespace testing { +namespace internal { + +class NeverThrown { + public: + const char* what() const noexcept { + return "this exception should never be thrown"; + } +}; + +} // namespace internal +} // namespace testing + +#if GTEST_HAS_RTTI + +#define GTEST_EXCEPTION_TYPE_(e) ::testing::internal::GetTypeName(typeid(e)) + +#else // GTEST_HAS_RTTI + +#define GTEST_EXCEPTION_TYPE_(e) \ + std::string { "an std::exception-derived error" } + +#endif // GTEST_HAS_RTTI + +#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) \ + catch (typename std::conditional< \ + std::is_same<typename std::remove_cv<typename std::remove_reference< \ + expected_exception>::type>::type, \ + std::exception>::value, \ + const ::testing::internal::NeverThrown&, const std::exception&>::type \ + e) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws "; \ + gtest_msg.value += GTEST_EXCEPTION_TYPE_(e); \ + gtest_msg.value += " with description \""; \ + gtest_msg.value += e.what(); \ + gtest_msg.value += "\"."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } + +#else // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) + +#endif // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_THROW_(statement, expected_exception, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::TrueWithString gtest_msg{}) { \ + bool gtest_caught_expected = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (expected_exception const&) { \ + gtest_caught_expected = true; \ + } \ + GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) \ + catch (...) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws a different type."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + if (!gtest_caught_expected) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws nothing."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else /*NOLINT*/ \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \ + : fail(gtest_msg.value.c_str()) + +#if GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() \ + catch (std::exception const& e) { \ + gtest_msg.value = "it throws "; \ + gtest_msg.value += GTEST_EXCEPTION_TYPE_(e); \ + gtest_msg.value += " with description \""; \ + gtest_msg.value += e.what(); \ + gtest_msg.value += "\"."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } + +#else // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() + +#endif // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::TrueWithString gtest_msg{}) { \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() \ + catch (...) { \ + gtest_msg.value = "it throws."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \ + fail(("Expected: " #statement " doesn't throw an exception.\n" \ + " Actual: " + gtest_msg.value).c_str()) + +#define GTEST_TEST_ANY_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + bool gtest_caught_any = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (...) { \ + gtest_caught_any = true; \ + } \ + if (!gtest_caught_any) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): \ + fail("Expected: " #statement " throws an exception.\n" \ + " Actual: it doesn't.") + + +// Implements Boolean test assertions such as EXPECT_TRUE. expression can be +// either a boolean expression or an AssertionResult. text is a textual +// representation of expression as it was passed into the EXPECT_TRUE. +#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar_ = \ + ::testing::AssertionResult(expression)) \ + ; \ + else \ + fail(::testing::internal::GetBoolAssertionFailureMessage(\ + gtest_ar_, text, #actual, #expected).c_str()) + +#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): \ + fail("Expected: " #statement " doesn't generate new fatal " \ + "failures in the current thread.\n" \ + " Actual: it does.") + +// Expands to the name of the class that implements the given test. +#define GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + test_suite_name##_##test_name##_Test + +// Helper macro for defining tests. +#define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id) \ + static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1, \ + "test_suite_name must not be empty"); \ + static_assert(sizeof(GTEST_STRINGIFY_(test_name)) > 1, \ + "test_name must not be empty"); \ + class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + : public parent_class { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default; \ + ~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default; \ + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)); \ + GTEST_DISALLOW_MOVE_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)); \ + \ + private: \ + void TestBody() override; \ + static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_; \ + }; \ + \ + ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)::test_info_ = \ + ::testing::internal::MakeAndRegisterTestInfo( \ + #test_suite_name, #test_name, nullptr, nullptr, \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id), \ + ::testing::internal::SuiteApiResolver< \ + parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__), \ + ::testing::internal::SuiteApiResolver< \ + parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__), \ + new ::testing::internal::TestFactoryImpl<GTEST_TEST_CLASS_NAME_( \ + test_suite_name, test_name)>); \ + void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the public API for death tests. It is +// #included by gtest.h so a user doesn't need to include this +// directly. +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. +// +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines internal utilities needed for implementing +// death tests. They are subject to change without notice. +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This file implements just enough of the matcher interface to allow +// EXPECT_DEATH and friends to accept a matcher argument. + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ + +#include <atomic> +#include <memory> +#include <ostream> +#include <string> +#include <type_traits> + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Google Test - The Google C++ Testing and Mocking Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr); +// +// A user can teach this function how to print a class type T by +// defining either operator<<() or PrintTo() in the namespace that +// defines T. More specifically, the FIRST defined function in the +// following list will be used (assuming T is defined in namespace +// foo): +// +// 1. foo::PrintTo(const T&, ostream*) +// 2. operator<<(ostream&, const T&) defined in either foo or the +// global namespace. +// +// However if T is an STL-style container then it is printed element-wise +// unless foo::PrintTo(const T&, ostream*) is defined. Note that +// operator<<() is ignored for container types. +// +// If none of the above is defined, it will print the debug string of +// the value if it is a protocol buffer, or print the raw bytes in the +// value otherwise. +// +// To aid debugging: when T is a reference type, the address of the +// value is also printed; when T is a (const) char pointer, both the +// pointer value and the NUL-terminated string it points to are +// printed. +// +// We also provide some convenient wrappers: +// +// // Prints a value to a string. For a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// std::string ::testing::PrintToString(const T& value); +// +// // Prints a value tersely: for a reference type, the referenced +// // value (but not the address) is printed; for a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// void ::testing::internal::UniversalTersePrint(const T& value, ostream*); +// +// // Prints value using the type inferred by the compiler. The difference +// // from UniversalTersePrint() is that this function prints both the +// // pointer and the NUL-terminated string for a (const or not) char pointer. +// void ::testing::internal::UniversalPrint(const T& value, ostream*); +// +// // Prints the fields of a tuple tersely to a string vector, one +// // element for each field. Tuple support must be enabled in +// // gtest-port.h. +// std::vector<string> UniversalTersePrintTupleFieldsToStrings( +// const Tuple& value); +// +// Known limitation: +// +// The print primitives print the elements of an STL-style container +// using the compiler-inferred type of *iter where iter is a +// const_iterator of the container. When const_iterator is an input +// iterator but not a forward iterator, this inferred type may not +// match value_type, and the print output may be incorrect. In +// practice, this is rarely a problem as for most containers +// const_iterator is a forward iterator. We'll fix this if there's an +// actual need for it. Note that this fix cannot rely on value_type +// being defined as many user-defined container types don't have +// value_type. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#include <functional> +#include <memory> +#include <ostream> // NOLINT +#include <sstream> +#include <string> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> + + +#if GTEST_HAS_RTTI +#include <typeindex> +#include <typeinfo> +#endif // GTEST_HAS_RTTI + +namespace testing { + +// Definitions in the internal* namespaces are subject to change without notice. +// DO NOT USE THEM IN USER CODE! +namespace internal { + +template <typename T> +void UniversalPrint(const T& value, ::std::ostream* os); + +// Used to print an STL-style container when the user doesn't define +// a PrintTo() for it. +struct ContainerPrinter { + template <typename T, + typename = typename std::enable_if< + (sizeof(IsContainerTest<T>(0)) == sizeof(IsContainer)) && + !IsRecursiveContainer<T>::value>::type> + static void PrintValue(const T& container, std::ostream* os) { + const size_t kMaxCount = 32; // The maximum number of elements to print. + *os << '{'; + size_t count = 0; + for (auto&& elem : container) { + if (count > 0) { + *os << ','; + if (count == kMaxCount) { // Enough has been printed. + *os << " ..."; + break; + } + } + *os << ' '; + // We cannot call PrintTo(elem, os) here as PrintTo() doesn't + // handle `elem` being a native array. + internal::UniversalPrint(elem, os); + ++count; + } + + if (count > 0) { + *os << ' '; + } + *os << '}'; + } +}; + +// Used to print a pointer that is neither a char pointer nor a member +// pointer, when the user doesn't define PrintTo() for it. (A member +// variable pointer or member function pointer doesn't really point to +// a location in the address space. Their representation is +// implementation-defined. Therefore they will be printed as raw +// bytes.) +struct FunctionPointerPrinter { + template <typename T, typename = typename std::enable_if< + std::is_function<T>::value>::type> + static void PrintValue(T* p, ::std::ostream* os) { + if (p == nullptr) { + *os << "NULL"; + } else { + // T is a function type, so '*os << p' doesn't do what we want + // (it just prints p as bool). We want to print p as a const + // void*. + *os << reinterpret_cast<const void*>(p); + } + } +}; + +struct PointerPrinter { + template <typename T> + static void PrintValue(T* p, ::std::ostream* os) { + if (p == nullptr) { + *os << "NULL"; + } else { + // T is not a function type. We just call << to print p, + // relying on ADL to pick up user-defined << for their pointer + // types, if any. + *os << p; + } + } +}; + +namespace internal_stream_operator_without_lexical_name_lookup { + +// The presence of an operator<< here will terminate lexical scope lookup +// straight away (even though it cannot be a match because of its argument +// types). Thus, the two operator<< calls in StreamPrinter will find only ADL +// candidates. +struct LookupBlocker {}; +void operator<<(LookupBlocker, LookupBlocker); + +struct StreamPrinter { + template <typename T, + // Don't accept member pointers here. We'd print them via implicit + // conversion to bool, which isn't useful. + typename = typename std::enable_if< + !std::is_member_pointer<T>::value>::type, + // Only accept types for which we can find a streaming operator via + // ADL (possibly involving implicit conversions). + typename = decltype(std::declval<std::ostream&>() + << std::declval<const T&>())> + static void PrintValue(const T& value, ::std::ostream* os) { + // Call streaming operator found by ADL, possibly with implicit conversions + // of the arguments. + *os << value; + } +}; + +} // namespace internal_stream_operator_without_lexical_name_lookup + +struct ProtobufPrinter { + // We print a protobuf using its ShortDebugString() when the string + // doesn't exceed this many characters; otherwise we print it using + // DebugString() for better readability. + static const size_t kProtobufOneLinerMaxLength = 50; + + template <typename T, + typename = typename std::enable_if< + internal::HasDebugStringAndShortDebugString<T>::value>::type> + static void PrintValue(const T& value, ::std::ostream* os) { + std::string pretty_str = value.ShortDebugString(); + if (pretty_str.length() > kProtobufOneLinerMaxLength) { + pretty_str = "\n" + value.DebugString(); + } + *os << ("<" + pretty_str + ">"); + } +}; + +struct ConvertibleToIntegerPrinter { + // Since T has no << operator or PrintTo() but can be implicitly + // converted to BiggestInt, we print it as a BiggestInt. + // + // Most likely T is an enum type (either named or unnamed), in which + // case printing it as an integer is the desired behavior. In case + // T is not an enum, printing it as an integer is the best we can do + // given that it has no user-defined printer. + static void PrintValue(internal::BiggestInt value, ::std::ostream* os) { + *os << value; + } +}; + +struct ConvertibleToStringViewPrinter { +#if GTEST_INTERNAL_HAS_STRING_VIEW + static void PrintValue(internal::StringView value, ::std::ostream* os) { + internal::UniversalPrint(value, os); + } +#endif +}; + + +// Prints the given number of bytes in the given object to the given +// ostream. +GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes, + size_t count, + ::std::ostream* os); +struct RawBytesPrinter { + // SFINAE on `sizeof` to make sure we have a complete type. + template <typename T, size_t = sizeof(T)> + static void PrintValue(const T& value, ::std::ostream* os) { + PrintBytesInObjectTo( + static_cast<const unsigned char*>( + // Load bearing cast to void* to support iOS + reinterpret_cast<const void*>(std::addressof(value))), + sizeof(value), os); + } +}; + +struct FallbackPrinter { + template <typename T> + static void PrintValue(const T&, ::std::ostream* os) { + *os << "(incomplete type)"; + } +}; + +// Try every printer in order and return the first one that works. +template <typename T, typename E, typename Printer, typename... Printers> +struct FindFirstPrinter : FindFirstPrinter<T, E, Printers...> {}; + +template <typename T, typename Printer, typename... Printers> +struct FindFirstPrinter< + T, decltype(Printer::PrintValue(std::declval<const T&>(), nullptr)), + Printer, Printers...> { + using type = Printer; +}; + +// Select the best printer in the following order: +// - Print containers (they have begin/end/etc). +// - Print function pointers. +// - Print object pointers. +// - Use the stream operator, if available. +// - Print protocol buffers. +// - Print types convertible to BiggestInt. +// - Print types convertible to StringView, if available. +// - Fallback to printing the raw bytes of the object. +template <typename T> +void PrintWithFallback(const T& value, ::std::ostream* os) { + using Printer = typename FindFirstPrinter< + T, void, ContainerPrinter, FunctionPointerPrinter, PointerPrinter, + internal_stream_operator_without_lexical_name_lookup::StreamPrinter, + ProtobufPrinter, ConvertibleToIntegerPrinter, + ConvertibleToStringViewPrinter, RawBytesPrinter, FallbackPrinter>::type; + Printer::PrintValue(value, os); +} + +// FormatForComparison<ToPrint, OtherOperand>::Format(value) formats a +// value of type ToPrint that is an operand of a comparison assertion +// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in +// the comparison, and is used to help determine the best way to +// format the value. In particular, when the value is a C string +// (char pointer) and the other operand is an STL string object, we +// want to format the C string as a string, since we know it is +// compared by value with the string object. If the value is a char +// pointer but the other operand is not an STL string object, we don't +// know whether the pointer is supposed to point to a NUL-terminated +// string, and thus want to print it as a pointer to be safe. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// The default case. +template <typename ToPrint, typename OtherOperand> +class FormatForComparison { + public: + static ::std::string Format(const ToPrint& value) { + return ::testing::PrintToString(value); + } +}; + +// Array. +template <typename ToPrint, size_t N, typename OtherOperand> +class FormatForComparison<ToPrint[N], OtherOperand> { + public: + static ::std::string Format(const ToPrint* value) { + return FormatForComparison<const ToPrint*, OtherOperand>::Format(value); + } +}; + +// By default, print C string as pointers to be safe, as we don't know +// whether they actually point to a NUL-terminated string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \ + template <typename OtherOperand> \ + class FormatForComparison<CharType*, OtherOperand> { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(static_cast<const void*>(value)); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t); +#ifdef __cpp_char8_t +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char8_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char8_t); +#endif +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char16_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char16_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char32_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char32_t); + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_ + +// If a C string is compared with an STL string object, we know it's meant +// to point to a NUL-terminated string, and thus can print it as a string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \ + template <> \ + class FormatForComparison<CharType*, OtherStringType> { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(value); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string); +#ifdef __cpp_char8_t +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char8_t, ::std::u8string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char8_t, ::std::u8string); +#endif +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char16_t, ::std::u16string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char16_t, ::std::u16string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char32_t, ::std::u32string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char32_t, ::std::u32string); + +#if GTEST_HAS_STD_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring); +#endif + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_ + +// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) +// operand to be used in a failure message. The type (but not value) +// of the other operand may affect the format. This allows us to +// print a char* as a raw pointer when it is compared against another +// char* or void*, and print it as a C string when it is compared +// against an std::string object, for example. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template <typename T1, typename T2> +std::string FormatForComparisonFailureMessage( + const T1& value, const T2& /* other_operand */) { + return FormatForComparison<T1, T2>::Format(value); +} + +// UniversalPrinter<T>::Print(value, ostream_ptr) prints the given +// value to the given ostream. The caller must ensure that +// 'ostream_ptr' is not NULL, or the behavior is undefined. +// +// We define UniversalPrinter as a class template (as opposed to a +// function template), as we need to partially specialize it for +// reference types, which cannot be done with function templates. +template <typename T> +class UniversalPrinter; + +// Prints the given value using the << operator if it has one; +// otherwise prints the bytes in it. This is what +// UniversalPrinter<T>::Print() does when PrintTo() is not specialized +// or overloaded for type T. +// +// A user can override this behavior for a class type Foo by defining +// an overload of PrintTo() in the namespace where Foo is defined. We +// give the user this option as sometimes defining a << operator for +// Foo is not desirable (e.g. the coding style may prevent doing it, +// or there is already a << operator but it doesn't do what the user +// wants). +template <typename T> +void PrintTo(const T& value, ::std::ostream* os) { + internal::PrintWithFallback(value, os); +} + +// The following list of PrintTo() overloads tells +// UniversalPrinter<T>::Print() how to print standard types (built-in +// types, strings, plain arrays, and pointers). + +// Overloads for various char types. +GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os); +GTEST_API_ void PrintTo(signed char c, ::std::ostream* os); +inline void PrintTo(char c, ::std::ostream* os) { + // When printing a plain char, we always treat it as unsigned. This + // way, the output won't be affected by whether the compiler thinks + // char is signed or not. + PrintTo(static_cast<unsigned char>(c), os); +} + +// Overloads for other simple built-in types. +inline void PrintTo(bool x, ::std::ostream* os) { + *os << (x ? "true" : "false"); +} + +// Overload for wchar_t type. +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its decimal code (except for L'\0'). +// The L'\0' char is printed as "L'\\0'". The decimal code is printed +// as signed integer when wchar_t is implemented by the compiler +// as a signed type and is printed as an unsigned integer when wchar_t +// is implemented as an unsigned type. +GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os); + +GTEST_API_ void PrintTo(char32_t c, ::std::ostream* os); +inline void PrintTo(char16_t c, ::std::ostream* os) { + PrintTo(ImplicitCast_<char32_t>(c), os); +} +#ifdef __cpp_char8_t +inline void PrintTo(char8_t c, ::std::ostream* os) { + PrintTo(ImplicitCast_<char32_t>(c), os); +} +#endif + +// Overloads for C strings. +GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); +inline void PrintTo(char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const char*>(s), os); +} + +// signed/unsigned char is often used for representing binary data, so +// we print pointers to it as void* to be safe. +inline void PrintTo(const signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const void*>(s), os); +} +inline void PrintTo(signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const void*>(s), os); +} +inline void PrintTo(const unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const void*>(s), os); +} +inline void PrintTo(unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const void*>(s), os); +} +#ifdef __cpp_char8_t +// Overloads for u8 strings. +void PrintTo(const char8_t* s, ::std::ostream* os); +inline void PrintTo(char8_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const char8_t*>(s), os); +} +#endif +// Overloads for u16 strings. +void PrintTo(const char16_t* s, ::std::ostream* os); +inline void PrintTo(char16_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const char16_t*>(s), os); +} +// Overloads for u32 strings. +void PrintTo(const char32_t* s, ::std::ostream* os); +inline void PrintTo(char32_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const char32_t*>(s), os); +} + +// MSVC can be configured to define wchar_t as a typedef of unsigned +// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native +// type. When wchar_t is a typedef, defining an overload for const +// wchar_t* would cause unsigned short* be printed as a wide string, +// possibly causing invalid memory accesses. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Overloads for wide C strings +GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os); +inline void PrintTo(wchar_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const wchar_t*>(s), os); +} +#endif + +// Overload for C arrays. Multi-dimensional arrays are printed +// properly. + +// Prints the given number of elements in an array, without printing +// the curly braces. +template <typename T> +void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) { + UniversalPrint(a[0], os); + for (size_t i = 1; i != count; i++) { + *os << ", "; + UniversalPrint(a[i], os); + } +} + +// Overloads for ::std::string. +GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os); +inline void PrintTo(const ::std::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} + +// Overloads for ::std::u8string +#ifdef __cpp_char8_t +GTEST_API_ void PrintU8StringTo(const ::std::u8string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u8string& s, ::std::ostream* os) { + PrintU8StringTo(s, os); +} +#endif + +// Overloads for ::std::u16string +GTEST_API_ void PrintU16StringTo(const ::std::u16string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u16string& s, ::std::ostream* os) { + PrintU16StringTo(s, os); +} + +// Overloads for ::std::u32string +GTEST_API_ void PrintU32StringTo(const ::std::u32string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u32string& s, ::std::ostream* os) { + PrintU32StringTo(s, os); +} + +// Overloads for ::std::wstring. +#if GTEST_HAS_STD_WSTRING +GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_INTERNAL_HAS_STRING_VIEW +// Overload for internal::StringView. +inline void PrintTo(internal::StringView sp, ::std::ostream* os) { + PrintTo(::std::string(sp), os); +} +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + +inline void PrintTo(std::nullptr_t, ::std::ostream* os) { *os << "(nullptr)"; } + +template <typename T> +void PrintTo(std::reference_wrapper<T> ref, ::std::ostream* os) { + UniversalPrinter<T&>::Print(ref.get(), os); +} + +inline const void* VoidifyPointer(const void* p) { return p; } +inline const void* VoidifyPointer(volatile const void* p) { + return const_cast<const void*>(p); +} + +template <typename T, typename Ptr> +void PrintSmartPointer(const Ptr& ptr, std::ostream* os, char) { + if (ptr == nullptr) { + *os << "(nullptr)"; + } else { + // We can't print the value. Just print the pointer.. + *os << "(" << (VoidifyPointer)(ptr.get()) << ")"; + } +} +template <typename T, typename Ptr, + typename = typename std::enable_if<!std::is_void<T>::value && + !std::is_array<T>::value>::type> +void PrintSmartPointer(const Ptr& ptr, std::ostream* os, int) { + if (ptr == nullptr) { + *os << "(nullptr)"; + } else { + *os << "(ptr = " << (VoidifyPointer)(ptr.get()) << ", value = "; + UniversalPrinter<T>::Print(*ptr, os); + *os << ")"; + } +} + +template <typename T, typename D> +void PrintTo(const std::unique_ptr<T, D>& ptr, std::ostream* os) { + (PrintSmartPointer<T>)(ptr, os, 0); +} + +template <typename T> +void PrintTo(const std::shared_ptr<T>& ptr, std::ostream* os) { + (PrintSmartPointer<T>)(ptr, os, 0); +} + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template <typename T> +void PrintTupleTo(const T&, std::integral_constant<size_t, 0>, + ::std::ostream*) {} + +template <typename T, size_t I> +void PrintTupleTo(const T& t, std::integral_constant<size_t, I>, + ::std::ostream* os) { + PrintTupleTo(t, std::integral_constant<size_t, I - 1>(), os); + GTEST_INTENTIONAL_CONST_COND_PUSH_() + if (I > 1) { + GTEST_INTENTIONAL_CONST_COND_POP_() + *os << ", "; + } + UniversalPrinter<typename std::tuple_element<I - 1, T>::type>::Print( + std::get<I - 1>(t), os); +} + +template <typename... Types> +void PrintTo(const ::std::tuple<Types...>& t, ::std::ostream* os) { + *os << "("; + PrintTupleTo(t, std::integral_constant<size_t, sizeof...(Types)>(), os); + *os << ")"; +} + +// Overload for std::pair. +template <typename T1, typename T2> +void PrintTo(const ::std::pair<T1, T2>& value, ::std::ostream* os) { + *os << '('; + // We cannot use UniversalPrint(value.first, os) here, as T1 may be + // a reference type. The same for printing value.second. + UniversalPrinter<T1>::Print(value.first, os); + *os << ", "; + UniversalPrinter<T2>::Print(value.second, os); + *os << ')'; +} + +#if GTEST_HAS_RTTI +inline void PrintTo(const ::std::type_info& value, ::std::ostream* os) { + internal::PrintTo<::std::type_info>(value, os); + *os << " (\"" << value.name() << "\")"; +} + +inline void PrintTo(const ::std::type_index& value, ::std::ostream* os) { + internal::PrintTo<::std::type_index>(value, os); + *os << " (\"" << value.name() << "\")"; +} +#endif // GTEST_HAS_RTTI + +// Implements printing a non-reference type T by letting the compiler +// pick the right overload of PrintTo() for T. +template <typename T> +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) + + // Note: we deliberately don't call this PrintTo(), as that name + // conflicts with ::testing::internal::PrintTo in the body of the + // function. + static void Print(const T& value, ::std::ostream* os) { + // By default, ::testing::internal::PrintTo() is used for printing + // the value. + // + // Thanks to Koenig look-up, if T is a class and has its own + // PrintTo() function defined in its namespace, that function will + // be visible here. Since it is more specific than the generic ones + // in ::testing::internal, it will be picked by the compiler in the + // following statement - exactly what we want. + PrintTo(value, os); + } + + GTEST_DISABLE_MSC_WARNINGS_POP_() +}; + +// Remove any const-qualifiers before passing a type to UniversalPrinter. +template <typename T> +class UniversalPrinter<const T> : public UniversalPrinter<T> {}; + +#if GTEST_INTERNAL_HAS_ANY + +// Printer for std::any / absl::any + +template <> +class UniversalPrinter<Any> { + public: + static void Print(const Any& value, ::std::ostream* os) { + if (value.has_value()) { + *os << "value of type " << GetTypeName(value); + } else { + *os << "no value"; + } + } + + private: + static std::string GetTypeName(const Any& value) { +#if GTEST_HAS_RTTI + return internal::GetTypeName(value.type()); +#else + static_cast<void>(value); // possibly unused + return "<unknown_type>"; +#endif // GTEST_HAS_RTTI + } +}; + +#endif // GTEST_INTERNAL_HAS_ANY + +#if GTEST_INTERNAL_HAS_OPTIONAL + +// Printer for std::optional / absl::optional + +template <typename T> +class UniversalPrinter<Optional<T>> { + public: + static void Print(const Optional<T>& value, ::std::ostream* os) { + *os << '('; + if (!value) { + *os << "nullopt"; + } else { + UniversalPrint(*value, os); + } + *os << ')'; + } +}; + +#endif // GTEST_INTERNAL_HAS_OPTIONAL + +#if GTEST_INTERNAL_HAS_VARIANT + +// Printer for std::variant / absl::variant + +template <typename... T> +class UniversalPrinter<Variant<T...>> { + public: + static void Print(const Variant<T...>& value, ::std::ostream* os) { + *os << '('; +#if GTEST_HAS_ABSL + absl::visit(Visitor{os, value.index()}, value); +#else + std::visit(Visitor{os, value.index()}, value); +#endif // GTEST_HAS_ABSL + *os << ')'; + } + + private: + struct Visitor { + template <typename U> + void operator()(const U& u) const { + *os << "'" << GetTypeName<U>() << "(index = " << index + << ")' with value "; + UniversalPrint(u, os); + } + ::std::ostream* os; + std::size_t index; + }; +}; + +#endif // GTEST_INTERNAL_HAS_VARIANT + +// UniversalPrintArray(begin, len, os) prints an array of 'len' +// elements, starting at address 'begin'. +template <typename T> +void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { + if (len == 0) { + *os << "{}"; + } else { + *os << "{ "; + const size_t kThreshold = 18; + const size_t kChunkSize = 8; + // If the array has more than kThreshold elements, we'll have to + // omit some details by printing only the first and the last + // kChunkSize elements. + if (len <= kThreshold) { + PrintRawArrayTo(begin, len, os); + } else { + PrintRawArrayTo(begin, kChunkSize, os); + *os << ", ..., "; + PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os); + } + *os << " }"; + } +} +// This overload prints a (const) char array compactly. +GTEST_API_ void UniversalPrintArray( + const char* begin, size_t len, ::std::ostream* os); + +#ifdef __cpp_char8_t +// This overload prints a (const) char8_t array compactly. +GTEST_API_ void UniversalPrintArray(const char8_t* begin, size_t len, + ::std::ostream* os); +#endif + +// This overload prints a (const) char16_t array compactly. +GTEST_API_ void UniversalPrintArray(const char16_t* begin, size_t len, + ::std::ostream* os); + +// This overload prints a (const) char32_t array compactly. +GTEST_API_ void UniversalPrintArray(const char32_t* begin, size_t len, + ::std::ostream* os); + +// This overload prints a (const) wchar_t array compactly. +GTEST_API_ void UniversalPrintArray( + const wchar_t* begin, size_t len, ::std::ostream* os); + +// Implements printing an array type T[N]. +template <typename T, size_t N> +class UniversalPrinter<T[N]> { + public: + // Prints the given array, omitting some elements when there are too + // many. + static void Print(const T (&a)[N], ::std::ostream* os) { + UniversalPrintArray(a, N, os); + } +}; + +// Implements printing a reference type T&. +template <typename T> +class UniversalPrinter<T&> { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) + + static void Print(const T& value, ::std::ostream* os) { + // Prints the address of the value. We use reinterpret_cast here + // as static_cast doesn't compile when T is a function type. + *os << "@" << reinterpret_cast<const void*>(&value) << " "; + + // Then prints the value itself. + UniversalPrint(value, os); + } + + GTEST_DISABLE_MSC_WARNINGS_POP_() +}; + +// Prints a value tersely: for a reference type, the referenced value +// (but not the address) is printed; for a (const) char pointer, the +// NUL-terminated string (but not the pointer) is printed. + +template <typename T> +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template <typename T> +class UniversalTersePrinter<T&> { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template <typename T, size_t N> +class UniversalTersePrinter<T[N]> { + public: + static void Print(const T (&value)[N], ::std::ostream* os) { + UniversalPrinter<T[N]>::Print(value, os); + } +}; +template <> +class UniversalTersePrinter<const char*> { + public: + static void Print(const char* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(std::string(str), os); + } + } +}; +template <> +class UniversalTersePrinter<char*> : public UniversalTersePrinter<const char*> { +}; + +#ifdef __cpp_char8_t +template <> +class UniversalTersePrinter<const char8_t*> { + public: + static void Print(const char8_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u8string(str), os); + } + } +}; +template <> +class UniversalTersePrinter<char8_t*> + : public UniversalTersePrinter<const char8_t*> {}; +#endif + +template <> +class UniversalTersePrinter<const char16_t*> { + public: + static void Print(const char16_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u16string(str), os); + } + } +}; +template <> +class UniversalTersePrinter<char16_t*> + : public UniversalTersePrinter<const char16_t*> {}; + +template <> +class UniversalTersePrinter<const char32_t*> { + public: + static void Print(const char32_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u32string(str), os); + } + } +}; +template <> +class UniversalTersePrinter<char32_t*> + : public UniversalTersePrinter<const char32_t*> {}; + +#if GTEST_HAS_STD_WSTRING +template <> +class UniversalTersePrinter<const wchar_t*> { + public: + static void Print(const wchar_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::wstring(str), os); + } + } +}; +#endif + +template <> +class UniversalTersePrinter<wchar_t*> { + public: + static void Print(wchar_t* str, ::std::ostream* os) { + UniversalTersePrinter<const wchar_t*>::Print(str, os); + } +}; + +template <typename T> +void UniversalTersePrint(const T& value, ::std::ostream* os) { + UniversalTersePrinter<T>::Print(value, os); +} + +// Prints a value using the type inferred by the compiler. The +// difference between this and UniversalTersePrint() is that for a +// (const) char pointer, this prints both the pointer and the +// NUL-terminated string. +template <typename T> +void UniversalPrint(const T& value, ::std::ostream* os) { + // A workarond for the bug in VC++ 7.1 that prevents us from instantiating + // UniversalPrinter with T directly. + typedef T T1; + UniversalPrinter<T1>::Print(value, os); +} + +typedef ::std::vector< ::std::string> Strings; + + // Tersely prints the first N fields of a tuple to a string vector, + // one element for each field. +template <typename Tuple> +void TersePrintPrefixToStrings(const Tuple&, std::integral_constant<size_t, 0>, + Strings*) {} +template <typename Tuple, size_t I> +void TersePrintPrefixToStrings(const Tuple& t, + std::integral_constant<size_t, I>, + Strings* strings) { + TersePrintPrefixToStrings(t, std::integral_constant<size_t, I - 1>(), + strings); + ::std::stringstream ss; + UniversalTersePrint(std::get<I - 1>(t), &ss); + strings->push_back(ss.str()); +} + +// Prints the fields of a tuple tersely to a string vector, one +// element for each field. See the comment before +// UniversalTersePrint() for how we define "tersely". +template <typename Tuple> +Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { + Strings result; + TersePrintPrefixToStrings( + value, std::integral_constant<size_t, std::tuple_size<Tuple>::value>(), + &result); + return result; +} + +} // namespace internal + +template <typename T> +::std::string PrintToString(const T& value) { + ::std::stringstream ss; + internal::UniversalTersePrinter<T>::Print(value, &ss); + return ss.str(); +} + +} // namespace testing + +// Include any custom printer added by the local installation. +// We must include this header at the end to make sure it can use the +// declarations from this file. +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. +// +// This file provides an injection point for custom printers in a local +// installation of gTest. +// It will be included from gtest-printers.h and the overrides in this file +// will be visible to everyone. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +// MSVC warning C5046 is new as of VS2017 version 15.8. +#if defined(_MSC_VER) && _MSC_VER >= 1915 +#define GTEST_MAYBE_5046_ 5046 +#else +#define GTEST_MAYBE_5046_ +#endif + +GTEST_DISABLE_MSC_WARNINGS_PUSH_( + 4251 GTEST_MAYBE_5046_ /* class A needs to have dll-interface to be used by + clients of class B */ + /* Symbol involving type with internal linkage not defined */) + +namespace testing { + +// To implement a matcher Foo for type T, define: +// 1. a class FooMatcherMatcher that implements the matcher interface: +// using is_gtest_matcher = void; +// bool MatchAndExplain(const T&, std::ostream*); +// (MatchResultListener* can also be used instead of std::ostream*) +// void DescribeTo(std::ostream*); +// void DescribeNegationTo(std::ostream*); +// +// 2. a factory function that creates a Matcher<T> object from a +// FooMatcherMatcher. + +class MatchResultListener { + public: + // Creates a listener object with the given underlying ostream. The + // listener does not own the ostream, and does not dereference it + // in the constructor or destructor. + explicit MatchResultListener(::std::ostream* os) : stream_(os) {} + virtual ~MatchResultListener() = 0; // Makes this class abstract. + + // Streams x to the underlying ostream; does nothing if the ostream + // is NULL. + template <typename T> + MatchResultListener& operator<<(const T& x) { + if (stream_ != nullptr) *stream_ << x; + return *this; + } + + // Returns the underlying ostream. + ::std::ostream* stream() { return stream_; } + + // Returns true if and only if the listener is interested in an explanation + // of the match result. A matcher's MatchAndExplain() method can use + // this information to avoid generating the explanation when no one + // intends to hear it. + bool IsInterested() const { return stream_ != nullptr; } + + private: + ::std::ostream* const stream_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(MatchResultListener); +}; + +inline MatchResultListener::~MatchResultListener() { +} + +// An instance of a subclass of this knows how to describe itself as a +// matcher. +class MatcherDescriberInterface { + public: + virtual ~MatcherDescriberInterface() {} + + // Describes this matcher to an ostream. The function should print + // a verb phrase that describes the property a value matching this + // matcher should have. The subject of the verb phrase is the value + // being matched. For example, the DescribeTo() method of the Gt(7) + // matcher prints "is greater than 7". + virtual void DescribeTo(::std::ostream* os) const = 0; + + // Describes the negation of this matcher to an ostream. For + // example, if the description of this matcher is "is greater than + // 7", the negated description could be "is not greater than 7". + // You are not required to override this when implementing + // MatcherInterface, but it is highly advised so that your matcher + // can produce good error messages. + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "not ("; + DescribeTo(os); + *os << ")"; + } +}; + +// The implementation of a matcher. +template <typename T> +class MatcherInterface : public MatcherDescriberInterface { + public: + // Returns true if and only if the matcher matches x; also explains the + // match result to 'listener' if necessary (see the next paragraph), in + // the form of a non-restrictive relative clause ("which ...", + // "whose ...", etc) that describes x. For example, the + // MatchAndExplain() method of the Pointee(...) matcher should + // generate an explanation like "which points to ...". + // + // Implementations of MatchAndExplain() should add an explanation of + // the match result *if and only if* they can provide additional + // information that's not already present (or not obvious) in the + // print-out of x and the matcher's description. Whether the match + // succeeds is not a factor in deciding whether an explanation is + // needed, as sometimes the caller needs to print a failure message + // when the match succeeds (e.g. when the matcher is used inside + // Not()). + // + // For example, a "has at least 10 elements" matcher should explain + // what the actual element count is, regardless of the match result, + // as it is useful information to the reader; on the other hand, an + // "is empty" matcher probably only needs to explain what the actual + // size is when the match fails, as it's redundant to say that the + // size is 0 when the value is already known to be empty. + // + // You should override this method when defining a new matcher. + // + // It's the responsibility of the caller (Google Test) to guarantee + // that 'listener' is not NULL. This helps to simplify a matcher's + // implementation when it doesn't care about the performance, as it + // can talk to 'listener' without checking its validity first. + // However, in order to implement dummy listeners efficiently, + // listener->stream() may be NULL. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; + + // Inherits these methods from MatcherDescriberInterface: + // virtual void DescribeTo(::std::ostream* os) const = 0; + // virtual void DescribeNegationTo(::std::ostream* os) const; +}; + +namespace internal { + +struct AnyEq { + template <typename A, typename B> + bool operator()(const A& a, const B& b) const { return a == b; } +}; +struct AnyNe { + template <typename A, typename B> + bool operator()(const A& a, const B& b) const { return a != b; } +}; +struct AnyLt { + template <typename A, typename B> + bool operator()(const A& a, const B& b) const { return a < b; } +}; +struct AnyGt { + template <typename A, typename B> + bool operator()(const A& a, const B& b) const { return a > b; } +}; +struct AnyLe { + template <typename A, typename B> + bool operator()(const A& a, const B& b) const { return a <= b; } +}; +struct AnyGe { + template <typename A, typename B> + bool operator()(const A& a, const B& b) const { return a >= b; } +}; + +// A match result listener that ignores the explanation. +class DummyMatchResultListener : public MatchResultListener { + public: + DummyMatchResultListener() : MatchResultListener(nullptr) {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(DummyMatchResultListener); +}; + +// A match result listener that forwards the explanation to a given +// ostream. The difference between this and MatchResultListener is +// that the former is concrete. +class StreamMatchResultListener : public MatchResultListener { + public: + explicit StreamMatchResultListener(::std::ostream* os) + : MatchResultListener(os) {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener); +}; + +struct SharedPayloadBase { + std::atomic<int> ref{1}; + void Ref() { ref.fetch_add(1, std::memory_order_relaxed); } + bool Unref() { return ref.fetch_sub(1, std::memory_order_acq_rel) == 1; } +}; + +template <typename T> +struct SharedPayload : SharedPayloadBase { + explicit SharedPayload(const T& v) : value(v) {} + explicit SharedPayload(T&& v) : value(std::move(v)) {} + + static void Destroy(SharedPayloadBase* shared) { + delete static_cast<SharedPayload*>(shared); + } + + T value; +}; + +template <typename T> +using is_trivially_copy_constructible = +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5 + std::has_trivial_copy_constructor<T>; +#else + std::is_trivially_copy_constructible<T>; +#endif + +// An internal class for implementing Matcher<T>, which will derive +// from it. We put functionalities common to all Matcher<T> +// specializations here to avoid code duplication. +template <typename T> +class MatcherBase : private MatcherDescriberInterface { + public: + // Returns true if and only if the matcher matches x; also explains the + // match result to 'listener'. + bool MatchAndExplain(const T& x, MatchResultListener* listener) const { + GTEST_CHECK_(vtable_ != nullptr); + return vtable_->match_and_explain(*this, x, listener); + } + + // Returns true if and only if this matcher matches x. + bool Matches(const T& x) const { + DummyMatchResultListener dummy; + return MatchAndExplain(x, &dummy); + } + + // Describes this matcher to an ostream. + void DescribeTo(::std::ostream* os) const final { + GTEST_CHECK_(vtable_ != nullptr); + vtable_->describe(*this, os, false); + } + + // Describes the negation of this matcher to an ostream. + void DescribeNegationTo(::std::ostream* os) const final { + GTEST_CHECK_(vtable_ != nullptr); + vtable_->describe(*this, os, true); + } + + // Explains why x matches, or doesn't match, the matcher. + void ExplainMatchResultTo(const T& x, ::std::ostream* os) const { + StreamMatchResultListener listener(os); + MatchAndExplain(x, &listener); + } + + // Returns the describer for this matcher object; retains ownership + // of the describer, which is only guaranteed to be alive when + // this matcher object is alive. + const MatcherDescriberInterface* GetDescriber() const { + if (vtable_ == nullptr) return nullptr; + return vtable_->get_describer(*this); + } + + protected: + MatcherBase() : vtable_(nullptr) {} + + // Constructs a matcher from its implementation. + template <typename U> + explicit MatcherBase(const MatcherInterface<U>* impl) { + Init(impl); + } + + template <typename M, typename = typename std::remove_reference< + M>::type::is_gtest_matcher> + MatcherBase(M&& m) { // NOLINT + Init(std::forward<M>(m)); + } + + MatcherBase(const MatcherBase& other) + : vtable_(other.vtable_), buffer_(other.buffer_) { + if (IsShared()) buffer_.shared->Ref(); + } + + MatcherBase& operator=(const MatcherBase& other) { + if (this == &other) return *this; + Destroy(); + vtable_ = other.vtable_; + buffer_ = other.buffer_; + if (IsShared()) buffer_.shared->Ref(); + return *this; + } + + MatcherBase(MatcherBase&& other) + : vtable_(other.vtable_), buffer_(other.buffer_) { + other.vtable_ = nullptr; + } + + MatcherBase& operator=(MatcherBase&& other) { + if (this == &other) return *this; + Destroy(); + vtable_ = other.vtable_; + buffer_ = other.buffer_; + other.vtable_ = nullptr; + return *this; + } + + ~MatcherBase() override { Destroy(); } + + private: + struct VTable { + bool (*match_and_explain)(const MatcherBase&, const T&, + MatchResultListener*); + void (*describe)(const MatcherBase&, std::ostream*, bool negation); + // Returns the captured object if it implements the interface, otherwise + // returns the MatcherBase itself. + const MatcherDescriberInterface* (*get_describer)(const MatcherBase&); + // Called on shared instances when the reference count reaches 0. + void (*shared_destroy)(SharedPayloadBase*); + }; + + bool IsShared() const { + return vtable_ != nullptr && vtable_->shared_destroy != nullptr; + } + + // If the implementation uses a listener, call that. + template <typename P> + static auto MatchAndExplainImpl(const MatcherBase& m, const T& value, + MatchResultListener* listener) + -> decltype(P::Get(m).MatchAndExplain(value, listener->stream())) { + return P::Get(m).MatchAndExplain(value, listener->stream()); + } + + template <typename P> + static auto MatchAndExplainImpl(const MatcherBase& m, const T& value, + MatchResultListener* listener) + -> decltype(P::Get(m).MatchAndExplain(value, listener)) { + return P::Get(m).MatchAndExplain(value, listener); + } + + template <typename P> + static void DescribeImpl(const MatcherBase& m, std::ostream* os, + bool negation) { + if (negation) { + P::Get(m).DescribeNegationTo(os); + } else { + P::Get(m).DescribeTo(os); + } + } + + template <typename P> + static const MatcherDescriberInterface* GetDescriberImpl( + const MatcherBase& m) { + // If the impl is a MatcherDescriberInterface, then return it. + // Otherwise use MatcherBase itself. + // This allows us to implement the GetDescriber() function without support + // from the impl, but some users really want to get their impl back when + // they call GetDescriber(). + // We use std::get on a tuple as a workaround of not having `if constexpr`. + return std::get<( + std::is_convertible<decltype(&P::Get(m)), + const MatcherDescriberInterface*>::value + ? 1 + : 0)>(std::make_tuple(&m, &P::Get(m))); + } + + template <typename P> + const VTable* GetVTable() { + static constexpr VTable kVTable = {&MatchAndExplainImpl<P>, + &DescribeImpl<P>, &GetDescriberImpl<P>, + P::shared_destroy}; + return &kVTable; + } + + union Buffer { + // Add some types to give Buffer some common alignment/size use cases. + void* ptr; + double d; + int64_t i; + // And add one for the out-of-line cases. + SharedPayloadBase* shared; + }; + + void Destroy() { + if (IsShared() && buffer_.shared->Unref()) { + vtable_->shared_destroy(buffer_.shared); + } + } + + template <typename M> + static constexpr bool IsInlined() { + return sizeof(M) <= sizeof(Buffer) && alignof(M) <= alignof(Buffer) && + is_trivially_copy_constructible<M>::value && + std::is_trivially_destructible<M>::value; + } + + template <typename M, bool = MatcherBase::IsInlined<M>()> + struct ValuePolicy { + static const M& Get(const MatcherBase& m) { + // When inlined along with Init, need to be explicit to avoid violating + // strict aliasing rules. + const M *ptr = static_cast<const M*>( + static_cast<const void*>(&m.buffer_)); + return *ptr; + } + static void Init(MatcherBase& m, M impl) { + ::new (static_cast<void*>(&m.buffer_)) M(impl); + } + static constexpr auto shared_destroy = nullptr; + }; + + template <typename M> + struct ValuePolicy<M, false> { + using Shared = SharedPayload<M>; + static const M& Get(const MatcherBase& m) { + return static_cast<Shared*>(m.buffer_.shared)->value; + } + template <typename Arg> + static void Init(MatcherBase& m, Arg&& arg) { + m.buffer_.shared = new Shared(std::forward<Arg>(arg)); + } + static constexpr auto shared_destroy = &Shared::Destroy; + }; + + template <typename U, bool B> + struct ValuePolicy<const MatcherInterface<U>*, B> { + using M = const MatcherInterface<U>; + using Shared = SharedPayload<std::unique_ptr<M>>; + static const M& Get(const MatcherBase& m) { + return *static_cast<Shared*>(m.buffer_.shared)->value; + } + static void Init(MatcherBase& m, M* impl) { + m.buffer_.shared = new Shared(std::unique_ptr<M>(impl)); + } + + static constexpr auto shared_destroy = &Shared::Destroy; + }; + + template <typename M> + void Init(M&& m) { + using MM = typename std::decay<M>::type; + using Policy = ValuePolicy<MM>; + vtable_ = GetVTable<Policy>(); + Policy::Init(*this, std::forward<M>(m)); + } + + const VTable* vtable_; + Buffer buffer_; +}; + +} // namespace internal + +// A Matcher<T> is a copyable and IMMUTABLE (except by assignment) +// object that can check whether a value of type T matches. The +// implementation of Matcher<T> is just a std::shared_ptr to const +// MatcherInterface<T>. Don't inherit from Matcher! +template <typename T> +class Matcher : public internal::MatcherBase<T> { + public: + // Constructs a null matcher. Needed for storing Matcher objects in STL + // containers. A default-constructed matcher is not yet initialized. You + // cannot use it until a valid value has been assigned to it. + explicit Matcher() {} // NOLINT + + // Constructs a matcher from its implementation. + explicit Matcher(const MatcherInterface<const T&>* impl) + : internal::MatcherBase<T>(impl) {} + + template <typename U> + explicit Matcher( + const MatcherInterface<U>* impl, + typename std::enable_if<!std::is_same<U, const U&>::value>::type* = + nullptr) + : internal::MatcherBase<T>(impl) {} + + template <typename M, typename = typename std::remove_reference< + M>::type::is_gtest_matcher> + Matcher(M&& m) : internal::MatcherBase<T>(std::forward<M>(m)) {} // NOLINT + + // Implicit constructor here allows people to write + // EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes + Matcher(T value); // NOLINT +}; + +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a std::string +// matcher is expected. +template <> +class GTEST_API_ Matcher<const std::string&> + : public internal::MatcherBase<const std::string&> { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface<const std::string&>* impl) + : internal::MatcherBase<const std::string&>(impl) {} + + template <typename M, typename = typename std::remove_reference< + M>::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase<const std::string&>(std::forward<M>(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +template <> +class GTEST_API_ Matcher<std::string> + : public internal::MatcherBase<std::string> { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface<const std::string&>* impl) + : internal::MatcherBase<std::string>(impl) {} + explicit Matcher(const MatcherInterface<std::string>* impl) + : internal::MatcherBase<std::string>(impl) {} + + template <typename M, typename = typename std::remove_reference< + M>::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase<std::string>(std::forward<M>(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +#if GTEST_INTERNAL_HAS_STRING_VIEW +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a absl::string_view +// matcher is expected. +template <> +class GTEST_API_ Matcher<const internal::StringView&> + : public internal::MatcherBase<const internal::StringView&> { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface<const internal::StringView&>* impl) + : internal::MatcherBase<const internal::StringView&>(impl) {} + + template <typename M, typename = typename std::remove_reference< + M>::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase<const internal::StringView&>(std::forward<M>(m)) { + } + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass absl::string_views or std::string_views directly. + Matcher(internal::StringView s); // NOLINT +}; + +template <> +class GTEST_API_ Matcher<internal::StringView> + : public internal::MatcherBase<internal::StringView> { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface<const internal::StringView&>* impl) + : internal::MatcherBase<internal::StringView>(impl) {} + explicit Matcher(const MatcherInterface<internal::StringView>* impl) + : internal::MatcherBase<internal::StringView>(impl) {} + + template <typename M, typename = typename std::remove_reference< + M>::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase<internal::StringView>(std::forward<M>(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass absl::string_views or std::string_views directly. + Matcher(internal::StringView s); // NOLINT +}; +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + +// Prints a matcher in a human-readable format. +template <typename T> +std::ostream& operator<<(std::ostream& os, const Matcher<T>& matcher) { + matcher.DescribeTo(&os); + return os; +} + +// The PolymorphicMatcher class template makes it easy to implement a +// polymorphic matcher (i.e. a matcher that can match values of more +// than one type, e.g. Eq(n) and NotNull()). +// +// To define a polymorphic matcher, a user should provide an Impl +// class that has a DescribeTo() method and a DescribeNegationTo() +// method, and define a member function (or member function template) +// +// bool MatchAndExplain(const Value& value, +// MatchResultListener* listener) const; +// +// See the definition of NotNull() for a complete example. +template <class Impl> +class PolymorphicMatcher { + public: + explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {} + + // Returns a mutable reference to the underlying matcher + // implementation object. + Impl& mutable_impl() { return impl_; } + + // Returns an immutable reference to the underlying matcher + // implementation object. + const Impl& impl() const { return impl_; } + + template <typename T> + operator Matcher<T>() const { + return Matcher<T>(new MonomorphicImpl<const T&>(impl_)); + } + + private: + template <typename T> + class MonomorphicImpl : public MatcherInterface<T> { + public: + explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} + + void DescribeTo(::std::ostream* os) const override { impl_.DescribeTo(os); } + + void DescribeNegationTo(::std::ostream* os) const override { + impl_.DescribeNegationTo(os); + } + + bool MatchAndExplain(T x, MatchResultListener* listener) const override { + return impl_.MatchAndExplain(x, listener); + } + + private: + const Impl impl_; + }; + + Impl impl_; +}; + +// Creates a matcher from its implementation. +// DEPRECATED: Especially in the generic code, prefer: +// Matcher<T>(new MyMatcherImpl<const T&>(...)); +// +// MakeMatcher may create a Matcher that accepts its argument by value, which +// leads to unnecessary copies & lack of support for non-copyable types. +template <typename T> +inline Matcher<T> MakeMatcher(const MatcherInterface<T>* impl) { + return Matcher<T>(impl); +} + +// Creates a polymorphic matcher from its implementation. This is +// easier to use than the PolymorphicMatcher<Impl> constructor as it +// doesn't require you to explicitly write the template argument, e.g. +// +// MakePolymorphicMatcher(foo); +// vs +// PolymorphicMatcher<TypeOfFoo>(foo); +template <class Impl> +inline PolymorphicMatcher<Impl> MakePolymorphicMatcher(const Impl& impl) { + return PolymorphicMatcher<Impl>(impl); +} + +namespace internal { +// Implements a matcher that compares a given value with a +// pre-supplied value using one of the ==, <=, <, etc, operators. The +// two values being compared don't have to have the same type. +// +// The matcher defined here is polymorphic (for example, Eq(5) can be +// used to match an int, a short, a double, etc). Therefore we use +// a template type conversion operator in the implementation. +// +// The following template definition assumes that the Rhs parameter is +// a "bare" type (i.e. neither 'const T' nor 'T&'). +template <typename D, typename Rhs, typename Op> +class ComparisonBase { + public: + explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {} + + using is_gtest_matcher = void; + + template <typename Lhs> + bool MatchAndExplain(const Lhs& lhs, std::ostream*) const { + return Op()(lhs, Unwrap(rhs_)); + } + void DescribeTo(std::ostream* os) const { + *os << D::Desc() << " "; + UniversalPrint(Unwrap(rhs_), os); + } + void DescribeNegationTo(std::ostream* os) const { + *os << D::NegatedDesc() << " "; + UniversalPrint(Unwrap(rhs_), os); + } + + private: + template <typename T> + static const T& Unwrap(const T& v) { + return v; + } + template <typename T> + static const T& Unwrap(std::reference_wrapper<T> v) { + return v; + } + + Rhs rhs_; +}; + +template <typename Rhs> +class EqMatcher : public ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq> { + public: + explicit EqMatcher(const Rhs& rhs) + : ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq>(rhs) { } + static const char* Desc() { return "is equal to"; } + static const char* NegatedDesc() { return "isn't equal to"; } +}; +template <typename Rhs> +class NeMatcher : public ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe> { + public: + explicit NeMatcher(const Rhs& rhs) + : ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe>(rhs) { } + static const char* Desc() { return "isn't equal to"; } + static const char* NegatedDesc() { return "is equal to"; } +}; +template <typename Rhs> +class LtMatcher : public ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt> { + public: + explicit LtMatcher(const Rhs& rhs) + : ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt>(rhs) { } + static const char* Desc() { return "is <"; } + static const char* NegatedDesc() { return "isn't <"; } +}; +template <typename Rhs> +class GtMatcher : public ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt> { + public: + explicit GtMatcher(const Rhs& rhs) + : ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt>(rhs) { } + static const char* Desc() { return "is >"; } + static const char* NegatedDesc() { return "isn't >"; } +}; +template <typename Rhs> +class LeMatcher : public ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe> { + public: + explicit LeMatcher(const Rhs& rhs) + : ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe>(rhs) { } + static const char* Desc() { return "is <="; } + static const char* NegatedDesc() { return "isn't <="; } +}; +template <typename Rhs> +class GeMatcher : public ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe> { + public: + explicit GeMatcher(const Rhs& rhs) + : ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe>(rhs) { } + static const char* Desc() { return "is >="; } + static const char* NegatedDesc() { return "isn't >="; } +}; + +template <typename T, typename = typename std::enable_if< + std::is_constructible<std::string, T>::value>::type> +using StringLike = T; + +// Implements polymorphic matchers MatchesRegex(regex) and +// ContainsRegex(regex), which can be used as a Matcher<T> as long as +// T can be converted to a string. +class MatchesRegexMatcher { + public: + MatchesRegexMatcher(const RE* regex, bool full_match) + : regex_(regex), full_match_(full_match) {} + +#if GTEST_INTERNAL_HAS_STRING_VIEW + bool MatchAndExplain(const internal::StringView& s, + MatchResultListener* listener) const { + return MatchAndExplain(std::string(s), listener); + } +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template <typename CharType> + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != nullptr && MatchAndExplain(std::string(s), listener); + } + + // Matches anything that can convert to std::string. + // + // This is a template, not just a plain function with const std::string&, + // because absl::string_view has some interfering non-explicit constructors. + template <class MatcheeStringType> + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const std::string& s2(s); + return full_match_ ? RE::FullMatch(s2, *regex_) + : RE::PartialMatch(s2, *regex_); + } + + void DescribeTo(::std::ostream* os) const { + *os << (full_match_ ? "matches" : "contains") << " regular expression "; + UniversalPrinter<std::string>::Print(regex_->pattern(), os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't " << (full_match_ ? "match" : "contain") + << " regular expression "; + UniversalPrinter<std::string>::Print(regex_->pattern(), os); + } + + private: + const std::shared_ptr<const RE> regex_; + const bool full_match_; +}; +} // namespace internal + +// Matches a string that fully matches regular expression 'regex'. +// The matcher takes ownership of 'regex'. +inline PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex( + const internal::RE* regex) { + return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true)); +} +template <typename T = std::string> +PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex( + const internal::StringLike<T>& regex) { + return MatchesRegex(new internal::RE(std::string(regex))); +} + +// Matches a string that contains regular expression 'regex'. +// The matcher takes ownership of 'regex'. +inline PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex( + const internal::RE* regex) { + return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false)); +} +template <typename T = std::string> +PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex( + const internal::StringLike<T>& regex) { + return ContainsRegex(new internal::RE(std::string(regex))); +} + +// Creates a polymorphic matcher that matches anything equal to x. +// Note: if the parameter of Eq() were declared as const T&, Eq("foo") +// wouldn't compile. +template <typename T> +inline internal::EqMatcher<T> Eq(T x) { return internal::EqMatcher<T>(x); } + +// Constructs a Matcher<T> from a 'value' of type T. The constructed +// matcher matches any value that's equal to 'value'. +template <typename T> +Matcher<T>::Matcher(T value) { *this = Eq(value); } + +// Creates a monomorphic matcher that matches anything with type Lhs +// and equal to rhs. A user may need to use this instead of Eq(...) +// in order to resolve an overloading ambiguity. +// +// TypedEq<T>(x) is just a convenient short-hand for Matcher<T>(Eq(x)) +// or Matcher<T>(x), but more readable than the latter. +// +// We could define similar monomorphic matchers for other comparison +// operations (e.g. TypedLt, TypedGe, and etc), but decided not to do +// it yet as those are used much less than Eq() in practice. A user +// can always write Matcher<T>(Lt(5)) to be explicit about the type, +// for example. +template <typename Lhs, typename Rhs> +inline Matcher<Lhs> TypedEq(const Rhs& rhs) { return Eq(rhs); } + +// Creates a polymorphic matcher that matches anything >= x. +template <typename Rhs> +inline internal::GeMatcher<Rhs> Ge(Rhs x) { + return internal::GeMatcher<Rhs>(x); +} + +// Creates a polymorphic matcher that matches anything > x. +template <typename Rhs> +inline internal::GtMatcher<Rhs> Gt(Rhs x) { + return internal::GtMatcher<Rhs>(x); +} + +// Creates a polymorphic matcher that matches anything <= x. +template <typename Rhs> +inline internal::LeMatcher<Rhs> Le(Rhs x) { + return internal::LeMatcher<Rhs>(x); +} + +// Creates a polymorphic matcher that matches anything < x. +template <typename Rhs> +inline internal::LtMatcher<Rhs> Lt(Rhs x) { + return internal::LtMatcher<Rhs>(x); +} + +// Creates a polymorphic matcher that matches anything != x. +template <typename Rhs> +inline internal::NeMatcher<Rhs> Ne(Rhs x) { + return internal::NeMatcher<Rhs>(x); +} +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 5046 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ + +#include <stdio.h> +#include <memory> + +namespace testing { +namespace internal { + +GTEST_DECLARE_string_(internal_run_death_test); + +// Names of the flags (needed for parsing Google Test flags). +const char kDeathTestStyleFlag[] = "death_test_style"; +const char kDeathTestUseFork[] = "death_test_use_fork"; +const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; + +#if GTEST_HAS_DEATH_TEST + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// DeathTest is a class that hides much of the complexity of the +// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method +// returns a concrete class that depends on the prevailing death test +// style, as defined by the --gtest_death_test_style and/or +// --gtest_internal_run_death_test flags. + +// In describing the results of death tests, these terms are used with +// the corresponding definitions: +// +// exit status: The integer exit information in the format specified +// by wait(2) +// exit code: The integer code passed to exit(3), _exit(2), or +// returned from main() +class GTEST_API_ DeathTest { + public: + // Create returns false if there was an error determining the + // appropriate action to take for the current death test; for example, + // if the gtest_death_test_style flag is set to an invalid value. + // The LastMessage method will return a more detailed message in that + // case. Otherwise, the DeathTest pointer pointed to by the "test" + // argument is set. If the death test should be skipped, the pointer + // is set to NULL; otherwise, it is set to the address of a new concrete + // DeathTest object that controls the execution of the current test. + static bool Create(const char* statement, Matcher<const std::string&> matcher, + const char* file, int line, DeathTest** test); + DeathTest(); + virtual ~DeathTest() { } + + // A helper class that aborts a death test when it's deleted. + class ReturnSentinel { + public: + explicit ReturnSentinel(DeathTest* test) : test_(test) { } + ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); } + private: + DeathTest* const test_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel); + } GTEST_ATTRIBUTE_UNUSED_; + + // An enumeration of possible roles that may be taken when a death + // test is encountered. EXECUTE means that the death test logic should + // be executed immediately. OVERSEE means that the program should prepare + // the appropriate environment for a child process to execute the death + // test, then wait for it to complete. + enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; + + // An enumeration of the three reasons that a test might be aborted. + enum AbortReason { + TEST_ENCOUNTERED_RETURN_STATEMENT, + TEST_THREW_EXCEPTION, + TEST_DID_NOT_DIE + }; + + // Assumes one of the above roles. + virtual TestRole AssumeRole() = 0; + + // Waits for the death test to finish and returns its status. + virtual int Wait() = 0; + + // Returns true if the death test passed; that is, the test process + // exited during the test, its exit status matches a user-supplied + // predicate, and its stderr output matches a user-supplied regular + // expression. + // The user-supplied predicate may be a macro expression rather + // than a function pointer or functor, or else Wait and Passed could + // be combined. + virtual bool Passed(bool exit_status_ok) = 0; + + // Signals that the death test did not die as expected. + virtual void Abort(AbortReason reason) = 0; + + // Returns a human-readable outcome message regarding the outcome of + // the last death test. + static const char* LastMessage(); + + static void set_last_death_test_message(const std::string& message); + + private: + // A string containing a description of the outcome of the last death test. + static std::string last_death_test_message_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest); +}; + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// Factory interface for death tests. May be mocked out for testing. +class DeathTestFactory { + public: + virtual ~DeathTestFactory() { } + virtual bool Create(const char* statement, + Matcher<const std::string&> matcher, const char* file, + int line, DeathTest** test) = 0; +}; + +// A concrete DeathTestFactory implementation for normal use. +class DefaultDeathTestFactory : public DeathTestFactory { + public: + bool Create(const char* statement, Matcher<const std::string&> matcher, + const char* file, int line, DeathTest** test) override; +}; + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +GTEST_API_ bool ExitedUnsuccessfully(int exit_status); + +// A string passed to EXPECT_DEATH (etc.) is caught by one of these overloads +// and interpreted as a regex (rather than an Eq matcher) for legacy +// compatibility. +inline Matcher<const ::std::string&> MakeDeathTestMatcher( + ::testing::internal::RE regex) { + return ContainsRegex(regex.pattern()); +} +inline Matcher<const ::std::string&> MakeDeathTestMatcher(const char* regex) { + return ContainsRegex(regex); +} +inline Matcher<const ::std::string&> MakeDeathTestMatcher( + const ::std::string& regex) { + return ContainsRegex(regex); +} + +// If a Matcher<const ::std::string&> is passed to EXPECT_DEATH (etc.), it's +// used directly. +inline Matcher<const ::std::string&> MakeDeathTestMatcher( + Matcher<const ::std::string&> matcher) { + return matcher; +} + +// Traps C++ exceptions escaping statement and reports them as test +// failures. Note that trapping SEH exceptions is not implemented here. +# if GTEST_HAS_EXCEPTIONS +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (const ::std::exception& gtest_exception) { \ + fprintf(\ + stderr, \ + "\n%s: Caught std::exception-derived exception escaping the " \ + "death test statement. Exception message: %s\n", \ + ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \ + gtest_exception.what()); \ + fflush(stderr); \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } catch (...) { \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } + +# else +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) + +# endif + +// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, +// ASSERT_EXIT*, and EXPECT_EXIT*. +#define GTEST_DEATH_TEST_(statement, predicate, regex_or_matcher, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + ::testing::internal::DeathTest* gtest_dt; \ + if (!::testing::internal::DeathTest::Create( \ + #statement, \ + ::testing::internal::MakeDeathTestMatcher(regex_or_matcher), \ + __FILE__, __LINE__, >est_dt)) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + if (gtest_dt != nullptr) { \ + std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr(gtest_dt); \ + switch (gtest_dt->AssumeRole()) { \ + case ::testing::internal::DeathTest::OVERSEE_TEST: \ + if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + break; \ + case ::testing::internal::DeathTest::EXECUTE_TEST: { \ + ::testing::internal::DeathTest::ReturnSentinel gtest_sentinel( \ + gtest_dt); \ + GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \ + gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ + break; \ + } \ + default: \ + break; \ + } \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__) \ + : fail(::testing::internal::DeathTest::LastMessage()) +// The symbol "fail" here expands to something into which a message +// can be streamed. + +// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in +// NDEBUG mode. In this case we need the statements to be executed and the macro +// must accept a streamed message even though the message is never printed. +// The regex object is not evaluated, but it is used to prevent "unused" +// warnings and to avoid an expression that doesn't compile in debug mode. +#define GTEST_EXECUTE_STATEMENT_(statement, regex_or_matcher) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } else if (!::testing::internal::AlwaysTrue()) { \ + ::testing::internal::MakeDeathTestMatcher(regex_or_matcher); \ + } else \ + ::testing::Message() + +// A class representing the parsed contents of the +// --gtest_internal_run_death_test flag, as it existed when +// RUN_ALL_TESTS was called. +class InternalRunDeathTestFlag { + public: + InternalRunDeathTestFlag(const std::string& a_file, + int a_line, + int an_index, + int a_write_fd) + : file_(a_file), line_(a_line), index_(an_index), + write_fd_(a_write_fd) {} + + ~InternalRunDeathTestFlag() { + if (write_fd_ >= 0) + posix::Close(write_fd_); + } + + const std::string& file() const { return file_; } + int line() const { return line_; } + int index() const { return index_; } + int write_fd() const { return write_fd_; } + + private: + std::string file_; + int line_; + int index_; + int write_fd_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag); +}; + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + +namespace testing { + +// This flag controls the style of death tests. Valid values are "threadsafe", +// meaning that the death test child process will re-execute the test binary +// from the start, running only a single death test, or "fast", +// meaning that the child process will execute the test logic immediately +// after forking. +GTEST_DECLARE_string_(death_test_style); + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +GTEST_API_ bool InDeathTestChild(); + +} // namespace internal + +// The following macros are useful for writing death tests. + +// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is +// executed: +// +// 1. It generates a warning if there is more than one active +// thread. This is because it's safe to fork() or clone() only +// when there is a single thread. +// +// 2. The parent process clone()s a sub-process and runs the death +// test in it; the sub-process exits with code 0 at the end of the +// death test, if it hasn't exited already. +// +// 3. The parent process waits for the sub-process to terminate. +// +// 4. The parent process checks the exit code and error message of +// the sub-process. +// +// Examples: +// +// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number"); +// for (int i = 0; i < 5; i++) { +// EXPECT_DEATH(server.ProcessRequest(i), +// "Invalid request .* in ProcessRequest()") +// << "Failed to die on request " << i; +// } +// +// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); +// +// bool KilledBySIGHUP(int exit_code) { +// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP; +// } +// +// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); +// +// On the regular expressions used in death tests: +// +// GOOGLETEST_CM0005 DO NOT DELETE +// On POSIX-compliant systems (*nix), we use the <regex.h> library, +// which uses the POSIX extended regex syntax. +// +// On other platforms (e.g. Windows or Mac), we only support a simple regex +// syntax implemented as part of Google Test. This limited +// implementation should be enough most of the time when writing +// death tests; though it lacks many features you can find in PCRE +// or POSIX extended regex syntax. For example, we don't support +// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and +// repetition count ("x{5,7}"), among others. +// +// Below is the syntax that we do support. We chose it to be a +// subset of both PCRE and POSIX extended regex, so it's easy to +// learn wherever you come from. In the following: 'A' denotes a +// literal character, period (.), or a single \\ escape sequence; +// 'x' and 'y' denote regular expressions; 'm' and 'n' are for +// natural numbers. +// +// c matches any literal character c +// \\d matches any decimal digit +// \\D matches any character that's not a decimal digit +// \\f matches \f +// \\n matches \n +// \\r matches \r +// \\s matches any ASCII whitespace, including \n +// \\S matches any character that's not a whitespace +// \\t matches \t +// \\v matches \v +// \\w matches any letter, _, or decimal digit +// \\W matches any character that \\w doesn't match +// \\c matches any literal character c, which must be a punctuation +// . matches any single character except \n +// A? matches 0 or 1 occurrences of A +// A* matches 0 or many occurrences of A +// A+ matches 1 or many occurrences of A +// ^ matches the beginning of a string (not that of each line) +// $ matches the end of a string (not that of each line) +// xy matches x followed by y +// +// If you accidentally use PCRE or POSIX extended regex features +// not implemented by us, you will get a run-time failure. In that +// case, please try to rewrite your regular expression within the +// above syntax. +// +// This implementation is *not* meant to be as highly tuned or robust +// as a compiled regex library, but should perform well enough for a +// death test, which already incurs significant overhead by launching +// a child process. +// +// Known caveats: +// +// A "threadsafe" style death test obtains the path to the test +// program from argv[0] and re-executes it in the sub-process. For +// simplicity, the current implementation doesn't search the PATH +// when launching the sub-process. This means that the user must +// invoke the test program via a path that contains at least one +// path separator (e.g. path/to/foo_test and +// /absolute/path/to/bar_test are fine, but foo_test is not). This +// is rarely a problem as people usually don't put the test binary +// directory in PATH. +// + +// Asserts that a given statement causes the program to exit, with an +// integer exit status that satisfies predicate, and emitting error output +// that matches regex. +# define ASSERT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_) + +// Like ASSERT_EXIT, but continues on to successive tests in the +// test suite, if any: +# define EXPECT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_) + +// Asserts that a given statement causes the program to exit, either by +// explicitly exiting with a nonzero exit code or being killed by a +// signal, and emitting error output that matches regex. +# define ASSERT_DEATH(statement, regex) \ + ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Like ASSERT_DEATH, but continues on to successive tests in the +// test suite, if any: +# define EXPECT_DEATH(statement, regex) \ + EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: + +// Tests that an exit code describes a normal exit with a given exit code. +class GTEST_API_ ExitedWithCode { + public: + explicit ExitedWithCode(int exit_code); + ExitedWithCode(const ExitedWithCode&) = default; + void operator=(const ExitedWithCode& other) = delete; + bool operator()(int exit_status) const; + private: + const int exit_code_; +}; + +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +// Tests that an exit code describes an exit due to termination by a +// given signal. +// GOOGLETEST_CM0006 DO NOT DELETE +class GTEST_API_ KilledBySignal { + public: + explicit KilledBySignal(int signum); + bool operator()(int exit_status) const; + private: + const int signum_; +}; +# endif // !GTEST_OS_WINDOWS + +// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. +// The death testing framework causes this to have interesting semantics, +// since the sideeffects of the call are only visible in opt mode, and not +// in debug mode. +// +// In practice, this can be used to test functions that utilize the +// LOG(DFATAL) macro using the following style: +// +// int DieInDebugOr12(int* sideeffect) { +// if (sideeffect) { +// *sideeffect = 12; +// } +// LOG(DFATAL) << "death"; +// return 12; +// } +// +// TEST(TestSuite, TestDieOr12WorksInDgbAndOpt) { +// int sideeffect = 0; +// // Only asserts in dbg. +// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); +// +// #ifdef NDEBUG +// // opt-mode has sideeffect visible. +// EXPECT_EQ(12, sideeffect); +// #else +// // dbg-mode no visible sideeffect. +// EXPECT_EQ(0, sideeffect); +// #endif +// } +// +// This will assert that DieInDebugReturn12InOpt() crashes in debug +// mode, usually due to a DCHECK or LOG(DFATAL), but returns the +// appropriate fallback value (12 in this case) in opt mode. If you +// need to test that a function has appropriate side-effects in opt +// mode, include assertions against the side-effects. A general +// pattern for this is: +// +// EXPECT_DEBUG_DEATH({ +// // Side-effects here will have an effect after this statement in +// // opt mode, but none in debug mode. +// EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); +// }, "death"); +// +# ifdef NDEBUG + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +# else + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + EXPECT_DEATH(statement, regex) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + ASSERT_DEATH(statement, regex) + +# endif // NDEBUG for EXPECT_DEBUG_DEATH +#endif // GTEST_HAS_DEATH_TEST + +// This macro is used for implementing macros such as +// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where +// death tests are not supported. Those macros must compile on such systems +// if and only if EXPECT_DEATH and ASSERT_DEATH compile with the same parameters +// on systems that support death tests. This allows one to write such a macro on +// a system that does not support death tests and be sure that it will compile +// on a death-test supporting system. It is exposed publicly so that systems +// that have death-tests with stricter requirements than GTEST_HAS_DEATH_TEST +// can write their own equivalent of EXPECT_DEATH_IF_SUPPORTED and +// ASSERT_DEATH_IF_SUPPORTED. +// +// Parameters: +// statement - A statement that a macro such as EXPECT_DEATH would test +// for program termination. This macro has to make sure this +// statement is compiled but not executed, to ensure that +// EXPECT_DEATH_IF_SUPPORTED compiles with a certain +// parameter if and only if EXPECT_DEATH compiles with it. +// regex - A regex that a macro such as EXPECT_DEATH would use to test +// the output of statement. This parameter has to be +// compiled but not evaluated by this macro, to ensure that +// this macro only accepts expressions that a macro such as +// EXPECT_DEATH would accept. +// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED +// and a return statement for ASSERT_DEATH_IF_SUPPORTED. +// This ensures that ASSERT_DEATH_IF_SUPPORTED will not +// compile inside functions where ASSERT_DEATH doesn't +// compile. +// +// The branch that has an always false condition is used to ensure that +// statement and regex are compiled (and thus syntactically correct) but +// never executed. The unreachable code macro protects the terminator +// statement from generating an 'unreachable code' warning in case +// statement unconditionally returns or throws. The Message constructor at +// the end allows the syntax of streaming additional messages into the +// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. +# define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_LOG_(WARNING) \ + << "Death tests are not supported on this platform.\n" \ + << "Statement '" #statement "' cannot be verified."; \ + } else if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::RE::PartialMatch(".*", (regex)); \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + terminator; \ + } else \ + ::testing::Message() + +// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and +// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if +// death tests are supported; otherwise they just issue a warning. This is +// useful when you are combining death test assertions with normal test +// assertions in one test. +#if GTEST_HAS_DEATH_TEST +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + EXPECT_DEATH(statement, regex) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + ASSERT_DEATH(statement, regex) +#else +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, ) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, return) +#endif + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. +// +// Macros and functions for implementing parameterized tests +// in Google C++ Testing and Mocking Framework (Google Test) +// +// GOOGLETEST_CM0001 DO NOT DELETE +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ + +// Value-parameterized tests allow you to test your code with different +// parameters without writing multiple copies of the same test. +// +// Here is how you use value-parameterized tests: + +#if 0 + +// To write value-parameterized tests, first you should define a fixture +// class. It is usually derived from testing::TestWithParam<T> (see below for +// another inheritance scheme that's sometimes useful in more complicated +// class hierarchies), where the type of your parameter values. +// TestWithParam<T> is itself derived from testing::Test. T can be any +// copyable type. If it's a raw pointer, you are responsible for managing the +// lifespan of the pointed values. + +class FooTest : public ::testing::TestWithParam<const char*> { + // You can implement all the usual class fixture members here. +}; + +// Then, use the TEST_P macro to define as many parameterized tests +// for this fixture as you want. The _P suffix is for "parameterized" +// or "pattern", whichever you prefer to think. + +TEST_P(FooTest, DoesBlah) { + // Inside a test, access the test parameter with the GetParam() method + // of the TestWithParam<T> class: + EXPECT_TRUE(foo.Blah(GetParam())); + ... +} + +TEST_P(FooTest, HasBlahBlah) { + ... +} + +// Finally, you can use INSTANTIATE_TEST_SUITE_P to instantiate the test +// case with any set of parameters you want. Google Test defines a number +// of functions for generating test parameters. They return what we call +// (surprise!) parameter generators. Here is a summary of them, which +// are all in the testing namespace: +// +// +// Range(begin, end [, step]) - Yields values {begin, begin+step, +// begin+step+step, ...}. The values do not +// include end. step defaults to 1. +// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}. +// ValuesIn(container) - Yields values from a C-style array, an STL +// ValuesIn(begin,end) container, or an iterator range [begin, end). +// Bool() - Yields sequence {false, true}. +// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product +// for the math savvy) of the values generated +// by the N generators. +// +// For more details, see comments at the definitions of these functions below +// in this file. +// +// The following statement will instantiate tests from the FooTest test suite +// each with parameter values "meeny", "miny", and "moe". + +INSTANTIATE_TEST_SUITE_P(InstantiationName, + FooTest, + Values("meeny", "miny", "moe")); + +// To distinguish different instances of the pattern, (yes, you +// can instantiate it more than once) the first argument to the +// INSTANTIATE_TEST_SUITE_P macro is a prefix that will be added to the +// actual test suite name. Remember to pick unique prefixes for different +// instantiations. The tests from the instantiation above will have +// these names: +// +// * InstantiationName/FooTest.DoesBlah/0 for "meeny" +// * InstantiationName/FooTest.DoesBlah/1 for "miny" +// * InstantiationName/FooTest.DoesBlah/2 for "moe" +// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny" +// * InstantiationName/FooTest.HasBlahBlah/1 for "miny" +// * InstantiationName/FooTest.HasBlahBlah/2 for "moe" +// +// You can use these names in --gtest_filter. +// +// This statement will instantiate all tests from FooTest again, each +// with parameter values "cat" and "dog": + +const char* pets[] = {"cat", "dog"}; +INSTANTIATE_TEST_SUITE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); + +// The tests from the instantiation above will have these names: +// +// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog" +// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog" +// +// Please note that INSTANTIATE_TEST_SUITE_P will instantiate all tests +// in the given test suite, whether their definitions come before or +// AFTER the INSTANTIATE_TEST_SUITE_P statement. +// +// Please also note that generator expressions (including parameters to the +// generators) are evaluated in InitGoogleTest(), after main() has started. +// This allows the user on one hand, to adjust generator parameters in order +// to dynamically determine a set of tests to run and on the other hand, +// give the user a chance to inspect the generated tests with Google Test +// reflection API before RUN_ALL_TESTS() is executed. +// +// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc +// for more examples. +// +// In the future, we plan to publish the API for defining new parameter +// generators. But for now this interface remains part of the internal +// implementation and is subject to change. +// +// +// A parameterized test fixture must be derived from testing::Test and from +// testing::WithParamInterface<T>, where T is the type of the parameter +// values. Inheriting from TestWithParam<T> satisfies that requirement because +// TestWithParam<T> inherits from both Test and WithParamInterface. In more +// complicated hierarchies, however, it is occasionally useful to inherit +// separately from Test and WithParamInterface. For example: + +class BaseTest : public ::testing::Test { + // You can inherit all the usual members for a non-parameterized test + // fixture here. +}; + +class DerivedTest : public BaseTest, public ::testing::WithParamInterface<int> { + // The usual test fixture members go here too. +}; + +TEST_F(BaseTest, HasFoo) { + // This is an ordinary non-parameterized test. +} + +TEST_P(DerivedTest, DoesBlah) { + // GetParam works just the same here as if you inherit from TestWithParam. + EXPECT_TRUE(foo.Blah(GetParam())); +} + +#endif // 0 + +#include <iterator> +#include <utility> + +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + + +// Type and function utilities for implementing parameterized tests. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ + +#include <ctype.h> + +#include <cassert> +#include <iterator> +#include <memory> +#include <set> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> + +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. +// +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ + +#include <iosfwd> +#include <vector> + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// A copyable object representing the result of a test part (i.e. an +// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). +// +// Don't inherit from TestPartResult as its destructor is not virtual. +class GTEST_API_ TestPartResult { + public: + // The possible outcomes of a test part (i.e. an assertion or an + // explicit SUCCEED(), FAIL(), or ADD_FAILURE()). + enum Type { + kSuccess, // Succeeded. + kNonFatalFailure, // Failed but the test can continue. + kFatalFailure, // Failed and the test should be terminated. + kSkip // Skipped. + }; + + // C'tor. TestPartResult does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestPartResult object. + TestPartResult(Type a_type, const char* a_file_name, int a_line_number, + const char* a_message) + : type_(a_type), + file_name_(a_file_name == nullptr ? "" : a_file_name), + line_number_(a_line_number), + summary_(ExtractSummary(a_message)), + message_(a_message) {} + + // Gets the outcome of the test part. + Type type() const { return type_; } + + // Gets the name of the source file where the test part took place, or + // NULL if it's unknown. + const char* file_name() const { + return file_name_.empty() ? nullptr : file_name_.c_str(); + } + + // Gets the line in the source file where the test part took place, + // or -1 if it's unknown. + int line_number() const { return line_number_; } + + // Gets the summary of the failure message. + const char* summary() const { return summary_.c_str(); } + + // Gets the message associated with the test part. + const char* message() const { return message_.c_str(); } + + // Returns true if and only if the test part was skipped. + bool skipped() const { return type_ == kSkip; } + + // Returns true if and only if the test part passed. + bool passed() const { return type_ == kSuccess; } + + // Returns true if and only if the test part non-fatally failed. + bool nonfatally_failed() const { return type_ == kNonFatalFailure; } + + // Returns true if and only if the test part fatally failed. + bool fatally_failed() const { return type_ == kFatalFailure; } + + // Returns true if and only if the test part failed. + bool failed() const { return fatally_failed() || nonfatally_failed(); } + + private: + Type type_; + + // Gets the summary of the failure message by omitting the stack + // trace in it. + static std::string ExtractSummary(const char* message); + + // The name of the source file where the test part took place, or + // "" if the source file is unknown. + std::string file_name_; + // The line in the source file where the test part took place, or -1 + // if the line number is unknown. + int line_number_; + std::string summary_; // The test failure summary. + std::string message_; // The test failure message. +}; + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result); + +// An array of TestPartResult objects. +// +// Don't inherit from TestPartResultArray as its destructor is not +// virtual. +class GTEST_API_ TestPartResultArray { + public: + TestPartResultArray() {} + + // Appends the given TestPartResult to the array. + void Append(const TestPartResult& result); + + // Returns the TestPartResult at the given index (0-based). + const TestPartResult& GetTestPartResult(int index) const; + + // Returns the number of TestPartResult objects in the array. + int size() const; + + private: + std::vector<TestPartResult> array_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray); +}; + +// This interface knows how to report a test part result. +class GTEST_API_ TestPartResultReporterInterface { + public: + virtual ~TestPartResultReporterInterface() {} + + virtual void ReportTestPartResult(const TestPartResult& result) = 0; +}; + +namespace internal { + +// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a +// statement generates new fatal failures. To do so it registers itself as the +// current test part result reporter. Besides checking if fatal failures were +// reported, it only delegates the reporting to the former result reporter. +// The original result reporter is restored in the destructor. +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +class GTEST_API_ HasNewFatalFailureHelper + : public TestPartResultReporterInterface { + public: + HasNewFatalFailureHelper(); + ~HasNewFatalFailureHelper() override; + void ReportTestPartResult(const TestPartResult& result) override; + bool has_new_fatal_failure() const { return has_new_fatal_failure_; } + private: + bool has_new_fatal_failure_; + TestPartResultReporterInterface* original_reporter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper); +}; + +} // namespace internal + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ + +namespace testing { +// Input to a parameterized test name generator, describing a test parameter. +// Consists of the parameter value and the integer parameter index. +template <class ParamType> +struct TestParamInfo { + TestParamInfo(const ParamType& a_param, size_t an_index) : + param(a_param), + index(an_index) {} + ParamType param; + size_t index; +}; + +// A builtin parameterized test name generator which returns the result of +// testing::PrintToString. +struct PrintToStringParamName { + template <class ParamType> + std::string operator()(const TestParamInfo<ParamType>& info) const { + return PrintToString(info.param); + } +}; + +namespace internal { + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// Utility Functions + +// Outputs a message explaining invalid registration of different +// fixture class for the same test suite. This may happen when +// TEST_P macro is used to define two tests with the same name +// but in different namespaces. +GTEST_API_ void ReportInvalidTestSuiteType(const char* test_suite_name, + CodeLocation code_location); + +template <typename> class ParamGeneratorInterface; +template <typename> class ParamGenerator; + +// Interface for iterating over elements provided by an implementation +// of ParamGeneratorInterface<T>. +template <typename T> +class ParamIteratorInterface { + public: + virtual ~ParamIteratorInterface() {} + // A pointer to the base generator instance. + // Used only for the purposes of iterator comparison + // to make sure that two iterators belong to the same generator. + virtual const ParamGeneratorInterface<T>* BaseGenerator() const = 0; + // Advances iterator to point to the next element + // provided by the generator. The caller is responsible + // for not calling Advance() on an iterator equal to + // BaseGenerator()->End(). + virtual void Advance() = 0; + // Clones the iterator object. Used for implementing copy semantics + // of ParamIterator<T>. + virtual ParamIteratorInterface* Clone() const = 0; + // Dereferences the current iterator and provides (read-only) access + // to the pointed value. It is the caller's responsibility not to call + // Current() on an iterator equal to BaseGenerator()->End(). + // Used for implementing ParamGenerator<T>::operator*(). + virtual const T* Current() const = 0; + // Determines whether the given iterator and other point to the same + // element in the sequence generated by the generator. + // Used for implementing ParamGenerator<T>::operator==(). + virtual bool Equals(const ParamIteratorInterface& other) const = 0; +}; + +// Class iterating over elements provided by an implementation of +// ParamGeneratorInterface<T>. It wraps ParamIteratorInterface<T> +// and implements the const forward iterator concept. +template <typename T> +class ParamIterator { + public: + typedef T value_type; + typedef const T& reference; + typedef ptrdiff_t difference_type; + + // ParamIterator assumes ownership of the impl_ pointer. + ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {} + ParamIterator& operator=(const ParamIterator& other) { + if (this != &other) + impl_.reset(other.impl_->Clone()); + return *this; + } + + const T& operator*() const { return *impl_->Current(); } + const T* operator->() const { return impl_->Current(); } + // Prefix version of operator++. + ParamIterator& operator++() { + impl_->Advance(); + return *this; + } + // Postfix version of operator++. + ParamIterator operator++(int /*unused*/) { + ParamIteratorInterface<T>* clone = impl_->Clone(); + impl_->Advance(); + return ParamIterator(clone); + } + bool operator==(const ParamIterator& other) const { + return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_); + } + bool operator!=(const ParamIterator& other) const { + return !(*this == other); + } + + private: + friend class ParamGenerator<T>; + explicit ParamIterator(ParamIteratorInterface<T>* impl) : impl_(impl) {} + std::unique_ptr<ParamIteratorInterface<T> > impl_; +}; + +// ParamGeneratorInterface<T> is the binary interface to access generators +// defined in other translation units. +template <typename T> +class ParamGeneratorInterface { + public: + typedef T ParamType; + + virtual ~ParamGeneratorInterface() {} + + // Generator interface definition + virtual ParamIteratorInterface<T>* Begin() const = 0; + virtual ParamIteratorInterface<T>* End() const = 0; +}; + +// Wraps ParamGeneratorInterface<T> and provides general generator syntax +// compatible with the STL Container concept. +// This class implements copy initialization semantics and the contained +// ParamGeneratorInterface<T> instance is shared among all copies +// of the original object. This is possible because that instance is immutable. +template<typename T> +class ParamGenerator { + public: + typedef ParamIterator<T> iterator; + + explicit ParamGenerator(ParamGeneratorInterface<T>* impl) : impl_(impl) {} + ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {} + + ParamGenerator& operator=(const ParamGenerator& other) { + impl_ = other.impl_; + return *this; + } + + iterator begin() const { return iterator(impl_->Begin()); } + iterator end() const { return iterator(impl_->End()); } + + private: + std::shared_ptr<const ParamGeneratorInterface<T> > impl_; +}; + +// Generates values from a range of two comparable values. Can be used to +// generate sequences of user-defined types that implement operator+() and +// operator<(). +// This class is used in the Range() function. +template <typename T, typename IncrementT> +class RangeGenerator : public ParamGeneratorInterface<T> { + public: + RangeGenerator(T begin, T end, IncrementT step) + : begin_(begin), end_(end), + step_(step), end_index_(CalculateEndIndex(begin, end, step)) {} + ~RangeGenerator() override {} + + ParamIteratorInterface<T>* Begin() const override { + return new Iterator(this, begin_, 0, step_); + } + ParamIteratorInterface<T>* End() const override { + return new Iterator(this, end_, end_index_, step_); + } + + private: + class Iterator : public ParamIteratorInterface<T> { + public: + Iterator(const ParamGeneratorInterface<T>* base, T value, int index, + IncrementT step) + : base_(base), value_(value), index_(index), step_(step) {} + ~Iterator() override {} + + const ParamGeneratorInterface<T>* BaseGenerator() const override { + return base_; + } + void Advance() override { + value_ = static_cast<T>(value_ + step_); + index_++; + } + ParamIteratorInterface<T>* Clone() const override { + return new Iterator(*this); + } + const T* Current() const override { return &value_; } + bool Equals(const ParamIteratorInterface<T>& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const int other_index = + CheckedDowncastToActualType<const Iterator>(&other)->index_; + return index_ == other_index; + } + + private: + Iterator(const Iterator& other) + : ParamIteratorInterface<T>(), + base_(other.base_), value_(other.value_), index_(other.index_), + step_(other.step_) {} + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface<T>* const base_; + T value_; + int index_; + const IncrementT step_; + }; // class RangeGenerator::Iterator + + static int CalculateEndIndex(const T& begin, + const T& end, + const IncrementT& step) { + int end_index = 0; + for (T i = begin; i < end; i = static_cast<T>(i + step)) + end_index++; + return end_index; + } + + // No implementation - assignment is unsupported. + void operator=(const RangeGenerator& other); + + const T begin_; + const T end_; + const IncrementT step_; + // The index for the end() iterator. All the elements in the generated + // sequence are indexed (0-based) to aid iterator comparison. + const int end_index_; +}; // class RangeGenerator + + +// Generates values from a pair of STL-style iterators. Used in the +// ValuesIn() function. The elements are copied from the source range +// since the source can be located on the stack, and the generator +// is likely to persist beyond that stack frame. +template <typename T> +class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> { + public: + template <typename ForwardIterator> + ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) + : container_(begin, end) {} + ~ValuesInIteratorRangeGenerator() override {} + + ParamIteratorInterface<T>* Begin() const override { + return new Iterator(this, container_.begin()); + } + ParamIteratorInterface<T>* End() const override { + return new Iterator(this, container_.end()); + } + + private: + typedef typename ::std::vector<T> ContainerType; + + class Iterator : public ParamIteratorInterface<T> { + public: + Iterator(const ParamGeneratorInterface<T>* base, + typename ContainerType::const_iterator iterator) + : base_(base), iterator_(iterator) {} + ~Iterator() override {} + + const ParamGeneratorInterface<T>* BaseGenerator() const override { + return base_; + } + void Advance() override { + ++iterator_; + value_.reset(); + } + ParamIteratorInterface<T>* Clone() const override { + return new Iterator(*this); + } + // We need to use cached value referenced by iterator_ because *iterator_ + // can return a temporary object (and of type other then T), so just + // having "return &*iterator_;" doesn't work. + // value_ is updated here and not in Advance() because Advance() + // can advance iterator_ beyond the end of the range, and we cannot + // detect that fact. The client code, on the other hand, is + // responsible for not calling Current() on an out-of-range iterator. + const T* Current() const override { + if (value_.get() == nullptr) value_.reset(new T(*iterator_)); + return value_.get(); + } + bool Equals(const ParamIteratorInterface<T>& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + return iterator_ == + CheckedDowncastToActualType<const Iterator>(&other)->iterator_; + } + + private: + Iterator(const Iterator& other) + // The explicit constructor call suppresses a false warning + // emitted by gcc when supplied with the -Wextra option. + : ParamIteratorInterface<T>(), + base_(other.base_), + iterator_(other.iterator_) {} + + const ParamGeneratorInterface<T>* const base_; + typename ContainerType::const_iterator iterator_; + // A cached value of *iterator_. We keep it here to allow access by + // pointer in the wrapping iterator's operator->(). + // value_ needs to be mutable to be accessed in Current(). + // Use of std::unique_ptr helps manage cached value's lifetime, + // which is bound by the lifespan of the iterator itself. + mutable std::unique_ptr<const T> value_; + }; // class ValuesInIteratorRangeGenerator::Iterator + + // No implementation - assignment is unsupported. + void operator=(const ValuesInIteratorRangeGenerator& other); + + const ContainerType container_; +}; // class ValuesInIteratorRangeGenerator + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Default parameterized test name generator, returns a string containing the +// integer test parameter index. +template <class ParamType> +std::string DefaultParamName(const TestParamInfo<ParamType>& info) { + Message name_stream; + name_stream << info.index; + return name_stream.GetString(); +} + +template <typename T = int> +void TestNotEmpty() { + static_assert(sizeof(T) == 0, "Empty arguments are not allowed."); +} +template <typename T = int> +void TestNotEmpty(const T&) {} + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Stores a parameter value and later creates tests parameterized with that +// value. +template <class TestClass> +class ParameterizedTestFactory : public TestFactoryBase { + public: + typedef typename TestClass::ParamType ParamType; + explicit ParameterizedTestFactory(ParamType parameter) : + parameter_(parameter) {} + Test* CreateTest() override { + TestClass::SetParam(¶meter_); + return new TestClass(); + } + + private: + const ParamType parameter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactoryBase is a base class for meta-factories that create +// test factories for passing into MakeAndRegisterTestInfo function. +template <class ParamType> +class TestMetaFactoryBase { + public: + virtual ~TestMetaFactoryBase() {} + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactory creates test factories for passing into +// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives +// ownership of test factory pointer, same factory object cannot be passed +// into that method twice. But ParameterizedTestSuiteInfo is going to call +// it for each Test/Parameter value combination. Thus it needs meta factory +// creator class. +template <class TestSuite> +class TestMetaFactory + : public TestMetaFactoryBase<typename TestSuite::ParamType> { + public: + using ParamType = typename TestSuite::ParamType; + + TestMetaFactory() {} + + TestFactoryBase* CreateTestFactory(ParamType parameter) override { + return new ParameterizedTestFactory<TestSuite>(parameter); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteInfoBase is a generic interface +// to ParameterizedTestSuiteInfo classes. ParameterizedTestSuiteInfoBase +// accumulates test information provided by TEST_P macro invocations +// and generators provided by INSTANTIATE_TEST_SUITE_P macro invocations +// and uses that information to register all resulting test instances +// in RegisterTests method. The ParameterizeTestSuiteRegistry class holds +// a collection of pointers to the ParameterizedTestSuiteInfo objects +// and calls RegisterTests() on each of them when asked. +class ParameterizedTestSuiteInfoBase { + public: + virtual ~ParameterizedTestSuiteInfoBase() {} + + // Base part of test suite name for display purposes. + virtual const std::string& GetTestSuiteName() const = 0; + // Test suite id to verify identity. + virtual TypeId GetTestSuiteTypeId() const = 0; + // UnitTest class invokes this method to register tests in this + // test suite right before running them in RUN_ALL_TESTS macro. + // This method should not be called more than once on any single + // instance of a ParameterizedTestSuiteInfoBase derived class. + virtual void RegisterTests() = 0; + + protected: + ParameterizedTestSuiteInfoBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteInfoBase); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Report a the name of a test_suit as safe to ignore +// as the side effect of construction of this type. +struct MarkAsIgnored { + explicit MarkAsIgnored(const char* test_suite); +}; + +GTEST_API_ void InsertSyntheticTestCase(const std::string& name, + CodeLocation location, bool has_test_p); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteInfo accumulates tests obtained from TEST_P +// macro invocations for a particular test suite and generators +// obtained from INSTANTIATE_TEST_SUITE_P macro invocations for that +// test suite. It registers tests with all values generated by all +// generators when asked. +template <class TestSuite> +class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase { + public: + // ParamType and GeneratorCreationFunc are private types but are required + // for declarations of public methods AddTestPattern() and + // AddTestSuiteInstantiation(). + using ParamType = typename TestSuite::ParamType; + // A function that returns an instance of appropriate generator type. + typedef ParamGenerator<ParamType>(GeneratorCreationFunc)(); + using ParamNameGeneratorFunc = std::string(const TestParamInfo<ParamType>&); + + explicit ParameterizedTestSuiteInfo(const char* name, + CodeLocation code_location) + : test_suite_name_(name), code_location_(code_location) {} + + // Test suite base name for display purposes. + const std::string& GetTestSuiteName() const override { + return test_suite_name_; + } + // Test suite id to verify identity. + TypeId GetTestSuiteTypeId() const override { return GetTypeId<TestSuite>(); } + // TEST_P macro uses AddTestPattern() to record information + // about a single test in a LocalTestInfo structure. + // test_suite_name is the base name of the test suite (without invocation + // prefix). test_base_name is the name of an individual test without + // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is + // test suite base name and DoBar is test base name. + void AddTestPattern(const char* test_suite_name, const char* test_base_name, + TestMetaFactoryBase<ParamType>* meta_factory, + CodeLocation code_location) { + tests_.push_back(std::shared_ptr<TestInfo>(new TestInfo( + test_suite_name, test_base_name, meta_factory, code_location))); + } + // INSTANTIATE_TEST_SUITE_P macro uses AddGenerator() to record information + // about a generator. + int AddTestSuiteInstantiation(const std::string& instantiation_name, + GeneratorCreationFunc* func, + ParamNameGeneratorFunc* name_func, + const char* file, int line) { + instantiations_.push_back( + InstantiationInfo(instantiation_name, func, name_func, file, line)); + return 0; // Return value used only to run this method in namespace scope. + } + // UnitTest class invokes this method to register tests in this test suite + // right before running tests in RUN_ALL_TESTS macro. + // This method should not be called more than once on any single + // instance of a ParameterizedTestSuiteInfoBase derived class. + // UnitTest has a guard to prevent from calling this method more than once. + void RegisterTests() override { + bool generated_instantiations = false; + + for (typename TestInfoContainer::iterator test_it = tests_.begin(); + test_it != tests_.end(); ++test_it) { + std::shared_ptr<TestInfo> test_info = *test_it; + for (typename InstantiationContainer::iterator gen_it = + instantiations_.begin(); gen_it != instantiations_.end(); + ++gen_it) { + const std::string& instantiation_name = gen_it->name; + ParamGenerator<ParamType> generator((*gen_it->generator)()); + ParamNameGeneratorFunc* name_func = gen_it->name_func; + const char* file = gen_it->file; + int line = gen_it->line; + + std::string test_suite_name; + if ( !instantiation_name.empty() ) + test_suite_name = instantiation_name + "/"; + test_suite_name += test_info->test_suite_base_name; + + size_t i = 0; + std::set<std::string> test_param_names; + for (typename ParamGenerator<ParamType>::iterator param_it = + generator.begin(); + param_it != generator.end(); ++param_it, ++i) { + generated_instantiations = true; + + Message test_name_stream; + + std::string param_name = name_func( + TestParamInfo<ParamType>(*param_it, i)); + + GTEST_CHECK_(IsValidParamName(param_name)) + << "Parameterized test name '" << param_name + << "' is invalid, in " << file + << " line " << line << std::endl; + + GTEST_CHECK_(test_param_names.count(param_name) == 0) + << "Duplicate parameterized test name '" << param_name + << "', in " << file << " line " << line << std::endl; + + test_param_names.insert(param_name); + + if (!test_info->test_base_name.empty()) { + test_name_stream << test_info->test_base_name << "/"; + } + test_name_stream << param_name; + MakeAndRegisterTestInfo( + test_suite_name.c_str(), test_name_stream.GetString().c_str(), + nullptr, // No type parameter. + PrintToString(*param_it).c_str(), test_info->code_location, + GetTestSuiteTypeId(), + SuiteApiResolver<TestSuite>::GetSetUpCaseOrSuite(file, line), + SuiteApiResolver<TestSuite>::GetTearDownCaseOrSuite(file, line), + test_info->test_meta_factory->CreateTestFactory(*param_it)); + } // for param_it + } // for gen_it + } // for test_it + + if (!generated_instantiations) { + // There are no generaotrs, or they all generate nothing ... + InsertSyntheticTestCase(GetTestSuiteName(), code_location_, + !tests_.empty()); + } + } // RegisterTests + + private: + // LocalTestInfo structure keeps information about a single test registered + // with TEST_P macro. + struct TestInfo { + TestInfo(const char* a_test_suite_base_name, const char* a_test_base_name, + TestMetaFactoryBase<ParamType>* a_test_meta_factory, + CodeLocation a_code_location) + : test_suite_base_name(a_test_suite_base_name), + test_base_name(a_test_base_name), + test_meta_factory(a_test_meta_factory), + code_location(a_code_location) {} + + const std::string test_suite_base_name; + const std::string test_base_name; + const std::unique_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory; + const CodeLocation code_location; + }; + using TestInfoContainer = ::std::vector<std::shared_ptr<TestInfo> >; + // Records data received from INSTANTIATE_TEST_SUITE_P macros: + // <Instantiation name, Sequence generator creation function, + // Name generator function, Source file, Source line> + struct InstantiationInfo { + InstantiationInfo(const std::string &name_in, + GeneratorCreationFunc* generator_in, + ParamNameGeneratorFunc* name_func_in, + const char* file_in, + int line_in) + : name(name_in), + generator(generator_in), + name_func(name_func_in), + file(file_in), + line(line_in) {} + + std::string name; + GeneratorCreationFunc* generator; + ParamNameGeneratorFunc* name_func; + const char* file; + int line; + }; + typedef ::std::vector<InstantiationInfo> InstantiationContainer; + + static bool IsValidParamName(const std::string& name) { + // Check for empty string + if (name.empty()) + return false; + + // Check for invalid characters + for (std::string::size_type index = 0; index < name.size(); ++index) { + if (!isalnum(name[index]) && name[index] != '_') + return false; + } + + return true; + } + + const std::string test_suite_name_; + CodeLocation code_location_; + TestInfoContainer tests_; + InstantiationContainer instantiations_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteInfo); +}; // class ParameterizedTestSuiteInfo + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +template <class TestCase> +using ParameterizedTestCaseInfo = ParameterizedTestSuiteInfo<TestCase>; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteRegistry contains a map of +// ParameterizedTestSuiteInfoBase classes accessed by test suite names. TEST_P +// and INSTANTIATE_TEST_SUITE_P macros use it to locate their corresponding +// ParameterizedTestSuiteInfo descriptors. +class ParameterizedTestSuiteRegistry { + public: + ParameterizedTestSuiteRegistry() {} + ~ParameterizedTestSuiteRegistry() { + for (auto& test_suite_info : test_suite_infos_) { + delete test_suite_info; + } + } + + // Looks up or creates and returns a structure containing information about + // tests and instantiations of a particular test suite. + template <class TestSuite> + ParameterizedTestSuiteInfo<TestSuite>* GetTestSuitePatternHolder( + const char* test_suite_name, CodeLocation code_location) { + ParameterizedTestSuiteInfo<TestSuite>* typed_test_info = nullptr; + for (auto& test_suite_info : test_suite_infos_) { + if (test_suite_info->GetTestSuiteName() == test_suite_name) { + if (test_suite_info->GetTestSuiteTypeId() != GetTypeId<TestSuite>()) { + // Complain about incorrect usage of Google Test facilities + // and terminate the program since we cannot guaranty correct + // test suite setup and tear-down in this case. + ReportInvalidTestSuiteType(test_suite_name, code_location); + posix::Abort(); + } else { + // At this point we are sure that the object we found is of the same + // type we are looking for, so we downcast it to that type + // without further checks. + typed_test_info = CheckedDowncastToActualType< + ParameterizedTestSuiteInfo<TestSuite> >(test_suite_info); + } + break; + } + } + if (typed_test_info == nullptr) { + typed_test_info = new ParameterizedTestSuiteInfo<TestSuite>( + test_suite_name, code_location); + test_suite_infos_.push_back(typed_test_info); + } + return typed_test_info; + } + void RegisterTests() { + for (auto& test_suite_info : test_suite_infos_) { + test_suite_info->RegisterTests(); + } + } +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + template <class TestCase> + ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder( + const char* test_case_name, CodeLocation code_location) { + return GetTestSuitePatternHolder<TestCase>(test_case_name, code_location); + } + +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + private: + using TestSuiteInfoContainer = ::std::vector<ParameterizedTestSuiteInfoBase*>; + + TestSuiteInfoContainer test_suite_infos_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteRegistry); +}; + +// Keep track of what type-parameterized test suite are defined and +// where as well as which are intatiated. This allows susequently +// identifying suits that are defined but never used. +class TypeParameterizedTestSuiteRegistry { + public: + // Add a suite definition + void RegisterTestSuite(const char* test_suite_name, + CodeLocation code_location); + + // Add an instantiation of a suit. + void RegisterInstantiation(const char* test_suite_name); + + // For each suit repored as defined but not reported as instantiation, + // emit a test that reports that fact (configurably, as an error). + void CheckForInstantiations(); + + private: + struct TypeParameterizedTestSuiteInfo { + explicit TypeParameterizedTestSuiteInfo(CodeLocation c) + : code_location(c), instantiated(false) {} + + CodeLocation code_location; + bool instantiated; + }; + + std::map<std::string, TypeParameterizedTestSuiteInfo> suites_; +}; + +} // namespace internal + +// Forward declarations of ValuesIn(), which is implemented in +// include/gtest/gtest-param-test.h. +template <class Container> +internal::ParamGenerator<typename Container::value_type> ValuesIn( + const Container& container); + +namespace internal { +// Used in the Values() function to provide polymorphic capabilities. + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4100) +#endif + +template <typename... Ts> +class ValueArray { + public: + explicit ValueArray(Ts... v) : v_(FlatTupleConstructTag{}, std::move(v)...) {} + + template <typename T> + operator ParamGenerator<T>() const { // NOLINT + return ValuesIn(MakeVector<T>(MakeIndexSequence<sizeof...(Ts)>())); + } + + private: + template <typename T, size_t... I> + std::vector<T> MakeVector(IndexSequence<I...>) const { + return std::vector<T>{static_cast<T>(v_.template Get<I>())...}; + } + + FlatTuple<Ts...> v_; +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +template <typename... T> +class CartesianProductGenerator + : public ParamGeneratorInterface<::std::tuple<T...>> { + public: + typedef ::std::tuple<T...> ParamType; + + CartesianProductGenerator(const std::tuple<ParamGenerator<T>...>& g) + : generators_(g) {} + ~CartesianProductGenerator() override {} + + ParamIteratorInterface<ParamType>* Begin() const override { + return new Iterator(this, generators_, false); + } + ParamIteratorInterface<ParamType>* End() const override { + return new Iterator(this, generators_, true); + } + + private: + template <class I> + class IteratorImpl; + template <size_t... I> + class IteratorImpl<IndexSequence<I...>> + : public ParamIteratorInterface<ParamType> { + public: + IteratorImpl(const ParamGeneratorInterface<ParamType>* base, + const std::tuple<ParamGenerator<T>...>& generators, bool is_end) + : base_(base), + begin_(std::get<I>(generators).begin()...), + end_(std::get<I>(generators).end()...), + current_(is_end ? end_ : begin_) { + ComputeCurrentValue(); + } + ~IteratorImpl() override {} + + const ParamGeneratorInterface<ParamType>* BaseGenerator() const override { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + void Advance() override { + assert(!AtEnd()); + // Advance the last iterator. + ++std::get<sizeof...(T) - 1>(current_); + // if that reaches end, propagate that up. + AdvanceIfEnd<sizeof...(T) - 1>(); + ComputeCurrentValue(); + } + ParamIteratorInterface<ParamType>* Clone() const override { + return new IteratorImpl(*this); + } + + const ParamType* Current() const override { return current_value_.get(); } + + bool Equals(const ParamIteratorInterface<ParamType>& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const IteratorImpl* typed_other = + CheckedDowncastToActualType<const IteratorImpl>(&other); + + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + if (AtEnd() && typed_other->AtEnd()) return true; + + bool same = true; + bool dummy[] = { + (same = same && std::get<I>(current_) == + std::get<I>(typed_other->current_))...}; + (void)dummy; + return same; + } + + private: + template <size_t ThisI> + void AdvanceIfEnd() { + if (std::get<ThisI>(current_) != std::get<ThisI>(end_)) return; + + bool last = ThisI == 0; + if (last) { + // We are done. Nothing else to propagate. + return; + } + + constexpr size_t NextI = ThisI - (ThisI != 0); + std::get<ThisI>(current_) = std::get<ThisI>(begin_); + ++std::get<NextI>(current_); + AdvanceIfEnd<NextI>(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = std::make_shared<ParamType>(*std::get<I>(current_)...); + } + bool AtEnd() const { + bool at_end = false; + bool dummy[] = { + (at_end = at_end || std::get<I>(current_) == std::get<I>(end_))...}; + (void)dummy; + return at_end; + } + + const ParamGeneratorInterface<ParamType>* const base_; + std::tuple<typename ParamGenerator<T>::iterator...> begin_; + std::tuple<typename ParamGenerator<T>::iterator...> end_; + std::tuple<typename ParamGenerator<T>::iterator...> current_; + std::shared_ptr<ParamType> current_value_; + }; + + using Iterator = IteratorImpl<typename MakeIndexSequence<sizeof...(T)>::type>; + + std::tuple<ParamGenerator<T>...> generators_; +}; + +template <class... Gen> +class CartesianProductHolder { + public: + CartesianProductHolder(const Gen&... g) : generators_(g...) {} + template <typename... T> + operator ParamGenerator<::std::tuple<T...>>() const { + return ParamGenerator<::std::tuple<T...>>( + new CartesianProductGenerator<T...>(generators_)); + } + + private: + std::tuple<Gen...> generators_; +}; + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ + +namespace testing { + +// Functions producing parameter generators. +// +// Google Test uses these generators to produce parameters for value- +// parameterized tests. When a parameterized test suite is instantiated +// with a particular generator, Google Test creates and runs tests +// for each element in the sequence produced by the generator. +// +// In the following sample, tests from test suite FooTest are instantiated +// each three times with parameter values 3, 5, and 8: +// +// class FooTest : public TestWithParam<int> { ... }; +// +// TEST_P(FooTest, TestThis) { +// } +// TEST_P(FooTest, TestThat) { +// } +// INSTANTIATE_TEST_SUITE_P(TestSequence, FooTest, Values(3, 5, 8)); +// + +// Range() returns generators providing sequences of values in a range. +// +// Synopsis: +// Range(start, end) +// - returns a generator producing a sequence of values {start, start+1, +// start+2, ..., }. +// Range(start, end, step) +// - returns a generator producing a sequence of values {start, start+step, +// start+step+step, ..., }. +// Notes: +// * The generated sequences never include end. For example, Range(1, 5) +// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2) +// returns a generator producing {1, 3, 5, 7}. +// * start and end must have the same type. That type may be any integral or +// floating-point type or a user defined type satisfying these conditions: +// * It must be assignable (have operator=() defined). +// * It must have operator+() (operator+(int-compatible type) for +// two-operand version). +// * It must have operator<() defined. +// Elements in the resulting sequences will also have that type. +// * Condition start < end must be satisfied in order for resulting sequences +// to contain any elements. +// +template <typename T, typename IncrementT> +internal::ParamGenerator<T> Range(T start, T end, IncrementT step) { + return internal::ParamGenerator<T>( + new internal::RangeGenerator<T, IncrementT>(start, end, step)); +} + +template <typename T> +internal::ParamGenerator<T> Range(T start, T end) { + return Range(start, end, 1); +} + +// ValuesIn() function allows generation of tests with parameters coming from +// a container. +// +// Synopsis: +// ValuesIn(const T (&array)[N]) +// - returns a generator producing sequences with elements from +// a C-style array. +// ValuesIn(const Container& container) +// - returns a generator producing sequences with elements from +// an STL-style container. +// ValuesIn(Iterator begin, Iterator end) +// - returns a generator producing sequences with elements from +// a range [begin, end) defined by a pair of STL-style iterators. These +// iterators can also be plain C pointers. +// +// Please note that ValuesIn copies the values from the containers +// passed in and keeps them to generate tests in RUN_ALL_TESTS(). +// +// Examples: +// +// This instantiates tests from test suite StringTest +// each with C-string values of "foo", "bar", and "baz": +// +// const char* strings[] = {"foo", "bar", "baz"}; +// INSTANTIATE_TEST_SUITE_P(StringSequence, StringTest, ValuesIn(strings)); +// +// This instantiates tests from test suite StlStringTest +// each with STL strings with values "a" and "b": +// +// ::std::vector< ::std::string> GetParameterStrings() { +// ::std::vector< ::std::string> v; +// v.push_back("a"); +// v.push_back("b"); +// return v; +// } +// +// INSTANTIATE_TEST_SUITE_P(CharSequence, +// StlStringTest, +// ValuesIn(GetParameterStrings())); +// +// +// This will also instantiate tests from CharTest +// each with parameter values 'a' and 'b': +// +// ::std::list<char> GetParameterChars() { +// ::std::list<char> list; +// list.push_back('a'); +// list.push_back('b'); +// return list; +// } +// ::std::list<char> l = GetParameterChars(); +// INSTANTIATE_TEST_SUITE_P(CharSequence2, +// CharTest, +// ValuesIn(l.begin(), l.end())); +// +template <typename ForwardIterator> +internal::ParamGenerator< + typename std::iterator_traits<ForwardIterator>::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end) { + typedef typename std::iterator_traits<ForwardIterator>::value_type ParamType; + return internal::ParamGenerator<ParamType>( + new internal::ValuesInIteratorRangeGenerator<ParamType>(begin, end)); +} + +template <typename T, size_t N> +internal::ParamGenerator<T> ValuesIn(const T (&array)[N]) { + return ValuesIn(array, array + N); +} + +template <class Container> +internal::ParamGenerator<typename Container::value_type> ValuesIn( + const Container& container) { + return ValuesIn(container.begin(), container.end()); +} + +// Values() allows generating tests from explicitly specified list of +// parameters. +// +// Synopsis: +// Values(T v1, T v2, ..., T vN) +// - returns a generator producing sequences with elements v1, v2, ..., vN. +// +// For example, this instantiates tests from test suite BarTest each +// with values "one", "two", and "three": +// +// INSTANTIATE_TEST_SUITE_P(NumSequence, +// BarTest, +// Values("one", "two", "three")); +// +// This instantiates tests from test suite BazTest each with values 1, 2, 3.5. +// The exact type of values will depend on the type of parameter in BazTest. +// +// INSTANTIATE_TEST_SUITE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); +// +// +template <typename... T> +internal::ValueArray<T...> Values(T... v) { + return internal::ValueArray<T...>(std::move(v)...); +} + +// Bool() allows generating tests with parameters in a set of (false, true). +// +// Synopsis: +// Bool() +// - returns a generator producing sequences with elements {false, true}. +// +// It is useful when testing code that depends on Boolean flags. Combinations +// of multiple flags can be tested when several Bool()'s are combined using +// Combine() function. +// +// In the following example all tests in the test suite FlagDependentTest +// will be instantiated twice with parameters false and true. +// +// class FlagDependentTest : public testing::TestWithParam<bool> { +// virtual void SetUp() { +// external_flag = GetParam(); +// } +// } +// INSTANTIATE_TEST_SUITE_P(BoolSequence, FlagDependentTest, Bool()); +// +inline internal::ParamGenerator<bool> Bool() { + return Values(false, true); +} + +// Combine() allows the user to combine two or more sequences to produce +// values of a Cartesian product of those sequences' elements. +// +// Synopsis: +// Combine(gen1, gen2, ..., genN) +// - returns a generator producing sequences with elements coming from +// the Cartesian product of elements from the sequences generated by +// gen1, gen2, ..., genN. The sequence elements will have a type of +// std::tuple<T1, T2, ..., TN> where T1, T2, ..., TN are the types +// of elements from sequences produces by gen1, gen2, ..., genN. +// +// Example: +// +// This will instantiate tests in test suite AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// class AnimalTest +// : public testing::TestWithParam<std::tuple<const char*, Color> > {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest, +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE))); +// +// This will instantiate tests in FlagDependentTest with all variations of two +// Boolean flags: +// +// class FlagDependentTest +// : public testing::TestWithParam<std::tuple<bool, bool> > { +// virtual void SetUp() { +// // Assigns external_flag_1 and external_flag_2 values from the tuple. +// std::tie(external_flag_1, external_flag_2) = GetParam(); +// } +// }; +// +// TEST_P(FlagDependentTest, TestFeature1) { +// // Test your code using external_flag_1 and external_flag_2 here. +// } +// INSTANTIATE_TEST_SUITE_P(TwoBoolSequence, FlagDependentTest, +// Combine(Bool(), Bool())); +// +template <typename... Generator> +internal::CartesianProductHolder<Generator...> Combine(const Generator&... g) { + return internal::CartesianProductHolder<Generator...>(g...); +} + +#define TEST_P(test_suite_name, test_name) \ + class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + : public test_suite_name { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {} \ + void TestBody() override; \ + \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance() \ + ->parameterized_test_registry() \ + .GetTestSuitePatternHolder<test_suite_name>( \ + GTEST_STRINGIFY_(test_suite_name), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ + ->AddTestPattern( \ + GTEST_STRINGIFY_(test_suite_name), GTEST_STRINGIFY_(test_name), \ + new ::testing::internal::TestMetaFactory<GTEST_TEST_CLASS_NAME_( \ + test_suite_name, test_name)>(), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)); \ + return 0; \ + } \ + static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \ + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)); \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::AddToRegistry(); \ + void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() + +// The last argument to INSTANTIATE_TEST_SUITE_P allows the user to specify +// generator and an optional function or functor that generates custom test name +// suffixes based on the test parameters. Such a function or functor should +// accept one argument of type testing::TestParamInfo<class ParamType>, and +// return std::string. +// +// testing::PrintToStringParamName is a builtin test suffix generator that +// returns the value of testing::PrintToString(GetParam()). +// +// Note: test names must be non-empty, unique, and may only contain ASCII +// alphanumeric characters or underscore. Because PrintToString adds quotes +// to std::string and C strings, it won't work for these types. + +#define GTEST_EXPAND_(arg) arg +#define GTEST_GET_FIRST_(first, ...) first +#define GTEST_GET_SECOND_(first, second, ...) second + +#define INSTANTIATE_TEST_SUITE_P(prefix, test_suite_name, ...) \ + static ::testing::internal::ParamGenerator<test_suite_name::ParamType> \ + gtest_##prefix##test_suite_name##_EvalGenerator_() { \ + return GTEST_EXPAND_(GTEST_GET_FIRST_(__VA_ARGS__, DUMMY_PARAM_)); \ + } \ + static ::std::string gtest_##prefix##test_suite_name##_EvalGenerateName_( \ + const ::testing::TestParamInfo<test_suite_name::ParamType>& info) { \ + if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::TestNotEmpty(GTEST_EXPAND_(GTEST_GET_SECOND_( \ + __VA_ARGS__, \ + ::testing::internal::DefaultParamName<test_suite_name::ParamType>, \ + DUMMY_PARAM_))); \ + auto t = std::make_tuple(__VA_ARGS__); \ + static_assert(std::tuple_size<decltype(t)>::value <= 2, \ + "Too Many Args!"); \ + } \ + return ((GTEST_EXPAND_(GTEST_GET_SECOND_( \ + __VA_ARGS__, \ + ::testing::internal::DefaultParamName<test_suite_name::ParamType>, \ + DUMMY_PARAM_))))(info); \ + } \ + static int gtest_##prefix##test_suite_name##_dummy_ \ + GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::UnitTest::GetInstance() \ + ->parameterized_test_registry() \ + .GetTestSuitePatternHolder<test_suite_name>( \ + GTEST_STRINGIFY_(test_suite_name), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ + ->AddTestSuiteInstantiation( \ + GTEST_STRINGIFY_(prefix), \ + >est_##prefix##test_suite_name##_EvalGenerator_, \ + >est_##prefix##test_suite_name##_EvalGenerateName_, \ + __FILE__, __LINE__) + + +// Allow Marking a Parameterized test class as not needing to be instantiated. +#define GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(T) \ + namespace gtest_do_not_use_outside_namespace_scope {} \ + static const ::testing::internal::MarkAsIgnored gtest_allow_ignore_##T( \ + GTEST_STRINGIFY_(T)) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define INSTANTIATE_TEST_CASE_P \ + static_assert(::testing::internal::InstantiateTestCase_P_IsDeprecated(), \ + ""); \ + INSTANTIATE_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// +// Google C++ Testing and Mocking Framework definitions useful in production code. +// GOOGLETEST_CM0003 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ + +// When you need to test the private or protected members of a class, +// use the FRIEND_TEST macro to declare your tests as friends of the +// class. For example: +// +// class MyClass { +// private: +// void PrivateMethod(); +// FRIEND_TEST(MyClassTest, PrivateMethodWorks); +// }; +// +// class MyClassTest : public testing::Test { +// // ... +// }; +// +// TEST_F(MyClassTest, PrivateMethodWorks) { +// // Can call MyClass::PrivateMethod() here. +// } +// +// Note: The test class must be in the same namespace as the class being tested. +// For example, putting MyClassTest in an anonymous namespace will not work. + +#define FRIEND_TEST(test_case_name, test_name)\ +friend class test_case_name##_##test_name##_Test + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +// This header implements typed tests and type-parameterized tests. + +// Typed (aka type-driven) tests repeat the same test for types in a +// list. You must know which types you want to test with when writing +// typed tests. Here's how you do it: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template <typename T> +class FooTest : public testing::Test { + public: + ... + typedef std::list<T> List; + static T shared_; + T value_; +}; + +// Next, associate a list of types with the test suite, which will be +// repeated for each type in the list. The typedef is necessary for +// the macro to parse correctly. +typedef testing::Types<char, int, unsigned int> MyTypes; +TYPED_TEST_SUITE(FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// TYPED_TEST_SUITE(FooTest, int); + +// Then, use TYPED_TEST() instead of TEST_F() to define as many typed +// tests for this test suite as you want. +TYPED_TEST(FooTest, DoesBlah) { + // Inside a test, refer to the special name TypeParam to get the type + // parameter. Since we are inside a derived class template, C++ requires + // us to visit the members of FooTest via 'this'. + TypeParam n = this->value_; + + // To visit static members of the fixture, add the TestFixture:: + // prefix. + n += TestFixture::shared_; + + // To refer to typedefs in the fixture, add the "typename + // TestFixture::" prefix. + typename TestFixture::List values; + values.push_back(n); + ... +} + +TYPED_TEST(FooTest, HasPropertyA) { ... } + +// TYPED_TEST_SUITE takes an optional third argument which allows to specify a +// class that generates custom test name suffixes based on the type. This should +// be a class which has a static template function GetName(int index) returning +// a string for each type. The provided integer index equals the index of the +// type in the provided type list. In many cases the index can be ignored. +// +// For example: +// class MyTypeNames { +// public: +// template <typename T> +// static std::string GetName(int) { +// if (std::is_same<T, char>()) return "char"; +// if (std::is_same<T, int>()) return "int"; +// if (std::is_same<T, unsigned int>()) return "unsignedInt"; +// } +// }; +// TYPED_TEST_SUITE(FooTest, MyTypes, MyTypeNames); + +#endif // 0 + +// Type-parameterized tests are abstract test patterns parameterized +// by a type. Compared with typed tests, type-parameterized tests +// allow you to define the test pattern without knowing what the type +// parameters are. The defined pattern can be instantiated with +// different types any number of times, in any number of translation +// units. +// +// If you are designing an interface or concept, you can define a +// suite of type-parameterized tests to verify properties that any +// valid implementation of the interface/concept should have. Then, +// each implementation can easily instantiate the test suite to verify +// that it conforms to the requirements, without having to write +// similar tests repeatedly. Here's an example: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template <typename T> +class FooTest : public testing::Test { + ... +}; + +// Next, declare that you will define a type-parameterized test suite +// (the _P suffix is for "parameterized" or "pattern", whichever you +// prefer): +TYPED_TEST_SUITE_P(FooTest); + +// Then, use TYPED_TEST_P() to define as many type-parameterized tests +// for this type-parameterized test suite as you want. +TYPED_TEST_P(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + TypeParam n = 0; + ... +} + +TYPED_TEST_P(FooTest, HasPropertyA) { ... } + +// Now the tricky part: you need to register all test patterns before +// you can instantiate them. The first argument of the macro is the +// test suite name; the rest are the names of the tests in this test +// case. +REGISTER_TYPED_TEST_SUITE_P(FooTest, + DoesBlah, HasPropertyA); + +// Finally, you are free to instantiate the pattern with the types you +// want. If you put the above code in a header file, you can #include +// it in multiple C++ source files and instantiate it multiple times. +// +// To distinguish different instances of the pattern, the first +// argument to the INSTANTIATE_* macro is a prefix that will be added +// to the actual test suite name. Remember to pick unique prefixes for +// different instances. +typedef testing::Types<char, int, unsigned int> MyTypes; +INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, int); +// +// Similar to the optional argument of TYPED_TEST_SUITE above, +// INSTANTIATE_TEST_SUITE_P takes an optional fourth argument which allows to +// generate custom names. +// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes, MyTypeNames); + +#endif // 0 + + +// Implements typed tests. + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the typedef for the type parameters of the +// given test suite. +#define GTEST_TYPE_PARAMS_(TestSuiteName) gtest_type_params_##TestSuiteName##_ + +// Expands to the name of the typedef for the NameGenerator, responsible for +// creating the suffixes of the name. +#define GTEST_NAME_GENERATOR_(TestSuiteName) \ + gtest_type_params_##TestSuiteName##_NameGenerator + +#define TYPED_TEST_SUITE(CaseName, Types, ...) \ + typedef ::testing::internal::GenerateTypeList<Types>::type \ + GTEST_TYPE_PARAMS_(CaseName); \ + typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \ + GTEST_NAME_GENERATOR_(CaseName) + +#define TYPED_TEST(CaseName, TestName) \ + static_assert(sizeof(GTEST_STRINGIFY_(TestName)) > 1, \ + "test-name must not be empty"); \ + template <typename gtest_TypeParam_> \ + class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ + : public CaseName<gtest_TypeParam_> { \ + private: \ + typedef CaseName<gtest_TypeParam_> TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + void TestBody() override; \ + }; \ + static bool gtest_##CaseName##_##TestName##_registered_ \ + GTEST_ATTRIBUTE_UNUSED_ = ::testing::internal::TypeParameterizedTest< \ + CaseName, \ + ::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_(CaseName, \ + TestName)>, \ + GTEST_TYPE_PARAMS_( \ + CaseName)>::Register("", \ + ::testing::internal::CodeLocation( \ + __FILE__, __LINE__), \ + GTEST_STRINGIFY_(CaseName), \ + GTEST_STRINGIFY_(TestName), 0, \ + ::testing::internal::GenerateNames< \ + GTEST_NAME_GENERATOR_(CaseName), \ + GTEST_TYPE_PARAMS_(CaseName)>()); \ + template <typename gtest_TypeParam_> \ + void GTEST_TEST_CLASS_NAME_(CaseName, \ + TestName)<gtest_TypeParam_>::TestBody() + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define TYPED_TEST_CASE \ + static_assert(::testing::internal::TypedTestCaseIsDeprecated(), ""); \ + TYPED_TEST_SUITE +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// Implements type-parameterized tests. + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the namespace name that the type-parameterized tests for +// the given type-parameterized test suite are defined in. The exact +// name of the namespace is subject to change without notice. +#define GTEST_SUITE_NAMESPACE_(TestSuiteName) gtest_suite_##TestSuiteName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the variable used to remember the names of +// the defined tests in the given test suite. +#define GTEST_TYPED_TEST_SUITE_P_STATE_(TestSuiteName) \ + gtest_typed_test_suite_p_state_##TestSuiteName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY. +// +// Expands to the name of the variable used to remember the names of +// the registered tests in the given test suite. +#define GTEST_REGISTERED_TEST_NAMES_(TestSuiteName) \ + gtest_registered_test_names_##TestSuiteName##_ + +// The variables defined in the type-parameterized test macros are +// static as typically these macros are used in a .h file that can be +// #included in multiple translation units linked together. +#define TYPED_TEST_SUITE_P(SuiteName) \ + static ::testing::internal::TypedTestSuitePState \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define TYPED_TEST_CASE_P \ + static_assert(::testing::internal::TypedTestCase_P_IsDeprecated(), ""); \ + TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#define TYPED_TEST_P(SuiteName, TestName) \ + namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \ + template <typename gtest_TypeParam_> \ + class TestName : public SuiteName<gtest_TypeParam_> { \ + private: \ + typedef SuiteName<gtest_TypeParam_> TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + void TestBody() override; \ + }; \ + static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \ + __FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName), \ + GTEST_STRINGIFY_(TestName)); \ + } \ + template <typename gtest_TypeParam_> \ + void GTEST_SUITE_NAMESPACE_( \ + SuiteName)::TestName<gtest_TypeParam_>::TestBody() + +// Note: this won't work correctly if the trailing arguments are macros. +#define REGISTER_TYPED_TEST_SUITE_P(SuiteName, ...) \ + namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \ + typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_; \ + } \ + static const char* const GTEST_REGISTERED_TEST_NAMES_( \ + SuiteName) GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \ + GTEST_STRINGIFY_(SuiteName), __FILE__, __LINE__, #__VA_ARGS__) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define REGISTER_TYPED_TEST_CASE_P \ + static_assert(::testing::internal::RegisterTypedTestCase_P_IsDeprecated(), \ + ""); \ + REGISTER_TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...) \ + static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1, \ + "test-suit-prefix must not be empty"); \ + static bool gtest_##Prefix##_##SuiteName GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTestSuite< \ + SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_, \ + ::testing::internal::GenerateTypeList<Types>::type>:: \ + Register(GTEST_STRINGIFY_(Prefix), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), \ + >EST_TYPED_TEST_SUITE_P_STATE_(SuiteName), \ + GTEST_STRINGIFY_(SuiteName), \ + GTEST_REGISTERED_TEST_NAMES_(SuiteName), \ + ::testing::internal::GenerateNames< \ + ::testing::internal::NameGeneratorSelector< \ + __VA_ARGS__>::type, \ + ::testing::internal::GenerateTypeList<Types>::type>()) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define INSTANTIATE_TYPED_TEST_CASE_P \ + static_assert( \ + ::testing::internal::InstantiateTypedTestCase_P_IsDeprecated(), ""); \ + INSTANTIATE_TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// Silence C4100 (unreferenced formal parameter) and 4805 +// unsafe mix of type 'const int' and type 'const bool' +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4805) +# pragma warning(disable:4100) +#endif + + +// Declares the flags. + +// This flag temporary enables the disabled tests. +GTEST_DECLARE_bool_(also_run_disabled_tests); + +// This flag brings the debugger on an assertion failure. +GTEST_DECLARE_bool_(break_on_failure); + +// This flag controls whether Google Test catches all test-thrown exceptions +// and logs them as failures. +GTEST_DECLARE_bool_(catch_exceptions); + +// This flag enables using colors in terminal output. Available values are +// "yes" to enable colors, "no" (disable colors), or "auto" (the default) +// to let Google Test decide. +GTEST_DECLARE_string_(color); + +// This flag controls whether the test runner should continue execution past +// first failure. +GTEST_DECLARE_bool_(fail_fast); + +// This flag sets up the filter to select by name using a glob pattern +// the tests to run. If the filter is not given all tests are executed. +GTEST_DECLARE_string_(filter); + +// This flag controls whether Google Test installs a signal handler that dumps +// debugging information when fatal signals are raised. +GTEST_DECLARE_bool_(install_failure_signal_handler); + +// This flag causes the Google Test to list tests. None of the tests listed +// are actually run if the flag is provided. +GTEST_DECLARE_bool_(list_tests); + +// This flag controls whether Google Test emits a detailed XML report to a file +// in addition to its normal textual output. +GTEST_DECLARE_string_(output); + +// This flags control whether Google Test prints only test failures. +GTEST_DECLARE_bool_(brief); + +// This flags control whether Google Test prints the elapsed time for each +// test. +GTEST_DECLARE_bool_(print_time); + +// This flags control whether Google Test prints UTF8 characters as text. +GTEST_DECLARE_bool_(print_utf8); + +// This flag specifies the random number seed. +GTEST_DECLARE_int32_(random_seed); + +// This flag sets how many times the tests are repeated. The default value +// is 1. If the value is -1 the tests are repeating forever. +GTEST_DECLARE_int32_(repeat); + +// This flag controls whether Google Test includes Google Test internal +// stack frames in failure stack traces. +GTEST_DECLARE_bool_(show_internal_stack_frames); + +// When this flag is specified, tests' order is randomized on every iteration. +GTEST_DECLARE_bool_(shuffle); + +// This flag specifies the maximum number of stack frames to be +// printed in a failure message. +GTEST_DECLARE_int32_(stack_trace_depth); + +// When this flag is specified, a failed assertion will throw an +// exception if exceptions are enabled, or exit the program with a +// non-zero code otherwise. For use with an external test framework. +GTEST_DECLARE_bool_(throw_on_failure); + +// When this flag is set with a "host:port" string, on supported +// platforms test results are streamed to the specified port on +// the specified host machine. +GTEST_DECLARE_string_(stream_result_to); + +#if GTEST_USE_OWN_FLAGFILE_FLAG_ +GTEST_DECLARE_string_(flagfile); +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + +// The upper limit for valid stack trace depths. +const int kMaxStackTraceDepth = 100; + +namespace internal { + +class AssertHelper; +class DefaultGlobalTestPartResultReporter; +class ExecDeathTest; +class NoExecDeathTest; +class FinalSuccessChecker; +class GTestFlagSaver; +class StreamingListenerTest; +class TestResultAccessor; +class TestEventListenersAccessor; +class TestEventRepeater; +class UnitTestRecordPropertyTestHelper; +class WindowsDeathTest; +class FuchsiaDeathTest; +class UnitTestImpl* GetUnitTestImpl(); +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message); +std::set<std::string>* GetIgnoredParameterizedTestSuites(); + +} // namespace internal + +// The friend relationship of some of these classes is cyclic. +// If we don't forward declare them the compiler might confuse the classes +// in friendship clauses with same named classes on the scope. +class Test; +class TestSuite; + +// Old API is still available but deprecated +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +using TestCase = TestSuite; +#endif +class TestInfo; +class UnitTest; + +// A class for indicating whether an assertion was successful. When +// the assertion wasn't successful, the AssertionResult object +// remembers a non-empty message that describes how it failed. +// +// To create an instance of this class, use one of the factory functions +// (AssertionSuccess() and AssertionFailure()). +// +// This class is useful for two purposes: +// 1. Defining predicate functions to be used with Boolean test assertions +// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts +// 2. Defining predicate-format functions to be +// used with predicate assertions (ASSERT_PRED_FORMAT*, etc). +// +// For example, if you define IsEven predicate: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5))) +// will print the message +// +// Value of: IsEven(Fib(5)) +// Actual: false (5 is odd) +// Expected: true +// +// instead of a more opaque +// +// Value of: IsEven(Fib(5)) +// Actual: false +// Expected: true +// +// in case IsEven is a simple Boolean predicate. +// +// If you expect your predicate to be reused and want to support informative +// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up +// about half as often as positive ones in our tests), supply messages for +// both success and failure cases: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess() << n << " is even"; +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print +// +// Value of: IsEven(Fib(6)) +// Actual: true (8 is even) +// Expected: false +// +// NB: Predicates that support negative Boolean assertions have reduced +// performance in positive ones so be careful not to use them in tests +// that have lots (tens of thousands) of positive Boolean assertions. +// +// To use this class with EXPECT_PRED_FORMAT assertions such as: +// +// // Verifies that Foo() returns an even number. +// EXPECT_PRED_FORMAT1(IsEven, Foo()); +// +// you need to define: +// +// testing::AssertionResult IsEven(const char* expr, int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() +// << "Expected: " << expr << " is even\n Actual: it's " << n; +// } +// +// If Foo() returns 5, you will see the following message: +// +// Expected: Foo() is even +// Actual: it's 5 +// +class GTEST_API_ AssertionResult { + public: + // Copy constructor. + // Used in EXPECT_TRUE/FALSE(assertion_result). + AssertionResult(const AssertionResult& other); + +// C4800 is a level 3 warning in Visual Studio 2015 and earlier. +// This warning is not emitted in Visual Studio 2017. +// This warning is off by default starting in Visual Studio 2019 but can be +// enabled with command-line options. +#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920) + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */) +#endif + + // Used in the EXPECT_TRUE/FALSE(bool_expression). + // + // T must be contextually convertible to bool. + // + // The second parameter prevents this overload from being considered if + // the argument is implicitly convertible to AssertionResult. In that case + // we want AssertionResult's copy constructor to be used. + template <typename T> + explicit AssertionResult( + const T& success, + typename std::enable_if< + !std::is_convertible<T, AssertionResult>::value>::type* + /*enabler*/ + = nullptr) + : success_(success) {} + +#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920) + GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + + // Assignment operator. + AssertionResult& operator=(AssertionResult other) { + swap(other); + return *this; + } + + // Returns true if and only if the assertion succeeded. + operator bool() const { return success_; } // NOLINT + + // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. + AssertionResult operator!() const; + + // Returns the text streamed into this AssertionResult. Test assertions + // use it when they fail (i.e., the predicate's outcome doesn't match the + // assertion's expectation). When nothing has been streamed into the + // object, returns an empty string. + const char* message() const { + return message_.get() != nullptr ? message_->c_str() : ""; + } + // Deprecated; please use message() instead. + const char* failure_message() const { return message(); } + + // Streams a custom failure message into this object. + template <typename T> AssertionResult& operator<<(const T& value) { + AppendMessage(Message() << value); + return *this; + } + + // Allows streaming basic output manipulators such as endl or flush into + // this object. + AssertionResult& operator<<( + ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) { + AppendMessage(Message() << basic_manipulator); + return *this; + } + + private: + // Appends the contents of message to message_. + void AppendMessage(const Message& a_message) { + if (message_.get() == nullptr) message_.reset(new ::std::string); + message_->append(a_message.GetString().c_str()); + } + + // Swap the contents of this AssertionResult with other. + void swap(AssertionResult& other); + + // Stores result of the assertion predicate. + bool success_; + // Stores the message describing the condition in case the expectation + // construct is not satisfied with the predicate's outcome. + // Referenced via a pointer to avoid taking too much stack frame space + // with test assertions. + std::unique_ptr< ::std::string> message_; +}; + +// Makes a successful assertion result. +GTEST_API_ AssertionResult AssertionSuccess(); + +// Makes a failed assertion result. +GTEST_API_ AssertionResult AssertionFailure(); + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << msg. +GTEST_API_ AssertionResult AssertionFailure(const Message& msg); + +} // namespace testing + +// Includes the auto-generated header that implements a family of generic +// predicate assertion macros. This include comes late because it relies on +// APIs declared above. +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// 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. + +// This file is AUTOMATICALLY GENERATED on 01/02/2019 by command +// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! +// +// Implements a family of generic predicate assertion macros. +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + + +namespace testing { + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most 5. +// Please email googletestframework@googlegroups.com if you need +// support for higher arities. + +// GTEST_ASSERT_ is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT_(expression, on_failure) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar = (expression)) \ + ; \ + else \ + on_failure(gtest_ar.failure_message()) + + +// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +template <typename Pred, + typename T1> +AssertionResult AssertPred1Helper(const char* pred_text, + const char* e1, + Pred pred, + const T1& v1) { + if (pred(v1)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. +// Don't use this in your code. +#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, v1), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +#define GTEST_PRED1_(pred, v1, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \ + #v1, \ + pred, \ + v1), on_failure) + +// Unary predicate assertion macros. +#define EXPECT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +template <typename Pred, + typename T1, + typename T2> +AssertionResult AssertPred2Helper(const char* pred_text, + const char* e1, + const char* e2, + Pred pred, + const T1& v1, + const T2& v2) { + if (pred(v1, v2)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. +// Don't use this in your code. +#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +#define GTEST_PRED2_(pred, v1, v2, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \ + #v1, \ + #v2, \ + pred, \ + v1, \ + v2), on_failure) + +// Binary predicate assertion macros. +#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +template <typename Pred, + typename T1, + typename T2, + typename T3> +AssertionResult AssertPred3Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3) { + if (pred(v1, v2, v3)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. +// Don't use this in your code. +#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + pred, \ + v1, \ + v2, \ + v3), on_failure) + +// Ternary predicate assertion macros. +#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +template <typename Pred, + typename T1, + typename T2, + typename T3, + typename T4> +AssertionResult AssertPred4Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4) { + if (pred(v1, v2, v3, v4)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n" + << e4 << " evaluates to " << ::testing::PrintToString(v4); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. +// Don't use this in your code. +#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4), on_failure) + +// 4-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +template <typename Pred, + typename T1, + typename T2, + typename T3, + typename T4, + typename T5> +AssertionResult AssertPred5Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + const char* e5, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4, + const T5& v5) { + if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4 + << ", " << e5 << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n" + << e4 << " evaluates to " << ::testing::PrintToString(v4) << "\n" + << e5 << " evaluates to " << ::testing::PrintToString(v5); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. +// Don't use this in your code. +#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + #v5, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4, \ + v5), on_failure) + +// 5-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) + + + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +namespace testing { + +// The abstract class that all tests inherit from. +// +// In Google Test, a unit test program contains one or many TestSuites, and +// each TestSuite contains one or many Tests. +// +// When you define a test using the TEST macro, you don't need to +// explicitly derive from Test - the TEST macro automatically does +// this for you. +// +// The only time you derive from Test is when defining a test fixture +// to be used in a TEST_F. For example: +// +// class FooTest : public testing::Test { +// protected: +// void SetUp() override { ... } +// void TearDown() override { ... } +// ... +// }; +// +// TEST_F(FooTest, Bar) { ... } +// TEST_F(FooTest, Baz) { ... } +// +// Test is not copyable. +class GTEST_API_ Test { + public: + friend class TestInfo; + + // The d'tor is virtual as we intend to inherit from Test. + virtual ~Test(); + + // Sets up the stuff shared by all tests in this test suite. + // + // Google Test will call Foo::SetUpTestSuite() before running the first + // test in test suite Foo. Hence a sub-class can define its own + // SetUpTestSuite() method to shadow the one defined in the super + // class. + static void SetUpTestSuite() {} + + // Tears down the stuff shared by all tests in this test suite. + // + // Google Test will call Foo::TearDownTestSuite() after running the last + // test in test suite Foo. Hence a sub-class can define its own + // TearDownTestSuite() method to shadow the one defined in the super + // class. + static void TearDownTestSuite() {} + + // Legacy API is deprecated but still available. Use SetUpTestSuite and + // TearDownTestSuite instead. +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + static void TearDownTestCase() {} + static void SetUpTestCase() {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns true if and only if the current test has a fatal failure. + static bool HasFatalFailure(); + + // Returns true if and only if the current test has a non-fatal failure. + static bool HasNonfatalFailure(); + + // Returns true if and only if the current test was skipped. + static bool IsSkipped(); + + // Returns true if and only if the current test has a (either fatal or + // non-fatal) failure. + static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } + + // Logs a property for the current test, test suite, or for the entire + // invocation of the test program when used outside of the context of a + // test suite. Only the last value for a given key is remembered. These + // are public static so they can be called from utility functions that are + // not members of the test fixture. Calls to RecordProperty made during + // lifespan of the test (from the moment its constructor starts to the + // moment its destructor finishes) will be output in XML as attributes of + // the <testcase> element. Properties recorded from fixture's + // SetUpTestSuite or TearDownTestSuite are logged as attributes of the + // corresponding <testsuite> element. Calls to RecordProperty made in the + // global context (before or after invocation of RUN_ALL_TESTS and from + // SetUp/TearDown method of Environment objects registered with Google + // Test) will be output as attributes of the <testsuites> element. + static void RecordProperty(const std::string& key, const std::string& value); + static void RecordProperty(const std::string& key, int value); + + protected: + // Creates a Test object. + Test(); + + // Sets up the test fixture. + virtual void SetUp(); + + // Tears down the test fixture. + virtual void TearDown(); + + private: + // Returns true if and only if the current test has the same fixture class + // as the first test in the current test suite. + static bool HasSameFixtureClass(); + + // Runs the test after the test fixture has been set up. + // + // A sub-class must implement this to define the test logic. + // + // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. + // Instead, use the TEST or TEST_F macro. + virtual void TestBody() = 0; + + // Sets up, executes, and tears down the test. + void Run(); + + // Deletes self. We deliberately pick an unusual name for this + // internal method to avoid clashing with names used in user TESTs. + void DeleteSelf_() { delete this; } + + const std::unique_ptr<GTEST_FLAG_SAVER_> gtest_flag_saver_; + + // Often a user misspells SetUp() as Setup() and spends a long time + // wondering why it is never called by Google Test. The declaration of + // the following method is solely for catching such an error at + // compile time: + // + // - The return type is deliberately chosen to be not void, so it + // will be a conflict if void Setup() is declared in the user's + // test fixture. + // + // - This method is private, so it will be another compiler error + // if the method is called from the user's test fixture. + // + // DO NOT OVERRIDE THIS FUNCTION. + // + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; } + + // We disallow copying Tests. + GTEST_DISALLOW_COPY_AND_ASSIGN_(Test); +}; + +typedef internal::TimeInMillis TimeInMillis; + +// A copyable object representing a user specified test property which can be +// output as a key/value string pair. +// +// Don't inherit from TestProperty as its destructor is not virtual. +class TestProperty { + public: + // C'tor. TestProperty does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestProperty object. + TestProperty(const std::string& a_key, const std::string& a_value) : + key_(a_key), value_(a_value) { + } + + // Gets the user supplied key. + const char* key() const { + return key_.c_str(); + } + + // Gets the user supplied value. + const char* value() const { + return value_.c_str(); + } + + // Sets a new value, overriding the one supplied in the constructor. + void SetValue(const std::string& new_value) { + value_ = new_value; + } + + private: + // The key supplied by the user. + std::string key_; + // The value supplied by the user. + std::string value_; +}; + +// The result of a single Test. This includes a list of +// TestPartResults, a list of TestProperties, a count of how many +// death tests there are in the Test, and how much time it took to run +// the Test. +// +// TestResult is not copyable. +class GTEST_API_ TestResult { + public: + // Creates an empty TestResult. + TestResult(); + + // D'tor. Do not inherit from TestResult. + ~TestResult(); + + // Gets the number of all test parts. This is the sum of the number + // of successful test parts and the number of failed test parts. + int total_part_count() const; + + // Returns the number of the test properties. + int test_property_count() const; + + // Returns true if and only if the test passed (i.e. no test part failed). + bool Passed() const { return !Skipped() && !Failed(); } + + // Returns true if and only if the test was skipped. + bool Skipped() const; + + // Returns true if and only if the test failed. + bool Failed() const; + + // Returns true if and only if the test fatally failed. + bool HasFatalFailure() const; + + // Returns true if and only if the test has a non-fatal failure. + bool HasNonfatalFailure() const; + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Gets the time of the test case start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Returns the i-th test part result among all the results. i can range from 0 + // to total_part_count() - 1. If i is not in that range, aborts the program. + const TestPartResult& GetTestPartResult(int i) const; + + // Returns the i-th test property. i can range from 0 to + // test_property_count() - 1. If i is not in that range, aborts the + // program. + const TestProperty& GetTestProperty(int i) const; + + private: + friend class TestInfo; + friend class TestSuite; + friend class UnitTest; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::ExecDeathTest; + friend class internal::TestResultAccessor; + friend class internal::UnitTestImpl; + friend class internal::WindowsDeathTest; + friend class internal::FuchsiaDeathTest; + + // Gets the vector of TestPartResults. + const std::vector<TestPartResult>& test_part_results() const { + return test_part_results_; + } + + // Gets the vector of TestProperties. + const std::vector<TestProperty>& test_properties() const { + return test_properties_; + } + + // Sets the start time. + void set_start_timestamp(TimeInMillis start) { start_timestamp_ = start; } + + // Sets the elapsed time. + void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } + + // Adds a test property to the list. The property is validated and may add + // a non-fatal failure if invalid (e.g., if it conflicts with reserved + // key names). If a property is already recorded for the same key, the + // value will be updated, rather than storing multiple values for the same + // key. xml_element specifies the element for which the property is being + // recorded and is used for validation. + void RecordProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a failure if the key is a reserved attribute of Google Test + // testsuite tags. Returns true if the property is valid. + // FIXME: Validate attribute names are legal and human readable. + static bool ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a test part result to the list. + void AddTestPartResult(const TestPartResult& test_part_result); + + // Returns the death test count. + int death_test_count() const { return death_test_count_; } + + // Increments the death test count, returning the new count. + int increment_death_test_count() { return ++death_test_count_; } + + // Clears the test part results. + void ClearTestPartResults(); + + // Clears the object. + void Clear(); + + // Protects mutable state of the property vector and of owned + // properties, whose values may be updated. + internal::Mutex test_properties_mutex_; + + // The vector of TestPartResults + std::vector<TestPartResult> test_part_results_; + // The vector of TestProperties + std::vector<TestProperty> test_properties_; + // Running count of death tests. + int death_test_count_; + // The start time, in milliseconds since UNIX Epoch. + TimeInMillis start_timestamp_; + // The elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + + // We disallow copying TestResult. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult); +}; // class TestResult + +// A TestInfo object stores the following information about a test: +// +// Test suite name +// Test name +// Whether the test should be run +// A function pointer that creates the test object when invoked +// Test result +// +// The constructor of TestInfo registers itself with the UnitTest +// singleton such that the RUN_ALL_TESTS() macro knows which tests to +// run. +class GTEST_API_ TestInfo { + public: + // Destructs a TestInfo object. This function is not virtual, so + // don't inherit from TestInfo. + ~TestInfo(); + + // Returns the test suite name. + const char* test_suite_name() const { return test_suite_name_.c_str(); } + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const char* test_case_name() const { return test_suite_name(); } +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns the test name. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a typed + // or a type-parameterized test. + const char* type_param() const { + if (type_param_.get() != nullptr) return type_param_->c_str(); + return nullptr; + } + + // Returns the text representation of the value parameter, or NULL if this + // is not a value-parameterized test. + const char* value_param() const { + if (value_param_.get() != nullptr) return value_param_->c_str(); + return nullptr; + } + + // Returns the file name where this test is defined. + const char* file() const { return location_.file.c_str(); } + + // Returns the line where this test is defined. + int line() const { return location_.line; } + + // Return true if this test should not be run because it's in another shard. + bool is_in_another_shard() const { return is_in_another_shard_; } + + // Returns true if this test should run, that is if the test is not + // disabled (or it is disabled but the also_run_disabled_tests flag has + // been specified) and its full name matches the user-specified filter. + // + // Google Test allows the user to filter the tests by their full names. + // The full name of a test Bar in test suite Foo is defined as + // "Foo.Bar". Only the tests that match the filter will run. + // + // A filter is a colon-separated list of glob (not regex) patterns, + // optionally followed by a '-' and a colon-separated list of + // negative patterns (tests to exclude). A test is run if it + // matches one of the positive patterns and does not match any of + // the negative patterns. + // + // For example, *A*:Foo.* is a filter that matches any string that + // contains the character 'A' or starts with "Foo.". + bool should_run() const { return should_run_; } + + // Returns true if and only if this test will appear in the XML report. + bool is_reportable() const { + // The XML report includes tests matching the filter, excluding those + // run in other shards. + return matches_filter_ && !is_in_another_shard_; + } + + // Returns the result of the test. + const TestResult* result() const { return &result_; } + + private: +#if GTEST_HAS_DEATH_TEST + friend class internal::DefaultDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + friend class Test; + friend class TestSuite; + friend class internal::UnitTestImpl; + friend class internal::StreamingListenerTest; + friend TestInfo* internal::MakeAndRegisterTestInfo( + const char* test_suite_name, const char* name, const char* type_param, + const char* value_param, internal::CodeLocation code_location, + internal::TypeId fixture_class_id, internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc, + internal::TestFactoryBase* factory); + + // Constructs a TestInfo object. The newly constructed instance assumes + // ownership of the factory object. + TestInfo(const std::string& test_suite_name, const std::string& name, + const char* a_type_param, // NULL if not a type-parameterized test + const char* a_value_param, // NULL if not a value-parameterized test + internal::CodeLocation a_code_location, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory); + + // Increments the number of death tests encountered in this test so + // far. + int increment_death_test_count() { + return result_.increment_death_test_count(); + } + + // Creates the test object, runs it, records its result, and then + // deletes it. + void Run(); + + // Skip and records the test result for this object. + void Skip(); + + static void ClearTestResult(TestInfo* test_info) { + test_info->result_.Clear(); + } + + // These fields are immutable properties of the test. + const std::string test_suite_name_; // test suite name + const std::string name_; // Test name + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const std::unique_ptr<const ::std::string> type_param_; + // Text representation of the value parameter, or NULL if this is not a + // value-parameterized test. + const std::unique_ptr<const ::std::string> value_param_; + internal::CodeLocation location_; + const internal::TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True if and only if this test should run + bool is_disabled_; // True if and only if this test is disabled + bool matches_filter_; // True if this test matches the + // user-specified filter. + bool is_in_another_shard_; // Will be run in another shard. + internal::TestFactoryBase* const factory_; // The factory that creates + // the test object + + // This field is mutable and needs to be reset before running the + // test for the second time. + TestResult result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo); +}; + +// A test suite, which consists of a vector of TestInfos. +// +// TestSuite is not copyable. +class GTEST_API_ TestSuite { + public: + // Creates a TestSuite with the given name. + // + // TestSuite does NOT have a default constructor. Always use this + // constructor to create a TestSuite object. + // + // Arguments: + // + // name: name of the test suite + // a_type_param: the name of the test's type parameter, or NULL if + // this is not a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test suite + // tear_down_tc: pointer to the function that tears down the test suite + TestSuite(const char* name, const char* a_type_param, + internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc); + + // Destructor of TestSuite. + virtual ~TestSuite(); + + // Gets the name of the TestSuite. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a + // type-parameterized test suite. + const char* type_param() const { + if (type_param_.get() != nullptr) return type_param_->c_str(); + return nullptr; + } + + // Returns true if any test in this test suite should run. + bool should_run() const { return should_run_; } + + // Gets the number of successful tests in this test suite. + int successful_test_count() const; + + // Gets the number of skipped tests in this test suite. + int skipped_test_count() const; + + // Gets the number of failed tests in this test suite. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests in this test suite. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Get the number of tests in this test suite that should run. + int test_to_run_count() const; + + // Gets the number of all tests in this test suite. + int total_test_count() const; + + // Returns true if and only if the test suite passed. + bool Passed() const { return !Failed(); } + + // Returns true if and only if the test suite failed. + bool Failed() const { + return failed_test_count() > 0 || ad_hoc_test_result().Failed(); + } + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Gets the time of the test suite start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + const TestInfo* GetTestInfo(int i) const; + + // Returns the TestResult that holds test properties recorded during + // execution of SetUpTestSuite and TearDownTestSuite. + const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; } + + private: + friend class Test; + friend class internal::UnitTestImpl; + + // Gets the (mutable) vector of TestInfos in this TestSuite. + std::vector<TestInfo*>& test_info_list() { return test_info_list_; } + + // Gets the (immutable) vector of TestInfos in this TestSuite. + const std::vector<TestInfo*>& test_info_list() const { + return test_info_list_; + } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + TestInfo* GetMutableTestInfo(int i); + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Adds a TestInfo to this test suite. Will delete the TestInfo upon + // destruction of the TestSuite object. + void AddTestInfo(TestInfo * test_info); + + // Clears the results of all tests in this test suite. + void ClearResult(); + + // Clears the results of all tests in the given test suite. + static void ClearTestSuiteResult(TestSuite* test_suite) { + test_suite->ClearResult(); + } + + // Runs every test in this TestSuite. + void Run(); + + // Skips the execution of tests under this TestSuite + void Skip(); + + // Runs SetUpTestSuite() for this TestSuite. This wrapper is needed + // for catching exceptions thrown from SetUpTestSuite(). + void RunSetUpTestSuite() { + if (set_up_tc_ != nullptr) { + (*set_up_tc_)(); + } + } + + // Runs TearDownTestSuite() for this TestSuite. This wrapper is + // needed for catching exceptions thrown from TearDownTestSuite(). + void RunTearDownTestSuite() { + if (tear_down_tc_ != nullptr) { + (*tear_down_tc_)(); + } + } + + // Returns true if and only if test passed. + static bool TestPassed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Passed(); + } + + // Returns true if and only if test skipped. + static bool TestSkipped(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Skipped(); + } + + // Returns true if and only if test failed. + static bool TestFailed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Failed(); + } + + // Returns true if and only if the test is disabled and will be reported in + // the XML report. + static bool TestReportableDisabled(const TestInfo* test_info) { + return test_info->is_reportable() && test_info->is_disabled_; + } + + // Returns true if and only if test is disabled. + static bool TestDisabled(const TestInfo* test_info) { + return test_info->is_disabled_; + } + + // Returns true if and only if this test will appear in the XML report. + static bool TestReportable(const TestInfo* test_info) { + return test_info->is_reportable(); + } + + // Returns true if the given test should run. + static bool ShouldRunTest(const TestInfo* test_info) { + return test_info->should_run(); + } + + // Shuffles the tests in this test suite. + void ShuffleTests(internal::Random* random); + + // Restores the test order to before the first shuffle. + void UnshuffleTests(); + + // Name of the test suite. + std::string name_; + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const std::unique_ptr<const ::std::string> type_param_; + // The vector of TestInfos in their original order. It owns the + // elements in the vector. + std::vector<TestInfo*> test_info_list_; + // Provides a level of indirection for the test list to allow easy + // shuffling and restoring the test order. The i-th element in this + // vector is the index of the i-th test in the shuffled test list. + std::vector<int> test_indices_; + // Pointer to the function that sets up the test suite. + internal::SetUpTestSuiteFunc set_up_tc_; + // Pointer to the function that tears down the test suite. + internal::TearDownTestSuiteFunc tear_down_tc_; + // True if and only if any test in this test suite should run. + bool should_run_; + // The start time, in milliseconds since UNIX Epoch. + TimeInMillis start_timestamp_; + // Elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + // Holds test properties recorded during execution of SetUpTestSuite and + // TearDownTestSuite. + TestResult ad_hoc_test_result_; + + // We disallow copying TestSuites. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestSuite); +}; + +// An Environment object is capable of setting up and tearing down an +// environment. You should subclass this to define your own +// environment(s). +// +// An Environment object does the set-up and tear-down in virtual +// methods SetUp() and TearDown() instead of the constructor and the +// destructor, as: +// +// 1. You cannot safely throw from a destructor. This is a problem +// as in some cases Google Test is used where exceptions are enabled, and +// we may want to implement ASSERT_* using exceptions where they are +// available. +// 2. You cannot use ASSERT_* directly in a constructor or +// destructor. +class Environment { + public: + // The d'tor is virtual as we need to subclass Environment. + virtual ~Environment() {} + + // Override this to define how to set up the environment. + virtual void SetUp() {} + + // Override this to define how to tear down the environment. + virtual void TearDown() {} + private: + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; } +}; + +#if GTEST_HAS_EXCEPTIONS + +// Exception which can be thrown from TestEventListener::OnTestPartResult. +class GTEST_API_ AssertionException + : public internal::GoogleTestFailureException { + public: + explicit AssertionException(const TestPartResult& result) + : GoogleTestFailureException(result) {} +}; + +#endif // GTEST_HAS_EXCEPTIONS + +// The interface for tracing execution of tests. The methods are organized in +// the order the corresponding events are fired. +class TestEventListener { + public: + virtual ~TestEventListener() {} + + // Fired before any test activity starts. + virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; + + // Fired before each iteration of tests starts. There may be more than + // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration + // index, starting from 0. + virtual void OnTestIterationStart(const UnitTest& unit_test, + int iteration) = 0; + + // Fired before environment set-up for each iteration of tests starts. + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; + + // Fired after environment set-up for each iteration of tests ends. + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0; + + // Fired before the test suite starts. + virtual void OnTestSuiteStart(const TestSuite& /*test_suite*/) {} + + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + virtual void OnTestCaseStart(const TestCase& /*test_case*/) {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Fired before the test starts. + virtual void OnTestStart(const TestInfo& test_info) = 0; + + // Fired after a failed assertion or a SUCCEED() invocation. + // If you want to throw an exception from this function to skip to the next + // TEST, it must be AssertionException defined above, or inherited from it. + virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; + + // Fired after the test ends. + virtual void OnTestEnd(const TestInfo& test_info) = 0; + + // Fired after the test suite ends. + virtual void OnTestSuiteEnd(const TestSuite& /*test_suite*/) {} + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Fired before environment tear-down for each iteration of tests starts. + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0; + + // Fired after environment tear-down for each iteration of tests ends. + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0; + + // Fired after each iteration of tests finishes. + virtual void OnTestIterationEnd(const UnitTest& unit_test, + int iteration) = 0; + + // Fired after all test activities have ended. + virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0; +}; + +// The convenience class for users who need to override just one or two +// methods and are not concerned that a possible change to a signature of +// the methods they override will not be caught during the build. For +// comments about each method please see the definition of TestEventListener +// above. +class EmptyTestEventListener : public TestEventListener { + public: + void OnTestProgramStart(const UnitTest& /*unit_test*/) override {} + void OnTestIterationStart(const UnitTest& /*unit_test*/, + int /*iteration*/) override {} + void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) override {} + void OnTestSuiteStart(const TestSuite& /*test_suite*/) override {} +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseStart(const TestCase& /*test_case*/) override {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnTestStart(const TestInfo& /*test_info*/) override {} + void OnTestPartResult(const TestPartResult& /*test_part_result*/) override {} + void OnTestEnd(const TestInfo& /*test_info*/) override {} + void OnTestSuiteEnd(const TestSuite& /*test_suite*/) override {} +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseEnd(const TestCase& /*test_case*/) override {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) override {} + void OnTestIterationEnd(const UnitTest& /*unit_test*/, + int /*iteration*/) override {} + void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {} +}; + +// TestEventListeners lets users add listeners to track events in Google Test. +class GTEST_API_ TestEventListeners { + public: + TestEventListeners(); + ~TestEventListeners(); + + // Appends an event listener to the end of the list. Google Test assumes + // the ownership of the listener (i.e. it will delete the listener when + // the test program finishes). + void Append(TestEventListener* listener); + + // Removes the given event listener from the list and returns it. It then + // becomes the caller's responsibility to delete the listener. Returns + // NULL if the listener is not found in the list. + TestEventListener* Release(TestEventListener* listener); + + // Returns the standard listener responsible for the default console + // output. Can be removed from the listeners list to shut down default + // console output. Note that removing this object from the listener list + // with Release transfers its ownership to the caller and makes this + // function return NULL the next time. + TestEventListener* default_result_printer() const { + return default_result_printer_; + } + + // Returns the standard listener responsible for the default XML output + // controlled by the --gtest_output=xml flag. Can be removed from the + // listeners list by users who want to shut down the default XML output + // controlled by this flag and substitute it with custom one. Note that + // removing this object from the listener list with Release transfers its + // ownership to the caller and makes this function return NULL the next + // time. + TestEventListener* default_xml_generator() const { + return default_xml_generator_; + } + + private: + friend class TestSuite; + friend class TestInfo; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::NoExecDeathTest; + friend class internal::TestEventListenersAccessor; + friend class internal::UnitTestImpl; + + // Returns repeater that broadcasts the TestEventListener events to all + // subscribers. + TestEventListener* repeater(); + + // Sets the default_result_printer attribute to the provided listener. + // The listener is also added to the listener list and previous + // default_result_printer is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultResultPrinter(TestEventListener* listener); + + // Sets the default_xml_generator attribute to the provided listener. The + // listener is also added to the listener list and previous + // default_xml_generator is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultXmlGenerator(TestEventListener* listener); + + // Controls whether events will be forwarded by the repeater to the + // listeners in the list. + bool EventForwardingEnabled() const; + void SuppressEventForwarding(); + + // The actual list of listeners. + internal::TestEventRepeater* repeater_; + // Listener responsible for the standard result output. + TestEventListener* default_result_printer_; + // Listener responsible for the creation of the XML output file. + TestEventListener* default_xml_generator_; + + // We disallow copying TestEventListeners. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventListeners); +}; + +// A UnitTest consists of a vector of TestSuites. +// +// This is a singleton class. The only instance of UnitTest is +// created when UnitTest::GetInstance() is first called. This +// instance is never deleted. +// +// UnitTest is not copyable. +// +// This class is thread-safe as long as the methods are called +// according to their specification. +class GTEST_API_ UnitTest { + public: + // Gets the singleton UnitTest object. The first time this method + // is called, a UnitTest object is constructed and returned. + // Consecutive calls will return the same object. + static UnitTest* GetInstance(); + + // Runs all tests in this UnitTest object and prints the result. + // Returns 0 if successful, or 1 otherwise. + // + // This method can only be called from the main thread. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + int Run() GTEST_MUST_USE_RESULT_; + + // Returns the working directory when the first TEST() or TEST_F() + // was executed. The UnitTest object owns the string. + const char* original_working_dir() const; + + // Returns the TestSuite object for the test that's currently running, + // or NULL if no test is running. + const TestSuite* current_test_suite() const GTEST_LOCK_EXCLUDED_(mutex_); + +// Legacy API is still available but deprecated +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const TestCase* current_test_case() const GTEST_LOCK_EXCLUDED_(mutex_); +#endif + + // Returns the TestInfo object for the test that's currently running, + // or NULL if no test is running. + const TestInfo* current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_); + + // Returns the random seed used at the start of the current test run. + int random_seed() const; + + // Returns the ParameterizedTestSuiteRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + internal::ParameterizedTestSuiteRegistry& parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_); + + // Gets the number of successful test suites. + int successful_test_suite_count() const; + + // Gets the number of failed test suites. + int failed_test_suite_count() const; + + // Gets the number of all test suites. + int total_test_suite_count() const; + + // Gets the number of all test suites that contain at least one test + // that should run. + int test_suite_to_run_count() const; + + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + int successful_test_case_count() const; + int failed_test_case_count() const; + int total_test_case_count() const; + int test_case_to_run_count() const; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of skipped tests. + int skipped_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const; + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const; + + // Returns true if and only if the unit test passed (i.e. all test suites + // passed). + bool Passed() const; + + // Returns true if and only if the unit test failed (i.e. some test suite + // failed or something outside of all tests failed). + bool Failed() const; + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + const TestSuite* GetTestSuite(int i) const; + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const TestCase* GetTestCase(int i) const; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns the TestResult containing information on test failures and + // properties logged outside of individual test suites. + const TestResult& ad_hoc_test_result() const; + + // Returns the list of event listeners that can be used to track events + // inside Google Test. + TestEventListeners& listeners(); + + private: + // Registers and returns a global test environment. When a test + // program is run, all global test environments will be set-up in + // the order they were registered. After all tests in the program + // have finished, all global test environments will be torn-down in + // the *reverse* order they were registered. + // + // The UnitTest object takes ownership of the given environment. + // + // This method can only be called from the main thread. + Environment* AddEnvironment(Environment* env); + + // Adds a TestPartResult to the current TestResult object. All + // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) + // eventually call this to report their results. The user code + // should use the assertion macros instead of calling this directly. + void AddTestPartResult(TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Adds a TestProperty to the current TestResult object when invoked from + // inside a test, to current TestSuite's ad_hoc_test_result_ when invoked + // from SetUpTestSuite or TearDownTestSuite, or to the global property set + // when invoked elsewhere. If the result already contains a property with + // the same key, the value will be updated. + void RecordProperty(const std::string& key, const std::string& value); + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + TestSuite* GetMutableTestSuite(int i); + + // Accessors for the implementation object. + internal::UnitTestImpl* impl() { return impl_; } + const internal::UnitTestImpl* impl() const { return impl_; } + + // These classes and functions are friends as they need to access private + // members of UnitTest. + friend class ScopedTrace; + friend class Test; + friend class internal::AssertHelper; + friend class internal::StreamingListenerTest; + friend class internal::UnitTestRecordPropertyTestHelper; + friend Environment* AddGlobalTestEnvironment(Environment* env); + friend std::set<std::string>* internal::GetIgnoredParameterizedTestSuites(); + friend internal::UnitTestImpl* internal::GetUnitTestImpl(); + friend void internal::ReportFailureInUnknownLocation( + TestPartResult::Type result_type, + const std::string& message); + + // Creates an empty UnitTest. + UnitTest(); + + // D'tor + virtual ~UnitTest(); + + // Pushes a trace defined by SCOPED_TRACE() on to the per-thread + // Google Test trace stack. + void PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Pops a trace from the per-thread Google Test trace stack. + void PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_); + + // Protects mutable state in *impl_. This is mutable as some const + // methods need to lock it too. + mutable internal::Mutex mutex_; + + // Opaque implementation object. This field is never changed once + // the object is constructed. We don't mark it as const here, as + // doing so will cause a warning in the constructor of UnitTest. + // Mutable state in *impl_ is protected by mutex_. + internal::UnitTestImpl* impl_; + + // We disallow copying UnitTest. + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTest); +}; + +// A convenient wrapper for adding an environment for the test +// program. +// +// You should call this before RUN_ALL_TESTS() is called, probably in +// main(). If you use gtest_main, you need to call this before main() +// starts for it to take effect. For example, you can define a global +// variable like this: +// +// testing::Environment* const foo_env = +// testing::AddGlobalTestEnvironment(new FooEnvironment); +// +// However, we strongly recommend you to write your own main() and +// call AddGlobalTestEnvironment() there, as relying on initialization +// of global variables makes the code harder to read and may cause +// problems when you register multiple environments from different +// translation units and the environments have dependencies among them +// (remember that the compiler doesn't guarantee the order in which +// global variables from different translation units are initialized). +inline Environment* AddGlobalTestEnvironment(Environment* env) { + return UnitTest::GetInstance()->AddEnvironment(env); +} + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +GTEST_API_ void InitGoogleTest(int* argc, char** argv); + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); + +// This overloaded version can be used on Arduino/embedded platforms where +// there is no argc/argv. +GTEST_API_ void InitGoogleTest(); + +namespace internal { + +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperEQ. This helps reduce the overhead of some sanitizers +// when calling EXPECT_* in a tight loop. +template <typename T1, typename T2> +AssertionResult CmpHelperEQFailure(const char* lhs_expression, + const char* rhs_expression, + const T1& lhs, const T2& rhs) { + return EqFailure(lhs_expression, + rhs_expression, + FormatForComparisonFailureMessage(lhs, rhs), + FormatForComparisonFailureMessage(rhs, lhs), + false); +} + +// This block of code defines operator==/!= +// to block lexical scope lookup. +// It prevents using invalid operator==/!= defined at namespace scope. +struct faketype {}; +inline bool operator==(faketype, faketype) { return true; } +inline bool operator!=(faketype, faketype) { return false; } + +// The helper function for {ASSERT|EXPECT}_EQ. +template <typename T1, typename T2> +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, + const T1& lhs, + const T2& rhs) { + if (lhs == rhs) { + return AssertionSuccess(); + } + + return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs); +} + +class EqHelper { + public: + // This templatized version is for the general case. + template < + typename T1, typename T2, + // Disable this overload for cases where one argument is a pointer + // and the other is the null pointer constant. + typename std::enable_if<!std::is_integral<T1>::value || + !std::is_pointer<T2>::value>::type* = nullptr> + static AssertionResult Compare(const char* lhs_expression, + const char* rhs_expression, const T1& lhs, + const T2& rhs) { + return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs); + } + + // With this overloaded version, we allow anonymous enums to be used + // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous + // enums can be implicitly cast to BiggestInt. + // + // Even though its body looks the same as the above version, we + // cannot merge the two, as it will make anonymous enums unhappy. + static AssertionResult Compare(const char* lhs_expression, + const char* rhs_expression, + BiggestInt lhs, + BiggestInt rhs) { + return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs); + } + + template <typename T> + static AssertionResult Compare( + const char* lhs_expression, const char* rhs_expression, + // Handle cases where '0' is used as a null pointer literal. + std::nullptr_t /* lhs */, T* rhs) { + // We already know that 'lhs' is a null pointer. + return CmpHelperEQ(lhs_expression, rhs_expression, static_cast<T*>(nullptr), + rhs); + } +}; + +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperOP. This helps reduce the overhead of some sanitizers +// when calling EXPECT_OP in a tight loop. +template <typename T1, typename T2> +AssertionResult CmpHelperOpFailure(const char* expr1, const char* expr2, + const T1& val1, const T2& val2, + const char* op) { + return AssertionFailure() + << "Expected: (" << expr1 << ") " << op << " (" << expr2 + << "), actual: " << FormatForComparisonFailureMessage(val1, val2) + << " vs " << FormatForComparisonFailureMessage(val2, val1); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste +// of similar code. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ +template <typename T1, typename T2>\ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + const T1& val1, const T2& val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + return CmpHelperOpFailure(expr1, expr2, val1, val2, #op);\ + }\ +} + +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// Implements the helper function for {ASSERT|EXPECT}_NE +GTEST_IMPL_CMP_HELPER_(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE +GTEST_IMPL_CMP_HELPER_(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT +GTEST_IMPL_CMP_HELPER_(LT, <) +// Implements the helper function for {ASSERT|EXPECT}_GE +GTEST_IMPL_CMP_HELPER_(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT +GTEST_IMPL_CMP_HELPER_(GT, >) + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRNE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + + +// Helper function for *_STREQ on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2); + +// Helper function for *_STRNE on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2); + +} // namespace internal + +// IsSubstring() and IsNotSubstring() are intended to be used as the +// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by +// themselves. They check whether needle is a substring of haystack +// (NULL is considered a substring of itself only), and return an +// appropriate error message when they fail. +// +// The {needle,haystack}_expr arguments are the stringified +// expressions that generated the two real arguments. +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +// Helper template function for comparing floating-points. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template <typename RawType> +AssertionResult CmpHelperFloatingPointEQ(const char* lhs_expression, + const char* rhs_expression, + RawType lhs_value, + RawType rhs_value) { + const FloatingPoint<RawType> lhs(lhs_value), rhs(rhs_value); + + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + ::std::stringstream lhs_ss; + lhs_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2) + << lhs_value; + + ::std::stringstream rhs_ss; + rhs_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2) + << rhs_value; + + return EqFailure(lhs_expression, + rhs_expression, + StringStreamToString(&lhs_ss), + StringStreamToString(&rhs_ss), + false); +} + +// Helper function for implementing ASSERT_NEAR. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// A class that enables one to stream messages to assertion macros +class GTEST_API_ AssertHelper { + public: + // Constructor. + AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message); + ~AssertHelper(); + + // Message assignment is a semantic trick to enable assertion + // streaming; see the GTEST_MESSAGE_ macro below. + void operator=(const Message& message) const; + + private: + // We put our data in a struct so that the size of the AssertHelper class can + // be as small as possible. This is important because gcc is incapable of + // re-using stack space even for temporary variables, so every EXPECT_EQ + // reserves stack space for another AssertHelper. + struct AssertHelperData { + AssertHelperData(TestPartResult::Type t, + const char* srcfile, + int line_num, + const char* msg) + : type(t), file(srcfile), line(line_num), message(msg) { } + + TestPartResult::Type const type; + const char* const file; + int const line; + std::string const message; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData); + }; + + AssertHelperData* const data_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper); +}; + +} // namespace internal + +// The pure interface class that all value-parameterized tests inherit from. +// A value-parameterized class must inherit from both ::testing::Test and +// ::testing::WithParamInterface. In most cases that just means inheriting +// from ::testing::TestWithParam, but more complicated test hierarchies +// may need to inherit from Test and WithParamInterface at different levels. +// +// This interface has support for accessing the test parameter value via +// the GetParam() method. +// +// Use it with one of the parameter generator defining functions, like Range(), +// Values(), ValuesIn(), Bool(), and Combine(). +// +// class FooTest : public ::testing::TestWithParam<int> { +// protected: +// FooTest() { +// // Can use GetParam() here. +// } +// ~FooTest() override { +// // Can use GetParam() here. +// } +// void SetUp() override { +// // Can use GetParam() here. +// } +// void TearDown override { +// // Can use GetParam() here. +// } +// }; +// TEST_P(FooTest, DoesBar) { +// // Can use GetParam() method here. +// Foo foo; +// ASSERT_TRUE(foo.DoesBar(GetParam())); +// } +// INSTANTIATE_TEST_SUITE_P(OneToTenRange, FooTest, ::testing::Range(1, 10)); + +template <typename T> +class WithParamInterface { + public: + typedef T ParamType; + virtual ~WithParamInterface() {} + + // The current parameter value. Is also available in the test fixture's + // constructor. + static const ParamType& GetParam() { + GTEST_CHECK_(parameter_ != nullptr) + << "GetParam() can only be called inside a value-parameterized test " + << "-- did you intend to write TEST_P instead of TEST_F?"; + return *parameter_; + } + + private: + // Sets parameter value. The caller is responsible for making sure the value + // remains alive and unchanged throughout the current test. + static void SetParam(const ParamType* parameter) { + parameter_ = parameter; + } + + // Static value used for accessing parameter during a test lifetime. + static const ParamType* parameter_; + + // TestClass must be a subclass of WithParamInterface<T> and Test. + template <class TestClass> friend class internal::ParameterizedTestFactory; +}; + +template <typename T> +const T* WithParamInterface<T>::parameter_ = nullptr; + +// Most value-parameterized classes can ignore the existence of +// WithParamInterface, and can just inherit from ::testing::TestWithParam. + +template <typename T> +class TestWithParam : public Test, public WithParamInterface<T> { +}; + +// Macros for indicating success/failure in test code. + +// Skips test in runtime. +// Skipping test aborts current function. +// Skipped tests are neither successful nor failed. +#define GTEST_SKIP() GTEST_SKIP_("") + +// ADD_FAILURE unconditionally adds a failure to the current test. +// SUCCEED generates a success - it doesn't automatically make the +// current test successful, as a test is only successful when it has +// no failure. +// +// EXPECT_* verifies that a certain condition is satisfied. If not, +// it behaves like ADD_FAILURE. In particular: +// +// EXPECT_TRUE verifies that a Boolean condition is true. +// EXPECT_FALSE verifies that a Boolean condition is false. +// +// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except +// that they will also abort the current function on failure. People +// usually want the fail-fast behavior of FAIL and ASSERT_*, but those +// writing data-driven tests often find themselves using ADD_FAILURE +// and EXPECT_* more. + +// Generates a nonfatal failure with a generic message. +#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") + +// Generates a nonfatal failure at the given source file location with +// a generic message. +#define ADD_FAILURE_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kNonFatalFailure) + +// Generates a fatal failure with a generic message. +#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") + +// Like GTEST_FAIL(), but at the given source file location. +#define GTEST_FAIL_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kFatalFailure) + +// Define this macro to 1 to omit the definition of FAIL(), which is a +// generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_FAIL +# define FAIL() GTEST_FAIL() +#endif + +// Generates a success with a generic message. +#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded") + +// Define this macro to 1 to omit the definition of SUCCEED(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_SUCCEED +# define SUCCEED() GTEST_SUCCEED() +#endif + +// Macros for testing exceptions. +// +// * {ASSERT|EXPECT}_THROW(statement, expected_exception): +// Tests that the statement throws the expected exception. +// * {ASSERT|EXPECT}_NO_THROW(statement): +// Tests that the statement doesn't throw any exception. +// * {ASSERT|EXPECT}_ANY_THROW(statement): +// Tests that the statement throws an exception. + +#define EXPECT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) +#define EXPECT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define EXPECT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define ASSERT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_) +#define ASSERT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_) +#define ASSERT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_) + +// Boolean assertions. Condition can be either a Boolean expression or an +// AssertionResult. For more information on how to use AssertionResult with +// these macros see comments on that class. +#define GTEST_EXPECT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_NONFATAL_FAILURE_) +#define GTEST_EXPECT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_NONFATAL_FAILURE_) +#define GTEST_ASSERT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_FATAL_FAILURE_) +#define GTEST_ASSERT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_FATAL_FAILURE_) + +// Define these macros to 1 to omit the definition of the corresponding +// EXPECT or ASSERT, which clashes with some users' own code. + +#if !GTEST_DONT_DEFINE_EXPECT_TRUE +#define EXPECT_TRUE(condition) GTEST_EXPECT_TRUE(condition) +#endif + +#if !GTEST_DONT_DEFINE_EXPECT_FALSE +#define EXPECT_FALSE(condition) GTEST_EXPECT_FALSE(condition) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_TRUE +#define ASSERT_TRUE(condition) GTEST_ASSERT_TRUE(condition) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_FALSE +#define ASSERT_FALSE(condition) GTEST_ASSERT_FALSE(condition) +#endif + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(v1, v2): Tests that v1 == v2 +// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 +// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 +// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 +// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 +// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 +// +// When they are not, Google Test prints both the tested expressions and +// their actual values. The values must be compatible built-in types, +// or you will get a compiler error. By "compatible" we mean that the +// values can be compared by the respective operator. +// +// Note: +// +// 1. It is possible to make a user-defined type work with +// {ASSERT|EXPECT}_??(), but that requires overloading the +// comparison operators and is thus discouraged by the Google C++ +// Usage Guide. Therefore, you are advised to use the +// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are +// equal. +// +// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on +// pointers (in particular, C strings). Therefore, if you use it +// with two C strings, you are testing how their locations in memory +// are related, not how their content is related. To compare two C +// strings by content, use {ASSERT|EXPECT}_STR*(). +// +// 3. {ASSERT|EXPECT}_EQ(v1, v2) is preferred to +// {ASSERT|EXPECT}_TRUE(v1 == v2), as the former tells you +// what the actual value is when it fails, and similarly for the +// other comparisons. +// +// 4. Do not depend on the order in which {ASSERT|EXPECT}_??() +// evaluate their arguments, which is undefined. +// +// 5. These macros evaluate their arguments exactly once. +// +// Examples: +// +// EXPECT_NE(Foo(), 5); +// EXPECT_EQ(a_pointer, NULL); +// ASSERT_LT(i, array_size); +// ASSERT_GT(records.size(), 0) << "There is no record left."; + +#define EXPECT_EQ(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) +#define EXPECT_NE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define EXPECT_LE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define EXPECT_LT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define EXPECT_GE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define EXPECT_GT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +#define GTEST_ASSERT_EQ(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) +#define GTEST_ASSERT_NE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define GTEST_ASSERT_LE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define GTEST_ASSERT_LT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define GTEST_ASSERT_GE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define GTEST_ASSERT_GT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of +// ASSERT_XY(), which clashes with some users' own code. + +#if !GTEST_DONT_DEFINE_ASSERT_EQ +# define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_NE +# define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LE +# define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LT +# define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GE +# define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GT +# define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) +#endif + +// C-string Comparisons. All tests treat NULL and any non-NULL string +// as different. Two NULLs are equal. +// +// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 +// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 +// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case +// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case +// +// For wide or narrow string objects, you can use the +// {ASSERT|EXPECT}_??() macros. +// +// Don't depend on the order in which the arguments are evaluated, +// which is undefined. +// +// These macros evaluate their arguments exactly once. + +#define EXPECT_STREQ(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2) +#define EXPECT_STRNE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define EXPECT_STRCASEEQ(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2) +#define EXPECT_STRCASENE(s1, s2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +#define ASSERT_STREQ(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2) +#define ASSERT_STRNE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define ASSERT_STRCASEEQ(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2) +#define ASSERT_STRCASENE(s1, s2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(val1, val2): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(val1, val2): +// Tests that two double values are almost equal. +// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): +// Tests that v1 and v2 are within the given distance to each other. +// +// Google Test uses ULP-based comparison to automatically pick a default +// error bound that is appropriate for the operands. See the +// FloatingPoint template class in gtest-internal.h if you are +// interested in the implementation details. + +#define EXPECT_FLOAT_EQ(val1, val2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<float>, \ + val1, val2) + +#define EXPECT_DOUBLE_EQ(val1, val2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<double>, \ + val1, val2) + +#define ASSERT_FLOAT_EQ(val1, val2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<float>, \ + val1, val2) + +#define ASSERT_DOUBLE_EQ(val1, val2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<double>, \ + val1, val2) + +#define EXPECT_NEAR(val1, val2, abs_error)\ + EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +#define ASSERT_NEAR(val1, val2, abs_error)\ + ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +// These predicate format functions work on floating-point values, and +// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. +// +// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2); +GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2); + + +#if GTEST_OS_WINDOWS + +// Macros that test for HRESULT failure and success, these are only useful +// on Windows, and rely on Windows SDK macros and APIs to compile. +// +// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) +// +// When expr unexpectedly fails or succeeds, Google Test prints the +// expected result and the actual result with both a human-readable +// string representation of the error, if available, as well as the +// hex result code. +# define EXPECT_HRESULT_SUCCEEDED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define ASSERT_HRESULT_SUCCEEDED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define EXPECT_HRESULT_FAILED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +# define ASSERT_HRESULT_FAILED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#endif // GTEST_OS_WINDOWS + +// Macros that execute statement and check that it doesn't generate new fatal +// failures in the current thread. +// +// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement); +// +// Examples: +// +// EXPECT_NO_FATAL_FAILURE(Process()); +// ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed"; +// +#define ASSERT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_) +#define EXPECT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_) + +// Causes a trace (including the given source file path and line number, +// and the given message) to be included in every test failure message generated +// by code in the scope of the lifetime of an instance of this class. The effect +// is undone with the destruction of the instance. +// +// The message argument can be anything streamable to std::ostream. +// +// Example: +// testing::ScopedTrace trace("file.cc", 123, "message"); +// +class GTEST_API_ ScopedTrace { + public: + // The c'tor pushes the given source file location and message onto + // a trace stack maintained by Google Test. + + // Template version. Uses Message() to convert the values into strings. + // Slow, but flexible. + template <typename T> + ScopedTrace(const char* file, int line, const T& message) { + PushTrace(file, line, (Message() << message).GetString()); + } + + // Optimize for some known types. + ScopedTrace(const char* file, int line, const char* message) { + PushTrace(file, line, message ? message : "(null)"); + } + + ScopedTrace(const char* file, int line, const std::string& message) { + PushTrace(file, line, message); + } + + // The d'tor pops the info pushed by the c'tor. + // + // Note that the d'tor is not virtual in order to be efficient. + // Don't inherit from ScopedTrace! + ~ScopedTrace(); + + private: + void PushTrace(const char* file, int line, std::string message); + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace); +} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its + // c'tor and d'tor. Therefore it doesn't + // need to be used otherwise. + +// Causes a trace (including the source file path, the current line +// number, and the given message) to be included in every test failure +// message generated by code in the current scope. The effect is +// undone when the control leaves the current scope. +// +// The message argument can be anything streamable to std::ostream. +// +// In the implementation, we include the current line number as part +// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s +// to appear in the same block - as long as they are on different +// lines. +// +// Assuming that each thread maintains its own stack of traces. +// Therefore, a SCOPED_TRACE() would (correctly) only affect the +// assertions in its own thread. +#define SCOPED_TRACE(message) \ + ::testing::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\ + __FILE__, __LINE__, (message)) + +// Compile-time assertion for type equality. +// StaticAssertTypeEq<type1, type2>() compiles if and only if type1 and type2 +// are the same type. The value it returns is not interesting. +// +// Instead of making StaticAssertTypeEq a class template, we make it a +// function template that invokes a helper class template. This +// prevents a user from misusing StaticAssertTypeEq<T1, T2> by +// defining objects of that type. +// +// CAVEAT: +// +// When used inside a method of a class template, +// StaticAssertTypeEq<T1, T2>() is effective ONLY IF the method is +// instantiated. For example, given: +// +// template <typename T> class Foo { +// public: +// void Bar() { testing::StaticAssertTypeEq<int, T>(); } +// }; +// +// the code: +// +// void Test1() { Foo<bool> foo; } +// +// will NOT generate a compiler error, as Foo<bool>::Bar() is never +// actually instantiated. Instead, you need: +// +// void Test2() { Foo<bool> foo; foo.Bar(); } +// +// to cause a compiler error. +template <typename T1, typename T2> +constexpr bool StaticAssertTypeEq() noexcept { + static_assert(std::is_same<T1, T2>::value, "T1 and T2 are not the same type"); + return true; +} + +// Defines a test. +// +// The first parameter is the name of the test suite, and the second +// parameter is the name of the test within the test suite. +// +// The convention is to end the test suite name with "Test". For +// example, a test suite for the Foo class can be named FooTest. +// +// Test code should appear between braces after an invocation of +// this macro. Example: +// +// TEST(FooTest, InitializesCorrectly) { +// Foo foo; +// EXPECT_TRUE(foo.StatusIsOK()); +// } + +// Note that we call GetTestTypeId() instead of GetTypeId< +// ::testing::Test>() here to get the type ID of testing::Test. This +// is to work around a suspected linker bug when using Google Test as +// a framework on Mac OS X. The bug causes GetTypeId< +// ::testing::Test>() to return different values depending on whether +// the call is from the Google Test framework itself or from user test +// code. GetTestTypeId() is guaranteed to always return the same +// value, as it always calls GetTypeId<>() from the Google Test +// framework. +#define GTEST_TEST(test_suite_name, test_name) \ + GTEST_TEST_(test_suite_name, test_name, ::testing::Test, \ + ::testing::internal::GetTestTypeId()) + +// Define this macro to 1 to omit the definition of TEST(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_TEST +#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name) +#endif + +// Defines a test that uses a test fixture. +// +// The first parameter is the name of the test fixture class, which +// also doubles as the test suite name. The second parameter is the +// name of the test within the test suite. +// +// A test fixture class must be declared earlier. The user should put +// the test code between braces after using this macro. Example: +// +// class FooTest : public testing::Test { +// protected: +// void SetUp() override { b_.AddElement(3); } +// +// Foo a_; +// Foo b_; +// }; +// +// TEST_F(FooTest, InitializesCorrectly) { +// EXPECT_TRUE(a_.StatusIsOK()); +// } +// +// TEST_F(FooTest, ReturnsElementCountCorrectly) { +// EXPECT_EQ(a_.size(), 0); +// EXPECT_EQ(b_.size(), 1); +// } +// +// GOOGLETEST_CM0011 DO NOT DELETE +#if !GTEST_DONT_DEFINE_TEST +#define TEST_F(test_fixture, test_name)\ + GTEST_TEST_(test_fixture, test_name, test_fixture, \ + ::testing::internal::GetTypeId<test_fixture>()) +#endif // !GTEST_DONT_DEFINE_TEST + +// Returns a path to temporary directory. +// Tries to determine an appropriate directory for the platform. +GTEST_API_ std::string TempDir(); + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +// Dynamically registers a test with the framework. +// +// This is an advanced API only to be used when the `TEST` macros are +// insufficient. The macros should be preferred when possible, as they avoid +// most of the complexity of calling this function. +// +// The `factory` argument is a factory callable (move-constructible) object or +// function pointer that creates a new instance of the Test object. It +// handles ownership to the caller. The signature of the callable is +// `Fixture*()`, where `Fixture` is the test fixture class for the test. All +// tests registered with the same `test_suite_name` must return the same +// fixture type. This is checked at runtime. +// +// The framework will infer the fixture class from the factory and will call +// the `SetUpTestSuite` and `TearDownTestSuite` for it. +// +// Must be called before `RUN_ALL_TESTS()` is invoked, otherwise behavior is +// undefined. +// +// Use case example: +// +// class MyFixture : public ::testing::Test { +// public: +// // All of these optional, just like in regular macro usage. +// static void SetUpTestSuite() { ... } +// static void TearDownTestSuite() { ... } +// void SetUp() override { ... } +// void TearDown() override { ... } +// }; +// +// class MyTest : public MyFixture { +// public: +// explicit MyTest(int data) : data_(data) {} +// void TestBody() override { ... } +// +// private: +// int data_; +// }; +// +// void RegisterMyTests(const std::vector<int>& values) { +// for (int v : values) { +// ::testing::RegisterTest( +// "MyFixture", ("Test" + std::to_string(v)).c_str(), nullptr, +// std::to_string(v).c_str(), +// __FILE__, __LINE__, +// // Important to use the fixture type as the return type here. +// [=]() -> MyFixture* { return new MyTest(v); }); +// } +// } +// ... +// int main(int argc, char** argv) { +// std::vector<int> values_to_test = LoadValuesFromConfig(); +// RegisterMyTests(values_to_test); +// ... +// return RUN_ALL_TESTS(); +// } +// +template <int&... ExplicitParameterBarrier, typename Factory> +TestInfo* RegisterTest(const char* test_suite_name, const char* test_name, + const char* type_param, const char* value_param, + const char* file, int line, Factory factory) { + using TestT = typename std::remove_pointer<decltype(factory())>::type; + + class FactoryImpl : public internal::TestFactoryBase { + public: + explicit FactoryImpl(Factory f) : factory_(std::move(f)) {} + Test* CreateTest() override { return factory_(); } + + private: + Factory factory_; + }; + + return internal::MakeAndRegisterTestInfo( + test_suite_name, test_name, type_param, value_param, + internal::CodeLocation(file, line), internal::GetTypeId<TestT>(), + internal::SuiteApiResolver<TestT>::GetSetUpCaseOrSuite(file, line), + internal::SuiteApiResolver<TestT>::GetTearDownCaseOrSuite(file, line), + new FactoryImpl{std::move(factory)}); +} + +} // namespace testing + +// Use this function in main() to run all tests. It returns 0 if all +// tests are successful, or 1 otherwise. +// +// RUN_ALL_TESTS() should be invoked after the command line has been +// parsed by InitGoogleTest(). +// +// This function was formerly a macro; thus, it is in the global +// namespace and has an all-caps name. +int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_; + +inline int RUN_ALL_TESTS() { + return ::testing::UnitTest::GetInstance()->Run(); +} + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_H_ diff --git a/src/fmt/test/header-only-test.cc b/src/fmt/test/header-only-test.cc new file mode 100644 index 000000000..570f09a56 --- /dev/null +++ b/src/fmt/test/header-only-test.cc @@ -0,0 +1,11 @@ +// Header-only configuration test + +#include "fmt/core.h" +#include "fmt/ostream.h" +#include "gtest/gtest.h" + +#ifndef FMT_HEADER_ONLY +# error "Not in the header-only mode." +#endif + +TEST(header_only_test, format) { EXPECT_EQ(fmt::format("foo"), "foo"); } diff --git a/src/fmt/test/mock-allocator.h b/src/fmt/test/mock-allocator.h new file mode 100644 index 000000000..6a67e08f4 --- /dev/null +++ b/src/fmt/test/mock-allocator.h @@ -0,0 +1,64 @@ +// Formatting library for C++ - mock allocator +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_MOCK_ALLOCATOR_H_ +#define FMT_MOCK_ALLOCATOR_H_ + +#include <assert.h> // assert +#include <stddef.h> // size_t + +#include <memory> // std::allocator_traits + +#include "gmock/gmock.h" + +template <typename T> class mock_allocator { + public: + mock_allocator() {} + mock_allocator(const mock_allocator&) {} + using value_type = T; + MOCK_METHOD1_T(allocate, T*(size_t n)); + MOCK_METHOD2_T(deallocate, void(T* p, size_t n)); +}; + +template <typename Allocator> class allocator_ref { + private: + Allocator* alloc_; + + void move(allocator_ref& other) { + alloc_ = other.alloc_; + other.alloc_ = nullptr; + } + + public: + using value_type = typename Allocator::value_type; + + explicit allocator_ref(Allocator* alloc = nullptr) : alloc_(alloc) {} + + allocator_ref(const allocator_ref& other) : alloc_(other.alloc_) {} + allocator_ref(allocator_ref&& other) { move(other); } + + allocator_ref& operator=(allocator_ref&& other) { + assert(this != &other); + move(other); + return *this; + } + + allocator_ref& operator=(const allocator_ref& other) { + alloc_ = other.alloc_; + return *this; + } + + public: + Allocator* get() const { return alloc_; } + + value_type* allocate(size_t n) { + return std::allocator_traits<Allocator>::allocate(*alloc_, n); + } + void deallocate(value_type* p, size_t n) { alloc_->deallocate(p, n); } +}; + +#endif // FMT_MOCK_ALLOCATOR_H_ diff --git a/src/fmt/test/module-test.cc b/src/fmt/test/module-test.cc new file mode 100644 index 000000000..62e5fe8ba --- /dev/null +++ b/src/fmt/test/module-test.cc @@ -0,0 +1,572 @@ +// Formatting library for C++ - module tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. +// +// Copyright (c) 2021 - present, Daniela Engert +// All Rights Reserved +// {fmt} module. + +#ifdef _MSC_FULL_VER +// hide some implementation bugs in msvc +// that are not essential to users of the module. +# define FMT_HIDE_MODULE_BUGS +#endif + +#define FMT_MODULE_TEST + +#include <bit> +#include <chrono> +#include <exception> +#include <iterator> +#include <locale> +#include <memory> +#include <ostream> +#include <string> +#include <string_view> +#include <system_error> + +#if (__has_include(<fcntl.h>) || defined(__APPLE__) || \ + defined(__linux__)) && \ + (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) +# include <fcntl.h> +# define FMT_USE_FCNTL 1 +#else +# define FMT_USE_FCNTL 0 +#endif +#if defined(_WIN32) && !defined(__MINGW32__) +# define FMT_POSIX(call) _##call +#else +# define FMT_POSIX(call) call +#endif +#define FMT_OS_H_ // don't pull in os.h directly or indirectly + +import fmt; + +// check for macros leaking from BMI +static bool macro_leaked = +#if defined(FMT_CORE_H_) || defined(FMT_FORMAT_H_) + true; +#else + false; +#endif + +// Include sources to pick up functions and classes from the module rather than +// from the non-modular library which is baked into the 'test-main' library. +// This averts linker problems: +// - strong ownership model: missing linker symbols +// - weak ownership model: duplicate linker symbols +#include "gtest-extra.cc" +#include "util.cc" + +// an implicitly exported namespace must be visible [module.interface]/2.2 +TEST(module_test, namespace) { + using namespace fmt; + using namespace fmt::literals; + ASSERT_TRUE(true); +} + +namespace detail { +bool oops_detail_namespace_is_visible; +} + +namespace fmt { +bool namespace_detail_invisible() { +#if defined(FMT_HIDE_MODULE_BUGS) && defined(_MSC_FULL_VER) && \ + ((_MSC_VER == 1929 && _MSC_FULL_VER <= 192930136) || \ + (_MSC_VER == 1930 && _MSC_FULL_VER <= 193030704)) + // bug in msvc up to 16.11.5 / 17.0-pre5: + + // the namespace is visible even when it is neither + // implicitly nor explicitly exported + return true; +#else + using namespace detail; + // this fails to compile if fmt::detail is visible + return !oops_detail_namespace_is_visible; +#endif +} +} // namespace fmt + +// the non-exported namespace 'detail' must be invisible [module.interface]/2 +TEST(module_test, detail_namespace) { + EXPECT_TRUE(fmt::namespace_detail_invisible()); +} + +// macros must not be imported from a *named* module [cpp.import]/5.1 +TEST(module_test, macros) { +#if defined(FMT_HIDE_MODULE_BUGS) && defined(_MSC_FULL_VER) && \ + _MSC_FULL_VER <= 192930130 + // bug in msvc up to 16.11-pre2: + // include-guard macros leak from BMI + // and even worse: they cannot be #undef-ined + macro_leaked = false; +#endif + EXPECT_FALSE(macro_leaked); +} + +// The following is less about functional testing (that's done elsewhere) +// but rather visibility of all client-facing overloads, reachability of +// non-exported entities, name lookup and overload resolution within +// template instantitions. +// Excercise all exported entities of the API at least once. +// Instantiate as many code paths as possible. + +TEST(module_test, to_string) { + EXPECT_EQ("42", fmt::to_string(42)); + EXPECT_EQ("42", fmt::to_string(42.0)); + + EXPECT_EQ(L"42", fmt::to_wstring(42)); + EXPECT_EQ(L"42", fmt::to_wstring(42.0)); +} + +TEST(module_test, format) { + EXPECT_EQ("42", fmt::format("{:}", 42)); + EXPECT_EQ("-42", fmt::format("{0}", -42.0)); + + EXPECT_EQ(L"42", fmt::format(L"{:}", 42)); + EXPECT_EQ(L"-42", fmt::format(L"{0}", -42.0)); +} + +TEST(module_test, format_to) { + std::string s; + fmt::format_to(std::back_inserter(s), "{}", 42); + EXPECT_EQ("42", s); + + char buffer[4] = {0}; + fmt::format_to(buffer, "{}", 42); + EXPECT_EQ("42", std::string_view(buffer)); + + fmt::memory_buffer mb; + fmt::format_to(mb, "{}", 42); + EXPECT_EQ("42", std::string_view(buffer)); + + std::wstring w; + fmt::format_to(std::back_inserter(w), L"{}", 42); + EXPECT_EQ(L"42", w); + + wchar_t wbuffer[4] = {0}; + fmt::format_to(wbuffer, L"{}", 42); + EXPECT_EQ(L"42", std::wstring_view(wbuffer)); + + fmt::wmemory_buffer wb; + fmt::format_to(wb, L"{}", 42); + EXPECT_EQ(L"42", std::wstring_view(wbuffer)); +} + +TEST(module_test, formatted_size) { + EXPECT_EQ(2u, fmt::formatted_size("{}", 42)); + EXPECT_EQ(2u, fmt::formatted_size(L"{}", 42)); +} + +TEST(module_test, format_to_n) { + std::string s; + auto result = fmt::format_to_n(std::back_inserter(s), 1, "{}", 42); + EXPECT_EQ(2u, result.size); + char buffer[4] = {0}; + fmt::format_to_n(buffer, 3, "{}", 12345); + + std::wstring w; + auto wresult = fmt::format_to_n(std::back_inserter(w), 1, L"{}", 42); + EXPECT_EQ(2u, wresult.size); + wchar_t wbuffer[4] = {0}; + fmt::format_to_n(wbuffer, 3, L"{}", 12345); +} + +TEST(module_test, format_args) { + auto no_args = fmt::format_args(); + EXPECT_FALSE(no_args.get(1)); + + fmt::basic_format_args args = fmt::make_format_args(42); + EXPECT_TRUE(args.max_size() > 0); + auto arg0 = args.get(0); + EXPECT_TRUE(arg0); + decltype(arg0) arg_none; + EXPECT_FALSE(arg_none); + EXPECT_TRUE(arg0.type() != arg_none.type()); +} + +TEST(module_test, wformat_args) { + auto no_args = fmt::wformat_args(); + EXPECT_FALSE(no_args.get(1)); + fmt::basic_format_args args = fmt::make_wformat_args(42); + EXPECT_TRUE(args.get(0)); +} + +TEST(module_test, dynamic_format_args) { + fmt::dynamic_format_arg_store<fmt::format_context> dyn_store; + dyn_store.push_back(fmt::arg("a42", 42)); + fmt::basic_format_args args = dyn_store; + EXPECT_FALSE(args.get(3)); + EXPECT_TRUE(args.get(fmt::string_view("a42"))); + + fmt::dynamic_format_arg_store<fmt::wformat_context> wdyn_store; + wdyn_store.push_back(fmt::arg(L"a42", 42)); + fmt::basic_format_args wargs = wdyn_store; + EXPECT_FALSE(wargs.get(3)); + EXPECT_TRUE(wargs.get(fmt::wstring_view(L"a42"))); +} + +TEST(module_test, vformat) { + EXPECT_EQ("42", fmt::vformat("{}", fmt::make_format_args(42))); + EXPECT_EQ(L"42", fmt::vformat(fmt::to_string_view(L"{}"), + fmt::make_wformat_args(42))); +} + +TEST(module_test, vformat_to) { + auto store = fmt::make_format_args(42); + std::string s; + fmt::vformat_to(std::back_inserter(s), "{}", store); + EXPECT_EQ("42", s); + + char buffer[4] = {0}; + fmt::vformat_to(buffer, "{:}", store); + EXPECT_EQ("42", std::string_view(buffer)); + + auto wstore = fmt::make_wformat_args(42); + std::wstring w; + fmt::vformat_to(std::back_inserter(w), L"{}", wstore); + EXPECT_EQ(L"42", w); + + wchar_t wbuffer[4] = {0}; + fmt::vformat_to(wbuffer, L"{:}", wstore); + EXPECT_EQ(L"42", std::wstring_view(wbuffer)); +} + +TEST(module_test, vformat_to_n) { + auto store = fmt::make_format_args(12345); + std::string s; + auto result = fmt::vformat_to_n(std::back_inserter(s), 1, "{}", store); + char buffer[4] = {0}; + fmt::vformat_to_n(buffer, 3, "{:}", store); + + auto wstore = fmt::make_wformat_args(12345); + std::wstring w; + auto wresult = fmt::vformat_to_n(std::back_inserter(w), 1, + fmt::to_string_view(L"{}"), wstore); + wchar_t wbuffer[4] = {0}; + fmt::vformat_to_n(wbuffer, 3, fmt::to_string_view(L"{:}"), wstore); +} + +std::string as_string(std::wstring_view text) { + return {reinterpret_cast<const char*>(text.data()), + text.size() * sizeof(text[0])}; +} + +TEST(module_test, print) { + EXPECT_WRITE(stdout, fmt::print("{}µ", 42), "42µ"); + EXPECT_WRITE(stderr, fmt::print(stderr, "{}µ", 4.2), "4.2µ"); + if (false) { + EXPECT_WRITE(stdout, fmt::print(L"{}µ", 42), as_string(L"42µ")); + EXPECT_WRITE(stderr, fmt::print(stderr, L"{}µ", 4.2), as_string(L"4.2µ")); + } +} + +TEST(module_test, vprint) { + EXPECT_WRITE(stdout, fmt::vprint("{:}µ", fmt::make_format_args(42)), "42µ"); + EXPECT_WRITE(stderr, fmt::vprint(stderr, "{}", fmt::make_format_args(4.2)), + "4.2"); + if (false) { + EXPECT_WRITE(stdout, fmt::vprint(L"{:}µ", fmt::make_wformat_args(42)), + as_string(L"42µ")); + EXPECT_WRITE(stderr, fmt::vprint(stderr, L"{}", fmt::make_wformat_args(42)), + as_string(L"42")); + } +} + +TEST(module_test, named_args) { + EXPECT_EQ("42", fmt::format("{answer}", fmt::arg("answer", 42))); + EXPECT_EQ(L"42", fmt::format(L"{answer}", fmt::arg(L"answer", 42))); +} + +TEST(module_test, literals) { + using namespace fmt::literals; + EXPECT_EQ("42", fmt::format("{answer}", "answer"_a = 42)); + EXPECT_EQ("42", "{}"_format(42)); + EXPECT_EQ(L"42", fmt::format(L"{answer}", L"answer"_a = 42)); + EXPECT_EQ(L"42", L"{}"_format(42)); +} + +TEST(module_test, locale) { + auto store = fmt::make_format_args(4.2); + const auto classic = std::locale::classic(); + EXPECT_EQ("4.2", fmt::format(classic, "{:L}", 4.2)); + EXPECT_EQ("4.2", fmt::vformat(classic, "{:L}", store)); + std::string s; + fmt::vformat_to(std::back_inserter(s), classic, "{:L}", store); + EXPECT_EQ("4.2", s); + EXPECT_EQ("4.2", fmt::format("{:L}", 4.2)); + + auto wstore = fmt::make_wformat_args(4.2); + EXPECT_EQ(L"4.2", fmt::format(classic, L"{:L}", 4.2)); + EXPECT_EQ(L"4.2", fmt::vformat(classic, L"{:L}", wstore)); + std::wstring w; + fmt::vformat_to(std::back_inserter(w), classic, L"{:L}", wstore); + EXPECT_EQ(L"4.2", w); + EXPECT_EQ(L"4.2", fmt::format(L"{:L}", 4.2)); +} + +TEST(module_test, string_view) { + fmt::string_view nsv("fmt"); + EXPECT_EQ("fmt", nsv); + EXPECT_TRUE(fmt::string_view("fmt") == nsv); + + fmt::wstring_view wsv(L"fmt"); + EXPECT_EQ(L"fmt", wsv); + EXPECT_TRUE(fmt::wstring_view(L"fmt") == wsv); +} + +TEST(module_test, memory_buffer) { + fmt::basic_memory_buffer<char, fmt::inline_buffer_size> buffer; + fmt::format_to(buffer, "{}", "42"); + EXPECT_EQ("42", to_string(buffer)); + fmt::memory_buffer nbuffer(std::move(buffer)); + EXPECT_EQ("42", to_string(nbuffer)); + buffer = std::move(nbuffer); + EXPECT_EQ("42", to_string(buffer)); + nbuffer.clear(); + EXPECT_EQ(0u, to_string(nbuffer).size()); + + fmt::wmemory_buffer wbuffer; + EXPECT_EQ(0u, to_string(wbuffer).size()); +} + +TEST(module_test, is_char) { + EXPECT_TRUE(fmt::is_char<char>()); + EXPECT_TRUE(fmt::is_char<wchar_t>()); + EXPECT_TRUE(fmt::is_char<char8_t>()); + EXPECT_TRUE(fmt::is_char<char16_t>()); + EXPECT_TRUE(fmt::is_char<char32_t>()); + EXPECT_FALSE(fmt::is_char<signed char>()); +} + +TEST(module_test, ptr) { + uintptr_t answer = 42; + auto p = std::bit_cast<int*>(answer); + EXPECT_EQ("0x2a", fmt::to_string(fmt::ptr(p))); + std::unique_ptr<int> up(p); + EXPECT_EQ("0x2a", fmt::to_string(fmt::ptr(up))); + up.release(); + auto sp = std::make_shared<int>(0); + p = sp.get(); + EXPECT_EQ(fmt::to_string(fmt::ptr(p)), fmt::to_string(fmt::ptr(sp))); +} + +TEST(module_test, errors) { + auto store = fmt::make_format_args(42); + EXPECT_THROW(throw fmt::format_error("oops"), std::exception); + EXPECT_THROW(throw fmt::vsystem_error(0, "{}", store), std::system_error); + EXPECT_THROW(throw fmt::system_error(0, "{}", 42), std::system_error); + + fmt::memory_buffer buffer; + fmt::format_system_error(buffer, 0, "oops"); + auto oops = to_string(buffer); + EXPECT_TRUE(oops.size() > 0); + EXPECT_WRITE(stderr, fmt::report_system_error(0, "oops"), oops + '\n'); + +#ifdef _WIN32 + EXPECT_THROW(throw fmt::vwindows_error(0, "{}", store), std::system_error); + EXPECT_THROW(throw fmt::windows_error(0, "{}", 42), std::system_error); + output_redirect redirect(stderr); + fmt::report_windows_error(0, "oops"); + EXPECT_TRUE(redirect.restore_and_read().size() > 0); +#endif +} + +TEST(module_test, error_code) { + EXPECT_EQ("generic:42", + fmt::format("{0}", std::error_code(42, std::generic_category()))); + EXPECT_EQ("system:42", + fmt::format("{0}", std::error_code(42, fmt::system_category()))); + EXPECT_EQ(L"generic:42", + fmt::format(L"{0}", std::error_code(42, std::generic_category()))); +} + +TEST(module_test, format_int) { + fmt::format_int sanswer(42); + EXPECT_EQ("42", fmt::string_view(sanswer.data(), sanswer.size())); + fmt::format_int uanswer(42u); + EXPECT_EQ("42", fmt::string_view(uanswer.data(), uanswer.size())); +} + +struct test_formatter : fmt::formatter<char> { + bool check() { return true; } +}; + +struct test_dynamic_formatter : fmt::dynamic_formatter<> { + bool check() { return true; } +}; + +TEST(module_test, formatter) { + EXPECT_TRUE(test_formatter{}.check()); + EXPECT_TRUE(test_dynamic_formatter{}.check()); +} + +TEST(module_test, join) { + int arr[3] = {1, 2, 3}; + std::vector<double> vec{1.0, 2.0, 3.0}; + std::initializer_list<int> il{1, 2, 3}; + auto sep = fmt::to_string_view(", "); + EXPECT_EQ("1, 2, 3", to_string(fmt::join(arr + 0, arr + 3, sep))); + EXPECT_EQ("1, 2, 3", to_string(fmt::join(arr, sep))); + EXPECT_EQ("1, 2, 3", to_string(fmt::join(vec.begin(), vec.end(), sep))); + EXPECT_EQ("1, 2, 3", to_string(fmt::join(vec, sep))); + EXPECT_EQ("1, 2, 3", to_string(fmt::join(il, sep))); + + auto wsep = fmt::to_string_view(L", "); + EXPECT_EQ(L"1, 2, 3", fmt::format(L"{}", fmt::join(arr + 0, arr + 3, wsep))); + EXPECT_EQ(L"1, 2, 3", fmt::format(L"{}", fmt::join(arr, wsep))); + EXPECT_EQ(L"1, 2, 3", fmt::format(L"{}", fmt::join(il, wsep))); +} + +TEST(module_test, time) { + auto time_now = std::time(nullptr); + EXPECT_TRUE(fmt::localtime(time_now).tm_year > 120); + EXPECT_TRUE(fmt::gmtime(time_now).tm_year > 120); + auto chrono_now = std::chrono::system_clock::now(); + EXPECT_TRUE(fmt::localtime(chrono_now).tm_year > 120); + EXPECT_TRUE(fmt::gmtime(chrono_now).tm_year > 120); +} + +TEST(module_test, time_point) { + auto now = std::chrono::system_clock::now(); + std::string_view past("2021-05-20 10:30:15"); + EXPECT_TRUE(past < fmt::format("{:%Y-%m-%d %H:%M:%S}", now)); + std::wstring_view wpast(L"2021-05-20 10:30:15"); + EXPECT_TRUE(wpast < fmt::format(L"{:%Y-%m-%d %H:%M:%S}", now)); +} + +TEST(module_test, time_duration) { + using us = std::chrono::duration<double, std::micro>; + EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds{42})); + EXPECT_EQ("4.2µs", fmt::format("{:3.1}", us{4.234})); + EXPECT_EQ("4.2µs", fmt::format(std::locale::classic(), "{:L}", us{4.2})); + + EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds{42})); + EXPECT_EQ(L"4.2µs", fmt::format(L"{:3.1}", us{4.234})); + EXPECT_EQ(L"4.2µs", fmt::format(std::locale::classic(), L"{:L}", us{4.2})); +} + +TEST(module_test, weekday) { + EXPECT_EQ("Mon", fmt::format(std::locale::classic(), "{}", fmt::weekday(1))); +} + +TEST(module_test, to_string_view) { + using fmt::to_string_view; + fmt::string_view nsv{to_string_view("42")}; + EXPECT_EQ("42", nsv); + fmt::wstring_view wsv{to_string_view(L"42")}; + EXPECT_EQ(L"42", wsv); +} + +TEST(module_test, printf) { + EXPECT_WRITE(stdout, fmt::printf("%f", 42.123456), "42.123456"); + EXPECT_WRITE(stdout, fmt::printf("%d", 42), "42"); + if (false) { + EXPECT_WRITE(stdout, fmt::printf(L"%f", 42.123456), + as_string(L"42.123456")); + EXPECT_WRITE(stdout, fmt::printf(L"%d", 42), as_string(L"42")); + } +} + +TEST(module_test, fprintf) { + EXPECT_WRITE(stderr, fmt::fprintf(stderr, "%d", 42), "42"); + std::ostringstream os; + fmt::fprintf(os, "%s", "bla"); + EXPECT_EQ("bla", os.str()); + + EXPECT_WRITE(stderr, fmt::fprintf(stderr, L"%d", 42), as_string(L"42")); + std::wostringstream ws; + fmt::fprintf(ws, L"%s", L"bla"); + EXPECT_EQ(L"bla", ws.str()); +} + +TEST(module_test, sprintf) { + EXPECT_EQ("42", fmt::sprintf("%d", 42)); + EXPECT_EQ(L"42", fmt::sprintf(L"%d", 42)); +} + +TEST(module_test, vprintf) { + EXPECT_WRITE(stdout, fmt::vprintf("%d", fmt::make_printf_args(42)), "42"); + if (false) { + EXPECT_WRITE(stdout, fmt::vprintf(L"%d", fmt::make_wprintf_args(42)), + as_string(L"42")); + } +} + +TEST(module_test, vfprintf) { + auto args = fmt::make_printf_args(42); + EXPECT_WRITE(stderr, fmt::vfprintf(stderr, "%d", args), "42"); + std::ostringstream os; + fmt::vfprintf(os, "%d", args); + EXPECT_EQ("42", os.str()); + auto wargs = fmt::make_wprintf_args(42); + if (false) { + EXPECT_WRITE(stderr, fmt::vfprintf(stderr, L"%d", wargs), as_string(L"42")); + } + std::wostringstream ws; + fmt::vfprintf(ws, L"%d", wargs); + EXPECT_EQ(L"42", ws.str()); +} + +TEST(module_test, vsprintf) { + EXPECT_EQ("42", fmt::vsprintf("%d", fmt::make_printf_args(42))); + EXPECT_EQ(L"42", fmt::vsprintf(L"%d", fmt::make_wprintf_args(42))); +} + +TEST(module_test, color) { + auto fg_check = fg(fmt::rgb(255, 200, 30)); + auto bg_check = bg(fmt::color::dark_slate_gray) | fmt::emphasis::italic; + auto emphasis_check = fmt::emphasis::underline | fmt::emphasis::bold; + EXPECT_EQ("\x1B[30m42\x1B[0m", + fmt::format(fg(fmt::terminal_color::black), "{}", 42)); + EXPECT_EQ(L"\x1B[30m42\x1B[0m", + fmt::format(fg(fmt::terminal_color::black), L"{}", 42)); +} + +TEST(module_test, cstring_view) { + auto s = "fmt"; + EXPECT_EQ(s, fmt::cstring_view(s).c_str()); + auto w = L"fmt"; + EXPECT_EQ(w, fmt::wcstring_view(w).c_str()); +} + +TEST(module_test, buffered_file) { + EXPECT_TRUE(fmt::buffered_file{}.get() == nullptr); +} + +TEST(module_test, output_file) { + fmt::ostream out = fmt::output_file("module-test", fmt::buffer_size = 1); + out.close(); +} + +struct custom_context { + using char_type = char; + using parse_context_type = fmt::format_parse_context; +}; + +TEST(module_test, custom_context) { + fmt::basic_format_arg<custom_context> custom_arg; + EXPECT_TRUE(!custom_arg); +} + +struct disabled_formatter {}; + +TEST(module_test, has_formatter) { + EXPECT_FALSE( + (fmt::has_formatter<disabled_formatter, fmt::format_context>::value)); +} + +TEST(module_test, is_formattable) { + EXPECT_FALSE(fmt::is_formattable<disabled_formatter>::value); +} + +TEST(module_test, compile_format_string) { + using namespace fmt::literals; + EXPECT_EQ("42", fmt::format("{0:x}"_cf, 0x42)); + EXPECT_EQ(L"42", fmt::format(L"{:}"_cf, 42)); + EXPECT_EQ("4.2", fmt::format("{arg:3.1f}"_cf, "arg"_a = 4.2)); + EXPECT_EQ(L" 42", fmt::format(L"{arg:>3}"_cf, L"arg"_a = L"42")); +} diff --git a/src/fmt/test/noexception-test.cc b/src/fmt/test/noexception-test.cc new file mode 100644 index 000000000..bf3472fd1 --- /dev/null +++ b/src/fmt/test/noexception-test.cc @@ -0,0 +1,18 @@ +// Formatting library for C++ - Noexception tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/args.h" +#include "fmt/chrono.h" +#include "fmt/color.h" +#include "fmt/compile.h" +#include "fmt/core.h" +#include "fmt/format.h" +#include "fmt/os.h" +#include "fmt/ostream.h" +#include "fmt/printf.h" +#include "fmt/ranges.h" +#include "fmt/xchar.h" diff --git a/src/fmt/test/os-test.cc b/src/fmt/test/os-test.cc new file mode 100644 index 000000000..a1745c8c8 --- /dev/null +++ b/src/fmt/test/os-test.cc @@ -0,0 +1,547 @@ +// Formatting library for C++ - tests of the OS-specific functionality +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/os.h" + +#include <cstdlib> // std::exit +#include <cstring> +#include <memory> + +#include "gtest-extra.h" +#include "util.h" + +using fmt::buffered_file; +using testing::HasSubstr; +using wstring_view = fmt::basic_string_view<wchar_t>; + +#ifdef _WIN32 + +# include <windows.h> + +TEST(util_test, utf16_to_utf8) { + auto s = std::string("ёжик"); + fmt::detail::utf16_to_utf8 u(L"\x0451\x0436\x0438\x043A"); + EXPECT_EQ(s, u.str()); + EXPECT_EQ(s.size(), u.size()); +} + +TEST(util_test, utf16_to_utf8_empty_string) { + std::string s = ""; + fmt::detail::utf16_to_utf8 u(L""); + EXPECT_EQ(s, u.str()); + EXPECT_EQ(s.size(), u.size()); +} + +template <typename Converter, typename Char> +void check_utf_conversion_error(const char* message, + fmt::basic_string_view<Char> str = + fmt::basic_string_view<Char>(nullptr, 1)) { + fmt::memory_buffer out; + fmt::detail::format_windows_error(out, ERROR_INVALID_PARAMETER, message); + auto error = std::system_error(std::error_code()); + try { + (Converter)(str); + } catch (const std::system_error& e) { + error = e; + } + EXPECT_EQ(ERROR_INVALID_PARAMETER, error.code().value()); + EXPECT_THAT(error.what(), HasSubstr(fmt::to_string(out))); +} + +TEST(util_test, utf16_to_utf8_error) { + check_utf_conversion_error<fmt::detail::utf16_to_utf8, wchar_t>( + "cannot convert string from UTF-16 to UTF-8"); +} + +TEST(util_test, utf16_to_utf8_convert) { + fmt::detail::utf16_to_utf8 u; + EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(wstring_view(nullptr, 1))); + EXPECT_EQ(ERROR_INVALID_PARAMETER, + u.convert(wstring_view(L"foo", INT_MAX + 1u))); +} + +TEST(os_test, format_std_error_code) { + EXPECT_EQ("generic:42", + fmt::format(FMT_STRING("{0}"), + std::error_code(42, std::generic_category()))); + EXPECT_EQ("system:42", + fmt::format(FMT_STRING("{0}"), + std::error_code(42, fmt::system_category()))); + EXPECT_EQ("system:-42", + fmt::format(FMT_STRING("{0}"), + std::error_code(-42, fmt::system_category()))); +} + +TEST(os_test, format_windows_error) { + LPWSTR message = nullptr; + auto result = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, ERROR_FILE_EXISTS, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast<LPWSTR>(&message), 0, nullptr); + fmt::detail::utf16_to_utf8 utf8_message(wstring_view(message, result - 2)); + LocalFree(message); + fmt::memory_buffer actual_message; + fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS, "test"); + EXPECT_EQ(fmt::format("test: {}", utf8_message.str()), + fmt::to_string(actual_message)); + actual_message.resize(0); +} + +TEST(os_test, format_long_windows_error) { + LPWSTR message = nullptr; + // this error code is not available on all Windows platforms and + // Windows SDKs, so do not fail the test if the error string cannot + // be retrieved. + int provisioning_not_allowed = 0x80284013L; // TBS_E_PROVISIONING_NOT_ALLOWED + auto result = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, static_cast<DWORD>(provisioning_not_allowed), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast<LPWSTR>(&message), 0, nullptr); + if (result == 0) { + LocalFree(message); + return; + } + fmt::detail::utf16_to_utf8 utf8_message(wstring_view(message, result - 2)); + LocalFree(message); + fmt::memory_buffer actual_message; + fmt::detail::format_windows_error(actual_message, provisioning_not_allowed, + "test"); + EXPECT_EQ(fmt::format("test: {}", utf8_message.str()), + fmt::to_string(actual_message)); +} + +TEST(os_test, windows_error) { + auto error = std::system_error(std::error_code()); + try { + throw fmt::windows_error(ERROR_FILE_EXISTS, "test {}", "error"); + } catch (const std::system_error& e) { + error = e; + } + fmt::memory_buffer message; + fmt::detail::format_windows_error(message, ERROR_FILE_EXISTS, "test error"); + EXPECT_THAT(error.what(), HasSubstr(to_string(message))); + EXPECT_EQ(ERROR_FILE_EXISTS, error.code().value()); +} + +TEST(os_test, report_windows_error) { + fmt::memory_buffer out; + fmt::detail::format_windows_error(out, ERROR_FILE_EXISTS, "test error"); + out.push_back('\n'); + EXPECT_WRITE(stderr, + fmt::report_windows_error(ERROR_FILE_EXISTS, "test error"), + fmt::to_string(out)); +} + +#endif // _WIN32 + +#if FMT_USE_FCNTL + +using fmt::file; + +bool isclosed(int fd) { + char buffer; + auto result = std::streamsize(); + SUPPRESS_ASSERT(result = FMT_POSIX(read(fd, &buffer, 1))); + return result == -1 && errno == EBADF; +} + +// Opens a file for reading. +file open_file() { + file read_end, write_end; + file::pipe(read_end, write_end); + write_end.write(file_content, std::strlen(file_content)); + write_end.close(); + return read_end; +} + +// Attempts to write a string to a file. +void write(file& f, fmt::string_view s) { + size_t num_chars_left = s.size(); + const char* ptr = s.data(); + do { + size_t count = f.write(ptr, num_chars_left); + ptr += count; + // We can't write more than size_t bytes since num_chars_left + // has type size_t. + num_chars_left -= count; + } while (num_chars_left != 0); +} + +TEST(buffered_file_test, default_ctor) { + auto f = buffered_file(); + EXPECT_TRUE(f.get() == nullptr); +} + +TEST(buffered_file_test, move_ctor) { + buffered_file bf = open_buffered_file(); + FILE* fp = bf.get(); + EXPECT_TRUE(fp != nullptr); + buffered_file bf2(std::move(bf)); + EXPECT_EQ(fp, bf2.get()); + EXPECT_TRUE(bf.get() == nullptr); +} + +TEST(buffered_file_test, move_assignment) { + buffered_file bf = open_buffered_file(); + FILE* fp = bf.get(); + EXPECT_TRUE(fp != nullptr); + buffered_file bf2; + bf2 = std::move(bf); + EXPECT_EQ(fp, bf2.get()); + EXPECT_TRUE(bf.get() == nullptr); +} + +TEST(buffered_file_test, move_assignment_closes_file) { + buffered_file bf = open_buffered_file(); + buffered_file bf2 = open_buffered_file(); + int old_fd = bf2.descriptor(); + bf2 = std::move(bf); + EXPECT_TRUE(isclosed(old_fd)); +} + +TEST(buffered_file_test, move_from_temporary_in_ctor) { + FILE* fp = nullptr; + buffered_file f = open_buffered_file(&fp); + EXPECT_EQ(fp, f.get()); +} + +TEST(buffered_file_test, move_from_temporary_in_assignment) { + FILE* fp = nullptr; + auto f = buffered_file(); + f = open_buffered_file(&fp); + EXPECT_EQ(fp, f.get()); +} + +TEST(buffered_file_test, move_from_temporary_in_assignment_closes_file) { + buffered_file f = open_buffered_file(); + int old_fd = f.descriptor(); + f = open_buffered_file(); + EXPECT_TRUE(isclosed(old_fd)); +} + +TEST(buffered_file_test, close_file_in_dtor) { + int fd = 0; + { + buffered_file f = open_buffered_file(); + fd = f.descriptor(); + } + EXPECT_TRUE(isclosed(fd)); +} + +TEST(buffered_file_test, close_error_in_dtor) { + auto f = + std::unique_ptr<buffered_file>(new buffered_file(open_buffered_file())); + EXPECT_WRITE( + stderr, + { + // The close function must be called inside EXPECT_WRITE, + // otherwise the system may recycle closed file descriptor when + // redirecting the output in EXPECT_STDERR and the second close + // will break output redirection. + FMT_POSIX(close(f->descriptor())); + SUPPRESS_ASSERT(f.reset(nullptr)); + }, + system_error_message(EBADF, "cannot close file") + "\n"); +} + +TEST(buffered_file_test, close) { + buffered_file f = open_buffered_file(); + int fd = f.descriptor(); + f.close(); + EXPECT_TRUE(f.get() == nullptr); + EXPECT_TRUE(isclosed(fd)); +} + +TEST(buffered_file_test, close_error) { + buffered_file f = open_buffered_file(); + FMT_POSIX(close(f.descriptor())); + EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file"); + EXPECT_TRUE(f.get() == nullptr); +} + +TEST(buffered_file_test, descriptor) { + auto f = open_buffered_file(); + EXPECT_TRUE(f.descriptor() != -1); + file copy = file::dup(f.descriptor()); + EXPECT_READ(copy, file_content); +} + +TEST(ostream_test, move) { + fmt::ostream out = fmt::output_file("test-file"); + fmt::ostream moved(std::move(out)); + moved.print("hello"); +} + +TEST(ostream_test, move_while_holding_data) { + { + fmt::ostream out = fmt::output_file("test-file"); + out.print("Hello, "); + fmt::ostream moved(std::move(out)); + moved.print("world!\n"); + } + { + file in("test-file", file::RDONLY); + EXPECT_READ(in, "Hello, world!\n"); + } +} + +TEST(ostream_test, print) { + fmt::ostream out = fmt::output_file("test-file"); + out.print("The answer is {}.\n", + fmt::join(std::initializer_list<int>{42}, ", ")); + out.close(); + file in("test-file", file::RDONLY); + EXPECT_READ(in, "The answer is 42.\n"); +} + +TEST(ostream_test, buffer_boundary) { + auto str = std::string(4096, 'x'); + fmt::ostream out = fmt::output_file("test-file"); + out.print("{}", str); + out.print("{}", str); + out.close(); + file in("test-file", file::RDONLY); + EXPECT_READ(in, str + str); +} + +TEST(ostream_test, buffer_size) { + fmt::ostream out = fmt::output_file("test-file", fmt::buffer_size = 1); + out.print("{}", "foo"); + out.close(); + file in("test-file", file::RDONLY); + EXPECT_READ(in, "foo"); +} + +TEST(ostream_test, truncate) { + { + fmt::ostream out = fmt::output_file("test-file"); + out.print("0123456789"); + } + { + fmt::ostream out = fmt::output_file("test-file"); + out.print("foo"); + } + file in("test-file", file::RDONLY); + EXPECT_EQ("foo", read(in, 4)); +} + +TEST(ostream_test, flush) { + auto out = fmt::output_file("test-file"); + out.print("x"); + out.flush(); + auto in = fmt::file("test-file", file::RDONLY); + EXPECT_READ(in, "x"); +} + +TEST(file_test, default_ctor) { + file f; + EXPECT_EQ(-1, f.descriptor()); +} + +TEST(file_test, open_buffered_file_in_ctor) { + FILE* fp = safe_fopen("test-file", "w"); + std::fputs(file_content, fp); + std::fclose(fp); + file f("test-file", file::RDONLY); + // Check if the file is open by reading one character from it. + char buffer; + bool isopen = FMT_POSIX(read(f.descriptor(), &buffer, 1)) == 1; + ASSERT_TRUE(isopen); +} + +TEST(file_test, open_buffered_file_error) { + EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY), ENOENT, + "cannot open file nonexistent"); +} + +TEST(file_test, move_ctor) { + file f = open_file(); + int fd = f.descriptor(); + EXPECT_NE(-1, fd); + file f2(std::move(f)); + EXPECT_EQ(fd, f2.descriptor()); + EXPECT_EQ(-1, f.descriptor()); +} + +TEST(file_test, move_assignment) { + file f = open_file(); + int fd = f.descriptor(); + EXPECT_NE(-1, fd); + file f2; + f2 = std::move(f); + EXPECT_EQ(fd, f2.descriptor()); + EXPECT_EQ(-1, f.descriptor()); +} + +TEST(file_test, move_assignment_closes_file) { + file f = open_file(); + file f2 = open_file(); + int old_fd = f2.descriptor(); + f2 = std::move(f); + EXPECT_TRUE(isclosed(old_fd)); +} + +file open_buffered_file(int& fd) { + file f = open_file(); + fd = f.descriptor(); + return f; +} + +TEST(file_test, move_from_temporary_in_ctor) { + int fd = 0xdead; + file f(open_buffered_file(fd)); + EXPECT_EQ(fd, f.descriptor()); +} + +TEST(file_test, move_from_temporary_in_assignment) { + int fd = 0xdead; + file f; + f = open_buffered_file(fd); + EXPECT_EQ(fd, f.descriptor()); +} + +TEST(file_test, move_from_temporary_in_assignment_closes_file) { + int fd = 0xdead; + file f = open_file(); + int old_fd = f.descriptor(); + f = open_buffered_file(fd); + EXPECT_TRUE(isclosed(old_fd)); +} + +TEST(file_test, close_file_in_dtor) { + int fd = 0; + { + file f = open_file(); + fd = f.descriptor(); + } + EXPECT_TRUE(isclosed(fd)); +} + +TEST(file_test, close_error_in_dtor) { + std::unique_ptr<file> f(new file(open_file())); + EXPECT_WRITE( + stderr, + { + // The close function must be called inside EXPECT_WRITE, + // otherwise the system may recycle closed file descriptor when + // redirecting the output in EXPECT_STDERR and the second close + // will break output redirection. + FMT_POSIX(close(f->descriptor())); + SUPPRESS_ASSERT(f.reset(nullptr)); + }, + system_error_message(EBADF, "cannot close file") + "\n"); +} + +TEST(file_test, close) { + file f = open_file(); + int fd = f.descriptor(); + f.close(); + EXPECT_EQ(-1, f.descriptor()); + EXPECT_TRUE(isclosed(fd)); +} + +TEST(file_test, close_error) { + file f = open_file(); + FMT_POSIX(close(f.descriptor())); + EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file"); + EXPECT_EQ(-1, f.descriptor()); +} + +TEST(file_test, read) { + file f = open_file(); + EXPECT_READ(f, file_content); +} + +TEST(file_test, read_error) { + file f("test-file", file::WRONLY); + char buf; + // We intentionally read from a file opened in the write-only mode to + // cause error. + EXPECT_SYSTEM_ERROR(f.read(&buf, 1), EBADF, "cannot read from file"); +} + +TEST(file_test, write) { + file read_end, write_end; + file::pipe(read_end, write_end); + write(write_end, "test"); + write_end.close(); + EXPECT_READ(read_end, "test"); +} + +TEST(file_test, write_error) { + file f("test-file", file::RDONLY); + // We intentionally write to a file opened in the read-only mode to + // cause error. + EXPECT_SYSTEM_ERROR(f.write(" ", 1), EBADF, "cannot write to file"); +} + +TEST(file_test, dup) { + file f = open_file(); + file copy = file::dup(f.descriptor()); + EXPECT_NE(f.descriptor(), copy.descriptor()); + EXPECT_EQ(file_content, read(copy, std::strlen(file_content))); +} + +# ifndef __COVERITY__ +TEST(file_test, dup_error) { + int value = -1; + EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value), EBADF, + "cannot duplicate file descriptor -1"); +} +# endif + +TEST(file_test, dup2) { + file f = open_file(); + file copy = open_file(); + f.dup2(copy.descriptor()); + EXPECT_NE(f.descriptor(), copy.descriptor()); + EXPECT_READ(copy, file_content); +} + +TEST(file_test, dup2_error) { + file f = open_file(); + EXPECT_SYSTEM_ERROR_NOASSERT( + f.dup2(-1), EBADF, + fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor())); +} + +TEST(file_test, dup2_noexcept) { + file f = open_file(); + file copy = open_file(); + std::error_code ec; + f.dup2(copy.descriptor(), ec); + EXPECT_EQ(ec.value(), 0); + EXPECT_NE(f.descriptor(), copy.descriptor()); + EXPECT_READ(copy, file_content); +} + +TEST(file_test, dup2_noexcept_error) { + file f = open_file(); + std::error_code ec; + SUPPRESS_ASSERT(f.dup2(-1, ec)); + EXPECT_EQ(EBADF, ec.value()); +} + +TEST(file_test, pipe) { + file read_end, write_end; + file::pipe(read_end, write_end); + EXPECT_NE(-1, read_end.descriptor()); + EXPECT_NE(-1, write_end.descriptor()); + write(write_end, "test"); + EXPECT_READ(read_end, "test"); +} + +TEST(file_test, fdopen) { + file read_end, write_end; + file::pipe(read_end, write_end); + int read_fd = read_end.descriptor(); + EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get()))); +} +#endif // FMT_USE_FCNTL diff --git a/src/fmt/test/ostream-test.cc b/src/fmt/test/ostream-test.cc new file mode 100644 index 000000000..a3a51d532 --- /dev/null +++ b/src/fmt/test/ostream-test.cc @@ -0,0 +1,322 @@ +// Formatting library for C++ - std::ostream support tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include <fstream> + +#include "fmt/format.h" + +using fmt::runtime; + +struct test {}; + +// Test that there is no issues with specializations when fmt/ostream.h is +// included after fmt/format.h. +namespace fmt { +template <> struct formatter<test> : formatter<int> { + auto format(const test&, format_context& ctx) -> decltype(ctx.out()) { + return formatter<int>::format(42, ctx); + } +}; +} // namespace fmt + +#include <sstream> + +#include "fmt/compile.h" +#include "fmt/ostream.h" +#include "fmt/ranges.h" +#include "gmock/gmock.h" +#include "gtest-extra.h" +#include "util.h" + +auto operator<<(std::ostream& os, const date& d) -> std::ostream& { + os << d.year() << '-' << d.month() << '-' << d.day(); + return os; +} + +auto operator<<(std::wostream& os, const date& d) -> std::wostream& { + os << d.year() << L'-' << d.month() << L'-' << d.day(); + return os; +} + +// Make sure that overloaded comma operators do no harm to is_streamable. +struct type_with_comma_op {}; +template <typename T> void operator,(type_with_comma_op, const T&); +template <typename T> type_with_comma_op operator<<(T&, const date&); + +enum streamable_enum {}; + +auto operator<<(std::ostream& os, streamable_enum) -> std::ostream& { + return os << "streamable_enum"; +} + +enum unstreamable_enum {}; +auto format_as(unstreamable_enum e) -> int { return e; } + +struct empty_test {}; +auto operator<<(std::ostream& os, empty_test) -> std::ostream& { + return os << ""; +} + +namespace fmt { +template <> struct formatter<test_string> : ostream_formatter {}; +template <> struct formatter<date> : ostream_formatter {}; +template <> struct formatter<streamable_enum> : ostream_formatter {}; +template <> struct formatter<empty_test> : ostream_formatter {}; +} // namespace fmt + +TEST(ostream_test, enum) { + EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_enum())); + EXPECT_EQ("0", fmt::format("{}", unstreamable_enum())); +} + +TEST(ostream_test, format) { + EXPECT_EQ("a string", fmt::format("{0}", test_string("a string"))); + EXPECT_EQ("The date is 2012-12-9", + fmt::format("The date is {0}", date(2012, 12, 9))); +} + +TEST(ostream_test, format_specs) { + using fmt::format_error; + EXPECT_EQ("def ", fmt::format("{0:<5}", test_string("def"))); + EXPECT_EQ(" def", fmt::format("{0:>5}", test_string("def"))); + EXPECT_EQ(" def ", fmt::format("{0:^5}", test_string("def"))); + EXPECT_EQ("def**", fmt::format("{0:*<5}", test_string("def"))); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), test_string()), + format_error, "format specifier requires numeric argument"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), test_string()), + format_error, "format specifier requires numeric argument"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), test_string()), + format_error, "format specifier requires numeric argument"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), test_string()), + format_error, "format specifier requires numeric argument"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), test_string()), + format_error, "format specifier requires numeric argument"); + EXPECT_EQ("test ", fmt::format("{0:13}", test_string("test"))); + EXPECT_EQ("test ", fmt::format("{0:{1}}", test_string("test"), 13)); + EXPECT_EQ("te", fmt::format("{0:.2}", test_string("test"))); + EXPECT_EQ("te", fmt::format("{0:.{1}}", test_string("test"), 2)); +} + +TEST(ostream_test, empty_custom_output) { + EXPECT_EQ("", fmt::format("{}", empty_test())); +} + +TEST(ostream_test, print) { + std::ostringstream os; + fmt::print(os, "Don't {}!", "panic"); + EXPECT_EQ("Don't panic!", os.str()); +} + +TEST(ostream_test, write_to_ostream) { + std::ostringstream os; + fmt::memory_buffer buffer; + const char* foo = "foo"; + buffer.append(foo, foo + std::strlen(foo)); + fmt::detail::write_buffer(os, buffer); + EXPECT_EQ("foo", os.str()); +} + +TEST(ostream_test, write_to_ostream_max_size) { + auto max_size = fmt::detail::max_value<size_t>(); + auto max_streamsize = fmt::detail::max_value<std::streamsize>(); + if (max_size <= fmt::detail::to_unsigned(max_streamsize)) return; + + struct test_buffer final : fmt::detail::buffer<char> { + explicit test_buffer(size_t size) + : fmt::detail::buffer<char>(nullptr, size, size) {} + void grow(size_t) override {} + } buffer(max_size); + + struct mock_streambuf : std::streambuf { + MOCK_METHOD2(xsputn, std::streamsize(const void* s, std::streamsize n)); + auto xsputn(const char* s, std::streamsize n) -> std::streamsize override { + const void* v = s; + return xsputn(v, n); + } + } streambuf; + + struct test_ostream : std::ostream { + explicit test_ostream(mock_streambuf& output_buffer) + : std::ostream(&output_buffer) {} + } os(streambuf); + + testing::InSequence sequence; + const char* data = nullptr; + using ustreamsize = std::make_unsigned<std::streamsize>::type; + ustreamsize size = max_size; + do { + auto n = std::min(size, fmt::detail::to_unsigned(max_streamsize)); + EXPECT_CALL(streambuf, xsputn(data, static_cast<std::streamsize>(n))) + .WillOnce(testing::Return(max_streamsize)); + data += n; + size -= n; + } while (size != 0); + fmt::detail::write_buffer(os, buffer); +} + +TEST(ostream_test, join) { + int v[3] = {1, 2, 3}; + EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join(v, v + 3, ", "))); +} + +TEST(ostream_test, join_fallback_formatter) { + auto strs = std::vector<test_string>{test_string("foo"), test_string("bar")}; + EXPECT_EQ("foo, bar", fmt::format("{}", fmt::join(strs, ", "))); +} + +#if FMT_USE_CONSTEXPR +TEST(ostream_test, constexpr_string) { + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), std::string("42"))); + EXPECT_EQ("a string", + fmt::format(FMT_STRING("{0}"), test_string("a string"))); +} +#endif + +namespace fmt_test { +struct abc {}; + +template <typename Output> auto operator<<(Output& out, abc) -> Output& { + return out << "abc"; +} +} // namespace fmt_test + +template <typename T> struct test_template {}; + +template <typename T> +auto operator<<(std::ostream& os, test_template<T>) -> std::ostream& { + return os << 1; +} + +namespace fmt { +template <typename T> struct formatter<test_template<T>> : formatter<int> { + auto format(test_template<T>, format_context& ctx) -> decltype(ctx.out()) { + return formatter<int>::format(2, ctx); + } +}; + +template <> struct formatter<fmt_test::abc> : ostream_formatter {}; +} // namespace fmt + +TEST(ostream_test, template) { + EXPECT_EQ("2", fmt::format("{}", test_template<int>())); +} + +TEST(ostream_test, format_to_n) { + char buffer[4]; + buffer[3] = 'x'; + auto result = fmt::format_to_n(buffer, 3, "{}", fmt_test::abc()); + EXPECT_EQ(3u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ("abcx", fmt::string_view(buffer, 4)); + result = fmt::format_to_n(buffer, 3, "x{}y", fmt_test::abc()); + EXPECT_EQ(5u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ("xabx", fmt::string_view(buffer, 4)); +} + +template <typename T> struct convertible { + T value; + explicit convertible(const T& val) : value(val) {} + operator T() const { return value; } +}; + +TEST(ostream_test, disable_builtin_ostream_operators) { + EXPECT_EQ("42", fmt::format("{:d}", convertible<unsigned short>(42))); + EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo"))); +} + +struct streamable_and_convertible_to_bool { + operator bool() const { return true; } +}; + +std::ostream& operator<<(std::ostream& os, streamable_and_convertible_to_bool) { + return os << "foo"; +} + +TEST(ostream_test, format_convertible_to_bool) { + // operator<< is intentionally not used because of potential ODR violations. + EXPECT_EQ(fmt::format("{}", streamable_and_convertible_to_bool()), "true"); +} + +struct streamable_and_convertible_to_string_view { + operator fmt::string_view() const { return "foo"; } +}; + +std::ostream& operator<<(std::ostream& os, + streamable_and_convertible_to_string_view) { + return os << "bar"; +} + +TEST(ostream_test, format_convertible_to_string_vew) { + // operator<< is intentionally not used because of potential ODR violations. + EXPECT_EQ(fmt::format("{}", streamable_and_convertible_to_string_view()), + "foo"); +} + +struct copyfmt_test {}; + +std::ostream& operator<<(std::ostream& os, copyfmt_test) { + std::ios ios(nullptr); + ios.copyfmt(os); + return os << "foo"; +} + +namespace fmt { +template <> struct formatter<copyfmt_test> : ostream_formatter {}; +} // namespace fmt + +TEST(ostream_test, copyfmt) { + EXPECT_EQ("foo", fmt::format("{}", copyfmt_test())); +} + +TEST(ostream_test, to_string) { + EXPECT_EQ("abc", fmt::to_string(fmt_test::abc())); +} + +TEST(ostream_test, range) { + auto strs = std::vector<test_string>{test_string("foo"), test_string("bar")}; + EXPECT_EQ("[foo, bar]", fmt::format("{}", strs)); +} + +struct abstract { + virtual ~abstract() = default; + virtual void f() = 0; + friend auto operator<<(std::ostream& os, const abstract&) -> std::ostream& { + return os; + } +}; + +namespace fmt { +template <> struct formatter<abstract> : ostream_formatter {}; +} // namespace fmt + +void format_abstract_compiles(const abstract& a) { + fmt::format(FMT_COMPILE("{}"), a); +} + +TEST(ostream_test, is_formattable) { + EXPECT_TRUE(fmt::is_formattable<std::string>()); + EXPECT_TRUE(fmt::is_formattable<fmt::detail::std_string_view<char>>()); +} + +struct streamable_and_unformattable {}; + +auto operator<<(std::ostream& os, streamable_and_unformattable) + -> std::ostream& { + return os << "foo"; +} + +TEST(ostream_test, streamed) { + EXPECT_FALSE(fmt::is_formattable<streamable_and_unformattable>()); + EXPECT_EQ(fmt::format("{}", fmt::streamed(streamable_and_unformattable())), + "foo"); +} + +TEST(ostream_test, closed_ofstream) { + std::ofstream ofs; + fmt::print(ofs, "discard"); +} diff --git a/src/fmt/test/posix-mock-test.cc b/src/fmt/test/posix-mock-test.cc new file mode 100644 index 000000000..819a94dfb --- /dev/null +++ b/src/fmt/test/posix-mock-test.cc @@ -0,0 +1,459 @@ +// Tests of the C++ interface to POSIX functions that require mocks +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +// Disable bogus MSVC warnings. +#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) +# define _CRT_SECURE_NO_WARNINGS +#endif + +#include "posix-mock.h" + +#include <errno.h> +#include <fcntl.h> + +#include <climits> +#include <memory> + +#include "../src/os.cc" + +#ifdef _WIN32 +# include <io.h> +# undef max +#endif + +#include "gmock/gmock.h" +#include "gtest-extra.h" +#include "util.h" + +using fmt::buffered_file; + +using testing::_; +using testing::Return; +using testing::StrEq; + +template <typename Mock> struct scoped_mock : testing::StrictMock<Mock> { + scoped_mock() { Mock::instance = this; } + ~scoped_mock() { Mock::instance = nullptr; } +}; + +namespace { +int open_count; +int close_count; +int dup_count; +int dup2_count; +int fdopen_count; +int read_count; +int write_count; +int pipe_count; +int fopen_count; +int fclose_count; +int fileno_count; +size_t read_nbyte; +size_t write_nbyte; +bool sysconf_error; + +enum { none, max_size, error } fstat_sim; +} // namespace + +#define EMULATE_EINTR(func, error_result) \ + if (func##_count != 0) { \ + if (func##_count++ != 3) { \ + errno = EINTR; \ + return error_result; \ + } \ + } + +#ifndef _MSC_VER +int test::open(const char* path, int oflag, int mode) { + EMULATE_EINTR(open, -1); + return ::open(path, oflag, mode); +} +#else +errno_t test::sopen_s(int* pfh, const char* filename, int oflag, int shflag, + int pmode) { + EMULATE_EINTR(open, EINTR); + return _sopen_s(pfh, filename, oflag, shflag, pmode); +} +#endif + +#ifndef _WIN32 + +long test::sysconf(int name) { + long result = ::sysconf(name); + if (!sysconf_error) return result; + // Simulate an error. + errno = EINVAL; + return -1; +} + +static off_t max_file_size() { return std::numeric_limits<off_t>::max(); } + +int test::fstat(int fd, struct stat* buf) { + int result = ::fstat(fd, buf); + if (fstat_sim == max_size) buf->st_size = max_file_size(); + return result; +} + +#else + +static LONGLONG max_file_size() { return std::numeric_limits<LONGLONG>::max(); } + +DWORD test::GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) { + if (fstat_sim == error) { + SetLastError(ERROR_ACCESS_DENIED); + return INVALID_FILE_SIZE; + } + if (fstat_sim == max_size) { + DWORD max = std::numeric_limits<DWORD>::max(); + *lpFileSizeHigh = max >> 1; + return max; + } + return ::GetFileSize(hFile, lpFileSizeHigh); +} + +#endif + +int test::close(int fildes) { + // Close the file first because close shouldn't be retried. + int result = ::FMT_POSIX(close(fildes)); + EMULATE_EINTR(close, -1); + return result; +} + +int test::dup(int fildes) { + EMULATE_EINTR(dup, -1); + return ::FMT_POSIX(dup(fildes)); +} + +int test::dup2(int fildes, int fildes2) { + EMULATE_EINTR(dup2, -1); + return ::FMT_POSIX(dup2(fildes, fildes2)); +} + +FILE* test::fdopen(int fildes, const char* mode) { + EMULATE_EINTR(fdopen, nullptr); + return ::FMT_POSIX(fdopen(fildes, mode)); +} + +test::ssize_t test::read(int fildes, void* buf, test::size_t nbyte) { + read_nbyte = nbyte; + EMULATE_EINTR(read, -1); + return ::FMT_POSIX(read(fildes, buf, nbyte)); +} + +test::ssize_t test::write(int fildes, const void* buf, test::size_t nbyte) { + write_nbyte = nbyte; + EMULATE_EINTR(write, -1); + return ::FMT_POSIX(write(fildes, buf, nbyte)); +} + +#ifndef _WIN32 +int test::pipe(int fildes[2]) { + EMULATE_EINTR(pipe, -1); + return ::pipe(fildes); +} +#else +int test::pipe(int* pfds, unsigned psize, int textmode) { + EMULATE_EINTR(pipe, -1); + return _pipe(pfds, psize, textmode); +} +#endif + +FILE* test::fopen(const char* filename, const char* mode) { + EMULATE_EINTR(fopen, nullptr); + return ::fopen(filename, mode); +} + +int test::fclose(FILE* stream) { + EMULATE_EINTR(fclose, EOF); + return ::fclose(stream); +} + +int(test::fileno)(FILE* stream) { + EMULATE_EINTR(fileno, -1); +#ifdef fileno + return FMT_POSIX(fileno(stream)); +#else + return ::FMT_POSIX(fileno(stream)); +#endif +} + +#ifndef _WIN32 +# define EXPECT_RETRY(statement, func, message) \ + func##_count = 1; \ + statement; \ + EXPECT_EQ(4, func##_count); \ + func##_count = 0; +# define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual) +#else +# define EXPECT_RETRY(statement, func, message) \ + func##_count = 1; \ + EXPECT_SYSTEM_ERROR(statement, EINTR, message); \ + func##_count = 0; +# define EXPECT_EQ_POSIX(expected, actual) +#endif + +#if FMT_USE_FCNTL +void write_file(fmt::cstring_view filename, fmt::string_view content) { + fmt::buffered_file f(filename, "w"); + f.print("{}", content); +} + +using fmt::file; + +TEST(os_test, getpagesize) { +# ifdef _WIN32 + SYSTEM_INFO si = {}; + GetSystemInfo(&si); + EXPECT_EQ(si.dwPageSize, fmt::getpagesize()); +# else + EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize()); + sysconf_error = true; + EXPECT_SYSTEM_ERROR(fmt::getpagesize(), EINVAL, + "cannot get memory page size"); + sysconf_error = false; +# endif +} + +TEST(file_test, open_retry) { + write_file("temp", "there must be something here"); + std::unique_ptr<file> f{nullptr}; + EXPECT_RETRY(f.reset(new file("temp", file::RDONLY)), open, + "cannot open file temp"); +# ifndef _WIN32 + char c = 0; + f->read(&c, 1); +# endif +} + +TEST(file_test, close_no_retry_in_dtor) { + file read_end, write_end; + file::pipe(read_end, write_end); + std::unique_ptr<file> f(new file(std::move(read_end))); + int saved_close_count = 0; + EXPECT_WRITE( + stderr, + { + close_count = 1; + f.reset(nullptr); + saved_close_count = close_count; + close_count = 0; + }, + system_error_message(EINTR, "cannot close file") + "\n"); + EXPECT_EQ(2, saved_close_count); +} + +TEST(file_test, close_no_retry) { + file read_end, write_end; + file::pipe(read_end, write_end); + close_count = 1; + EXPECT_SYSTEM_ERROR(read_end.close(), EINTR, "cannot close file"); + EXPECT_EQ(2, close_count); + close_count = 0; +} + +TEST(file_test, size) { + std::string content = "top secret, destroy before reading"; + write_file("temp", content); + file f("temp", file::RDONLY); + EXPECT_GE(f.size(), 0); + EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size())); +# ifdef _WIN32 + auto error_code = std::error_code(); + fstat_sim = error; + try { + f.size(); + } catch (const std::system_error& e) { + error_code = e.code(); + } + fstat_sim = none; + EXPECT_EQ(error_code, + std::error_code(ERROR_ACCESS_DENIED, fmt::system_category())); +# else + f.close(); + EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes"); +# endif +} + +TEST(file_test, max_size) { + write_file("temp", ""); + file f("temp", file::RDONLY); + fstat_sim = max_size; + EXPECT_GE(f.size(), 0); + EXPECT_EQ(max_file_size(), f.size()); + fstat_sim = none; +} + +TEST(file_test, read_retry) { + file read_end, write_end; + file::pipe(read_end, write_end); + enum { SIZE = 4 }; + write_end.write("test", SIZE); + write_end.close(); + char buffer[SIZE]; + size_t count = 0; + EXPECT_RETRY(count = read_end.read(buffer, SIZE), read, + "cannot read from file"); + EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count); +} + +TEST(file_test, write_retry) { + file read_end, write_end; + file::pipe(read_end, write_end); + enum { SIZE = 4 }; + size_t count = 0; + EXPECT_RETRY(count = write_end.write("test", SIZE), write, + "cannot write to file"); + write_end.close(); +# ifndef _WIN32 + EXPECT_EQ(static_cast<std::streamsize>(SIZE), count); + char buffer[SIZE + 1]; + read_end.read(buffer, SIZE); + buffer[SIZE] = '\0'; + EXPECT_STREQ("test", buffer); +# endif +} + +# ifdef _WIN32 +TEST(file_test, convert_read_count) { + file read_end, write_end; + file::pipe(read_end, write_end); + char c; + size_t size = UINT_MAX; + if (sizeof(unsigned) != sizeof(size_t)) ++size; + read_count = 1; + read_nbyte = 0; + EXPECT_THROW(read_end.read(&c, size), std::system_error); + read_count = 0; + EXPECT_EQ(UINT_MAX, read_nbyte); +} + +TEST(file_test, convert_write_count) { + file read_end, write_end; + file::pipe(read_end, write_end); + char c; + size_t size = UINT_MAX; + if (sizeof(unsigned) != sizeof(size_t)) ++size; + write_count = 1; + write_nbyte = 0; + EXPECT_THROW(write_end.write(&c, size), std::system_error); + write_count = 0; + EXPECT_EQ(UINT_MAX, write_nbyte); +} +# endif + +TEST(file_test, dup_no_retry) { + int stdout_fd = FMT_POSIX(fileno(stdout)); + dup_count = 1; + EXPECT_SYSTEM_ERROR( + file::dup(stdout_fd), EINTR, + fmt::format("cannot duplicate file descriptor {}", stdout_fd)); + dup_count = 0; +} + +TEST(file_test, dup2_retry) { + int stdout_fd = FMT_POSIX(fileno(stdout)); + file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd); + EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2, + fmt::format("cannot duplicate file descriptor {} to {}", + f1.descriptor(), f2.descriptor())); +} + +TEST(file_test, dup2_no_except_retry) { + int stdout_fd = FMT_POSIX(fileno(stdout)); + file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd); + std::error_code ec; + dup2_count = 1; + f1.dup2(f2.descriptor(), ec); +# ifndef _WIN32 + EXPECT_EQ(4, dup2_count); +# else + EXPECT_EQ(EINTR, ec.value()); +# endif + dup2_count = 0; +} + +TEST(file_test, pipe_no_retry) { + file read_end, write_end; + pipe_count = 1; + EXPECT_SYSTEM_ERROR(file::pipe(read_end, write_end), EINTR, + "cannot create pipe"); + pipe_count = 0; +} + +TEST(file_test, fdopen_no_retry) { + file read_end, write_end; + file::pipe(read_end, write_end); + fdopen_count = 1; + EXPECT_SYSTEM_ERROR(read_end.fdopen("r"), EINTR, + "cannot associate stream with file descriptor"); + fdopen_count = 0; +} + +TEST(buffered_file_test, open_retry) { + write_file("temp", "there must be something here"); + std::unique_ptr<buffered_file> f{nullptr}; + EXPECT_RETRY(f.reset(new buffered_file("temp", "r")), fopen, + "cannot open file temp"); +# ifndef _WIN32 + char c = 0; + if (fread(&c, 1, 1, f->get()) < 1) + throw fmt::system_error(errno, "fread failed"); +# endif +} + +TEST(buffered_file_test, close_no_retry_in_dtor) { + file read_end, write_end; + file::pipe(read_end, write_end); + std::unique_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r"))); + int saved_fclose_count = 0; + EXPECT_WRITE( + stderr, + { + fclose_count = 1; + f.reset(nullptr); + saved_fclose_count = fclose_count; + fclose_count = 0; + }, + system_error_message(EINTR, "cannot close file") + "\n"); + EXPECT_EQ(2, saved_fclose_count); +} + +TEST(buffered_file_test, close_no_retry) { + file read_end, write_end; + file::pipe(read_end, write_end); + buffered_file f = read_end.fdopen("r"); + fclose_count = 1; + EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file"); + EXPECT_EQ(2, fclose_count); + fclose_count = 0; +} + +TEST(buffered_file_test, fileno_no_retry) { + file read_end, write_end; + file::pipe(read_end, write_end); + buffered_file f = read_end.fdopen("r"); + fileno_count = 1; + EXPECT_SYSTEM_ERROR((f.descriptor)(), EINTR, "cannot get file descriptor"); + EXPECT_EQ(2, fileno_count); + fileno_count = 0; +} +#endif // FMT_USE_FCNTL + +struct test_mock { + static test_mock* instance; +} * test_mock::instance; + +TEST(scoped_mock, scope) { + { + scoped_mock<test_mock> mock; + EXPECT_EQ(&mock, test_mock::instance); + test_mock& copy = mock; + static_cast<void>(copy); + } + EXPECT_EQ(nullptr, test_mock::instance); +} diff --git a/src/fmt/test/posix-mock.h b/src/fmt/test/posix-mock.h new file mode 100644 index 000000000..e76af7002 --- /dev/null +++ b/src/fmt/test/posix-mock.h @@ -0,0 +1,77 @@ +// Formatting library for C++ - mocks of POSIX functions +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_POSIX_TEST_H +#define FMT_POSIX_TEST_H + +#include <errno.h> +#include <locale.h> +#include <stdio.h> +#ifdef __APPLE__ +# include <xlocale.h> +#endif + +#ifdef _WIN32 +# include <windows.h> +#else +# include <sys/param.h> // for FreeBSD version +# include <sys/types.h> // for ssize_t +#endif + +#ifndef _MSC_VER +struct stat; +#endif + +namespace test { + +#ifndef _MSC_VER +// Size type for read and write. +typedef size_t size_t; +typedef ssize_t ssize_t; +int open(const char* path, int oflag, int mode); +int fstat(int fd, struct stat* buf); +#else +typedef unsigned size_t; +typedef int ssize_t; +errno_t sopen_s(int* pfh, const char* filename, int oflag, int shflag, + int pmode); +#endif + +#ifndef _WIN32 +long sysconf(int name); +#else +DWORD GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh); +#endif + +int close(int fildes); + +int dup(int fildes); +int dup2(int fildes, int fildes2); + +FILE* fdopen(int fildes, const char* mode); + +ssize_t read(int fildes, void* buf, size_t nbyte); +ssize_t write(int fildes, const void* buf, size_t nbyte); + +#ifndef _WIN32 +int pipe(int fildes[2]); +#else +int pipe(int* pfds, unsigned psize, int textmode); +#endif + +FILE* fopen(const char* filename, const char* mode); +int fclose(FILE* stream); +int(fileno)(FILE* stream); + +#if defined(FMT_LOCALE) && !defined(_WIN32) +locale_t newlocale(int category_mask, const char* locale, locale_t base); +#endif +} // namespace test + +#define FMT_SYSTEM(call) test::call + +#endif // FMT_POSIX_TEST_H diff --git a/src/fmt/test/printf-test.cc b/src/fmt/test/printf-test.cc new file mode 100644 index 000000000..383ffb83b --- /dev/null +++ b/src/fmt/test/printf-test.cc @@ -0,0 +1,584 @@ +// Formatting library for C++ - printf tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/printf.h" + +#include <cctype> +#include <climits> +#include <cstring> + +#include "fmt/xchar.h" +#include "gtest-extra.h" +#include "util.h" + +using fmt::format; +using fmt::format_error; +using fmt::detail::max_value; + +const unsigned big_num = INT_MAX + 1u; + +// Makes format string argument positional. +static std::string make_positional(fmt::string_view format) { + std::string s(format.data(), format.size()); + s.replace(s.find('%'), 1, "%1$"); + return s; +} + +static std::wstring make_positional(fmt::basic_string_view<wchar_t> format) { + std::wstring s(format.data(), format.size()); + s.replace(s.find(L'%'), 1, L"%1$"); + return s; +} + +// A wrapper around fmt::sprintf to workaround bogus warnings about invalid +// format strings in MSVC. +template <typename... Args> +std::string test_sprintf(fmt::string_view format, const Args&... args) { + return fmt::sprintf(format, args...); +} +template <typename... Args> +std::wstring test_sprintf(fmt::basic_string_view<wchar_t> format, + const Args&... args) { + return fmt::sprintf(format, args...); +} + +#define EXPECT_PRINTF(expected_output, format, arg) \ + EXPECT_EQ(expected_output, test_sprintf(format, arg)) \ + << "format: " << format; \ + EXPECT_EQ(expected_output, fmt::sprintf(make_positional(format), arg)) + +TEST(printf_test, no_args) { + EXPECT_EQ("test", test_sprintf("test")); + EXPECT_EQ(L"test", fmt::sprintf(L"test")); +} + +TEST(printf_test, escape) { + EXPECT_EQ("%", test_sprintf("%%")); + EXPECT_EQ("before %", test_sprintf("before %%")); + EXPECT_EQ("% after", test_sprintf("%% after")); + EXPECT_EQ("before % after", test_sprintf("before %% after")); + EXPECT_EQ("%s", test_sprintf("%%s")); + EXPECT_EQ(L"%", fmt::sprintf(L"%%")); + EXPECT_EQ(L"before %", fmt::sprintf(L"before %%")); + EXPECT_EQ(L"% after", fmt::sprintf(L"%% after")); + EXPECT_EQ(L"before % after", fmt::sprintf(L"before %% after")); + EXPECT_EQ(L"%s", fmt::sprintf(L"%%s")); +} + +TEST(printf_test, positional_args) { + EXPECT_EQ("42", test_sprintf("%1$d", 42)); + EXPECT_EQ("before 42", test_sprintf("before %1$d", 42)); + EXPECT_EQ("42 after", test_sprintf("%1$d after", 42)); + EXPECT_EQ("before 42 after", test_sprintf("before %1$d after", 42)); + EXPECT_EQ("answer = 42", test_sprintf("%1$s = %2$d", "answer", 42)); + EXPECT_EQ("42 is the answer", test_sprintf("%2$d is the %1$s", "answer", 42)); + EXPECT_EQ("abracadabra", test_sprintf("%1$s%2$s%1$s", "abra", "cad")); +} + +TEST(printf_test, automatic_arg_indexing) { + EXPECT_EQ("abc", test_sprintf("%c%c%c", 'a', 'b', 'c')); +} + +TEST(printf_test, number_is_too_big_in_arg_index) { + EXPECT_THROW_MSG(test_sprintf(format("%{}$", big_num)), format_error, + "argument not found"); + EXPECT_THROW_MSG(test_sprintf(format("%{}$d", big_num)), format_error, + "argument not found"); +} + +TEST(printf_test, switch_arg_indexing) { + EXPECT_THROW_MSG(test_sprintf("%1$d%", 1, 2), format_error, + "cannot switch from manual to automatic argument indexing"); + EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", big_num), 1, 2), + format_error, "number is too big"); + EXPECT_THROW_MSG(test_sprintf("%1$d%d", 1, 2), format_error, + "cannot switch from manual to automatic argument indexing"); + + EXPECT_THROW_MSG(test_sprintf("%d%1$", 1, 2), format_error, + "cannot switch from automatic to manual argument indexing"); + EXPECT_THROW_MSG(test_sprintf(format("%d%{}$d", big_num), 1, 2), format_error, + "cannot switch from automatic to manual argument indexing"); + EXPECT_THROW_MSG(test_sprintf("%d%1$d", 1, 2), format_error, + "cannot switch from automatic to manual argument indexing"); + + // Indexing errors override width errors. + EXPECT_THROW_MSG(test_sprintf(format("%d%1${}d", big_num), 1, 2), + format_error, "number is too big"); + EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", big_num), 1, 2), + format_error, "number is too big"); +} + +TEST(printf_test, invalid_arg_index) { + EXPECT_THROW_MSG(test_sprintf("%0$d", 42), format_error, + "argument not found"); + EXPECT_THROW_MSG(test_sprintf("%2$d", 42), format_error, + "argument not found"); + EXPECT_THROW_MSG(test_sprintf(format("%{}$d", INT_MAX), 42), format_error, + "argument not found"); + + EXPECT_THROW_MSG(test_sprintf("%2$", 42), format_error, "argument not found"); + EXPECT_THROW_MSG(test_sprintf(format("%{}$d", big_num), 42), format_error, + "argument not found"); +} + +TEST(printf_test, default_align_right) { + EXPECT_PRINTF(" 42", "%5d", 42); + EXPECT_PRINTF(" abc", "%5s", "abc"); +} + +TEST(printf_test, zero_flag) { + EXPECT_PRINTF("00042", "%05d", 42); + EXPECT_PRINTF("-0042", "%05d", -42); + + EXPECT_PRINTF("00042", "%05d", 42); + EXPECT_PRINTF("-0042", "%05d", -42); + EXPECT_PRINTF("-004.2", "%06g", -4.2); + + EXPECT_PRINTF("+00042", "%00+6d", 42); + + EXPECT_PRINTF(" 42", "%05.d", 42); + EXPECT_PRINTF(" 0042", "%05.4d", 42); + + // '0' flag is ignored for non-numeric types. + EXPECT_PRINTF(" x", "%05c", 'x'); +} + +TEST(printf_test, plus_flag) { + EXPECT_PRINTF("+42", "%+d", 42); + EXPECT_PRINTF("-42", "%+d", -42); + EXPECT_PRINTF("+0042", "%+05d", 42); + EXPECT_PRINTF("+0042", "%0++5d", 42); + + // '+' flag is ignored for non-numeric types. + EXPECT_PRINTF("x", "%+c", 'x'); + + // '+' flag wins over space flag + EXPECT_PRINTF("+42", "%+ d", 42); + EXPECT_PRINTF("-42", "%+ d", -42); + EXPECT_PRINTF("+42", "% +d", 42); + EXPECT_PRINTF("-42", "% +d", -42); + EXPECT_PRINTF("+0042", "% +05d", 42); + EXPECT_PRINTF("+0042", "%0+ 5d", 42); + + // '+' flag and space flag are both ignored for non-numeric types. + EXPECT_PRINTF("x", "%+ c", 'x'); + EXPECT_PRINTF("x", "% +c", 'x'); +} + +TEST(printf_test, minus_flag) { + EXPECT_PRINTF("abc ", "%-5s", "abc"); + EXPECT_PRINTF("abc ", "%0--5s", "abc"); + + EXPECT_PRINTF("7 ", "%-5d", 7); + EXPECT_PRINTF("97 ", "%-5hhi", 'a'); + EXPECT_PRINTF("a ", "%-5c", 'a'); + + // '0' flag is ignored if '-' flag is given + EXPECT_PRINTF("7 ", "%-05d", 7); + EXPECT_PRINTF("7 ", "%0-5d", 7); + EXPECT_PRINTF("a ", "%-05c", 'a'); + EXPECT_PRINTF("a ", "%0-5c", 'a'); + EXPECT_PRINTF("97 ", "%-05hhi", 'a'); + EXPECT_PRINTF("97 ", "%0-5hhi", 'a'); + + // '-' and space flag don't interfere + EXPECT_PRINTF(" 42", "%- d", 42); +} + +TEST(printf_test, space_flag) { + EXPECT_PRINTF(" 42", "% d", 42); + EXPECT_PRINTF("-42", "% d", -42); + EXPECT_PRINTF(" 0042", "% 05d", 42); + EXPECT_PRINTF(" 0042", "%0 5d", 42); + + // ' ' flag is ignored for non-numeric types. + EXPECT_PRINTF("x", "% c", 'x'); +} + +TEST(printf_test, hash_flag) { + EXPECT_PRINTF("042", "%#o", 042); + EXPECT_PRINTF(fmt::format("0{:o}", static_cast<unsigned>(-042)), "%#o", -042); + EXPECT_PRINTF("0", "%#o", 0); + + EXPECT_PRINTF("0x42", "%#x", 0x42); + EXPECT_PRINTF("0X42", "%#X", 0x42); + EXPECT_PRINTF(fmt::format("0x{:x}", static_cast<unsigned>(-0x42)), "%#x", + -0x42); + EXPECT_PRINTF("0", "%#x", 0); + + EXPECT_PRINTF("0x0042", "%#06x", 0x42); + EXPECT_PRINTF("0x0042", "%0##6x", 0x42); + + EXPECT_PRINTF("-42.000000", "%#f", -42.0); + EXPECT_PRINTF("-42.000000", "%#F", -42.0); + + char buffer[256]; + safe_sprintf(buffer, "%#e", -42.0); + EXPECT_PRINTF(buffer, "%#e", -42.0); + safe_sprintf(buffer, "%#E", -42.0); + EXPECT_PRINTF(buffer, "%#E", -42.0); + + EXPECT_PRINTF("-42.0000", "%#g", -42.0); + EXPECT_PRINTF("-42.0000", "%#G", -42.0); + + safe_sprintf(buffer, "%#a", 16.0); + EXPECT_PRINTF(buffer, "%#a", 16.0); + safe_sprintf(buffer, "%#A", 16.0); + EXPECT_PRINTF(buffer, "%#A", 16.0); + + // '#' flag is ignored for non-numeric types. + EXPECT_PRINTF("x", "%#c", 'x'); +} + +TEST(printf_test, width) { + EXPECT_PRINTF(" abc", "%5s", "abc"); + + // Width cannot be specified twice. + EXPECT_THROW_MSG(test_sprintf("%5-5d", 42), format_error, + "invalid type specifier"); + + EXPECT_THROW_MSG(test_sprintf(format("%{}d", big_num), 42), format_error, + "number is too big"); + EXPECT_THROW_MSG(test_sprintf(format("%1${}d", big_num), 42), format_error, + "number is too big"); +} + +TEST(printf_test, dynamic_width) { + EXPECT_EQ(" 42", test_sprintf("%*d", 5, 42)); + EXPECT_EQ("42 ", test_sprintf("%*d", -5, 42)); + EXPECT_THROW_MSG(test_sprintf("%*d", 5.0, 42), format_error, + "width is not integer"); + EXPECT_THROW_MSG(test_sprintf("%*d"), format_error, "argument not found"); + EXPECT_THROW_MSG(test_sprintf("%*d", big_num, 42), format_error, + "number is too big"); +} + +TEST(printf_test, int_precision) { + EXPECT_PRINTF("00042", "%.5d", 42); + EXPECT_PRINTF("-00042", "%.5d", -42); + EXPECT_PRINTF("00042", "%.5x", 0x42); + EXPECT_PRINTF("0x00042", "%#.5x", 0x42); + EXPECT_PRINTF("00042", "%.5o", 042); + EXPECT_PRINTF("00042", "%#.5o", 042); + + EXPECT_PRINTF(" 00042", "%7.5d", 42); + EXPECT_PRINTF(" 00042", "%7.5x", 0x42); + EXPECT_PRINTF(" 0x00042", "%#10.5x", 0x42); + EXPECT_PRINTF(" 00042", "%7.5o", 042); + EXPECT_PRINTF(" 00042", "%#10.5o", 042); + + EXPECT_PRINTF("00042 ", "%-7.5d", 42); + EXPECT_PRINTF("00042 ", "%-7.5x", 0x42); + EXPECT_PRINTF("0x00042 ", "%-#10.5x", 0x42); + EXPECT_PRINTF("00042 ", "%-7.5o", 042); + EXPECT_PRINTF("00042 ", "%-#10.5o", 042); +} + +TEST(printf_test, float_precision) { + char buffer[256]; + safe_sprintf(buffer, "%.3e", 1234.5678); + EXPECT_PRINTF(buffer, "%.3e", 1234.5678); + EXPECT_PRINTF("1234.568", "%.3f", 1234.5678); + EXPECT_PRINTF("1.23e+03", "%.3g", 1234.5678); + safe_sprintf(buffer, "%.3a", 1234.5678); + EXPECT_PRINTF(buffer, "%.3a", 1234.5678); +} + +TEST(printf_test, string_precision) { + char test[] = {'H', 'e', 'l', 'l', 'o'}; + EXPECT_EQ(fmt::sprintf("%.4s", test), "Hell"); +} + +TEST(printf_test, ignore_precision_for_non_numeric_arg) { + EXPECT_PRINTF("abc", "%.5s", "abc"); +} + +TEST(printf_test, dynamic_precision) { + EXPECT_EQ("00042", test_sprintf("%.*d", 5, 42)); + EXPECT_EQ("42", test_sprintf("%.*d", -5, 42)); + EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error, + "precision is not integer"); + EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error, "argument not found"); + EXPECT_THROW_MSG(test_sprintf("%.*d", big_num, 42), format_error, + "number is too big"); + if (sizeof(long long) != sizeof(int)) { + long long prec = static_cast<long long>(INT_MIN) - 1; + EXPECT_THROW_MSG(test_sprintf("%.*d", prec, 42), format_error, + "number is too big"); + } +} + +template <typename T> struct make_signed { typedef T type; }; + +#define SPECIALIZE_MAKE_SIGNED(T, S) \ + template <> struct make_signed<T> { typedef S type; } + +SPECIALIZE_MAKE_SIGNED(char, signed char); +SPECIALIZE_MAKE_SIGNED(unsigned char, signed char); +SPECIALIZE_MAKE_SIGNED(unsigned short, short); +SPECIALIZE_MAKE_SIGNED(unsigned, int); +SPECIALIZE_MAKE_SIGNED(unsigned long, long); +SPECIALIZE_MAKE_SIGNED(unsigned long long, long long); + +// Test length format specifier ``length_spec``. +template <typename T, typename U> +void test_length(const char* length_spec, U value) { + long long signed_value = 0; + unsigned long long unsigned_value = 0; + // Apply integer promotion to the argument. + unsigned long long max = max_value<U>(); + using fmt::detail::const_check; + if (const_check(max <= static_cast<unsigned>(max_value<int>()))) { + signed_value = static_cast<int>(value); + unsigned_value = static_cast<unsigned long long>(value); + } else if (const_check(max <= max_value<unsigned>())) { + signed_value = static_cast<unsigned>(value); + unsigned_value = static_cast<unsigned long long>(value); + } + if (sizeof(U) <= sizeof(int) && sizeof(int) < sizeof(T)) { + signed_value = static_cast<long long>(value); + unsigned_value = static_cast<unsigned long long>( + static_cast<typename std::make_unsigned<unsigned>::type>(value)); + } else { + signed_value = static_cast<typename make_signed<T>::type>(value); + unsigned_value = static_cast<typename std::make_unsigned<T>::type>(value); + } + std::ostringstream os; + os << signed_value; + EXPECT_PRINTF(os.str(), fmt::format("%{}d", length_spec), value); + EXPECT_PRINTF(os.str(), fmt::format("%{}i", length_spec), value); + os.str(""); + os << unsigned_value; + EXPECT_PRINTF(os.str(), fmt::format("%{}u", length_spec), value); + os.str(""); + os << std::oct << unsigned_value; + EXPECT_PRINTF(os.str(), fmt::format("%{}o", length_spec), value); + os.str(""); + os << std::hex << unsigned_value; + EXPECT_PRINTF(os.str(), fmt::format("%{}x", length_spec), value); + os.str(""); + os << std::hex << std::uppercase << unsigned_value; + EXPECT_PRINTF(os.str(), fmt::format("%{}X", length_spec), value); +} + +template <typename T> void test_length(const char* length_spec) { + T min = std::numeric_limits<T>::min(), max = max_value<T>(); + test_length<T>(length_spec, 42); + test_length<T>(length_spec, -42); + test_length<T>(length_spec, min); + test_length<T>(length_spec, max); + long long long_long_min = std::numeric_limits<long long>::min(); + if (static_cast<long long>(min) > long_long_min) + test_length<T>(length_spec, static_cast<long long>(min) - 1); + unsigned long long long_long_max = max_value<long long>(); + if (static_cast<unsigned long long>(max) < long_long_max) + test_length<T>(length_spec, static_cast<long long>(max) + 1); + test_length<T>(length_spec, std::numeric_limits<short>::min()); + test_length<T>(length_spec, max_value<unsigned short>()); + test_length<T>(length_spec, std::numeric_limits<int>::min()); + test_length<T>(length_spec, max_value<int>()); + test_length<T>(length_spec, std::numeric_limits<unsigned>::min()); + test_length<T>(length_spec, max_value<unsigned>()); + test_length<T>(length_spec, std::numeric_limits<long long>::min()); + test_length<T>(length_spec, max_value<long long>()); + test_length<T>(length_spec, std::numeric_limits<unsigned long long>::min()); + test_length<T>(length_spec, max_value<unsigned long long>()); +} + +TEST(printf_test, length) { + test_length<char>("hh"); + test_length<signed char>("hh"); + test_length<unsigned char>("hh"); + test_length<short>("h"); + test_length<unsigned short>("h"); + test_length<long>("l"); + test_length<unsigned long>("l"); + test_length<long long>("ll"); + test_length<unsigned long long>("ll"); + test_length<intmax_t>("j"); + test_length<size_t>("z"); + test_length<std::ptrdiff_t>("t"); + long double max = max_value<long double>(); + EXPECT_PRINTF(fmt::format("{:.6}", max), "%g", max); + EXPECT_PRINTF(fmt::format("{:.6}", max), "%Lg", max); +} + +TEST(printf_test, bool) { + EXPECT_PRINTF("1", "%d", true); + EXPECT_PRINTF("true", "%s", true); +} + +TEST(printf_test, int) { + EXPECT_PRINTF("-42", "%d", -42); + EXPECT_PRINTF("-42", "%i", -42); + unsigned u = 0 - 42u; + EXPECT_PRINTF(fmt::format("{}", u), "%u", -42); + EXPECT_PRINTF(fmt::format("{:o}", u), "%o", -42); + EXPECT_PRINTF(fmt::format("{:x}", u), "%x", -42); + EXPECT_PRINTF(fmt::format("{:X}", u), "%X", -42); +} + +TEST(printf_test, long_long) { + // fmt::printf allows passing long long arguments to %d without length + // specifiers. + long long max = max_value<long long>(); + EXPECT_PRINTF(fmt::format("{}", max), "%d", max); +} + +TEST(printf_test, float) { + EXPECT_PRINTF("392.650000", "%f", 392.65); + EXPECT_PRINTF("392.65", "%.2f", 392.65); + EXPECT_PRINTF("392.6", "%.1f", 392.65); + EXPECT_PRINTF("393", "%.f", 392.65); + EXPECT_PRINTF("392.650000", "%F", 392.65); + char buffer[256]; + safe_sprintf(buffer, "%e", 392.65); + EXPECT_PRINTF(buffer, "%e", 392.65); + safe_sprintf(buffer, "%E", 392.65); + EXPECT_PRINTF(buffer, "%E", 392.65); + EXPECT_PRINTF("392.65", "%g", 392.65); + EXPECT_PRINTF("392.65", "%G", 392.65); + EXPECT_PRINTF("392", "%g", 392.0); + EXPECT_PRINTF("392", "%G", 392.0); + EXPECT_PRINTF("4.56e-07", "%g", 0.000000456); + safe_sprintf(buffer, "%a", -392.65); + EXPECT_EQ(buffer, format("{:a}", -392.65)); + safe_sprintf(buffer, "%A", -392.65); + EXPECT_EQ(buffer, format("{:A}", -392.65)); +} + +TEST(printf_test, inf) { + double inf = std::numeric_limits<double>::infinity(); + for (const char* type = "fega"; *type; ++type) { + EXPECT_PRINTF("inf", fmt::format("%{}", *type), inf); + char upper = static_cast<char>(std::toupper(*type)); + EXPECT_PRINTF("INF", fmt::format("%{}", upper), inf); + } +} + +TEST(printf_test, char) { + EXPECT_PRINTF("x", "%c", 'x'); + int max = max_value<int>(); + EXPECT_PRINTF(fmt::format("{}", static_cast<char>(max)), "%c", max); + // EXPECT_PRINTF("x", "%lc", L'x'); + EXPECT_PRINTF(L"x", L"%c", L'x'); + EXPECT_PRINTF(fmt::format(L"{}", static_cast<wchar_t>(max)), L"%c", max); +} + +TEST(printf_test, string) { + EXPECT_PRINTF("abc", "%s", "abc"); + const char* null_str = nullptr; + EXPECT_PRINTF("(null)", "%s", null_str); + EXPECT_PRINTF(" (null)", "%10s", null_str); + EXPECT_PRINTF(L"abc", L"%s", L"abc"); + const wchar_t* null_wstr = nullptr; + EXPECT_PRINTF(L"(null)", L"%s", null_wstr); + EXPECT_PRINTF(L" (null)", L"%10s", null_wstr); +} + +TEST(printf_test, pointer) { + int n; + void* p = &n; + EXPECT_PRINTF(fmt::format("{}", p), "%p", p); + p = nullptr; + EXPECT_PRINTF("(nil)", "%p", p); + EXPECT_PRINTF(" (nil)", "%10p", p); + const char* s = "test"; + EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s); + const char* null_str = nullptr; + EXPECT_PRINTF("(nil)", "%p", null_str); + + p = &n; + EXPECT_PRINTF(fmt::format(L"{}", p), L"%p", p); + p = nullptr; + EXPECT_PRINTF(L"(nil)", L"%p", p); + EXPECT_PRINTF(L" (nil)", L"%10p", p); + const wchar_t* w = L"test"; + EXPECT_PRINTF(fmt::format(L"{:p}", w), L"%p", w); + const wchar_t* null_wstr = nullptr; + EXPECT_PRINTF(L"(nil)", L"%p", null_wstr); +} + +enum test_enum { answer = 42 }; +auto format_as(test_enum e) -> int { return e; } + +TEST(printf_test, enum) { + EXPECT_PRINTF("42", "%d", answer); + volatile test_enum volatile_enum = answer; + EXPECT_PRINTF("42", "%d", volatile_enum); +} + +#if FMT_USE_FCNTL +TEST(printf_test, examples) { + const char* weekday = "Thursday"; + const char* month = "August"; + int day = 21; + EXPECT_WRITE(stdout, fmt::printf("%1$s, %3$d %2$s", weekday, month, day), + "Thursday, 21 August"); +} + +TEST(printf_test, printf_error) { + fmt::file read_end, write_end; + fmt::file::pipe(read_end, write_end); + int result = fmt::fprintf(read_end.fdopen("r").get(), "test"); + EXPECT_LT(result, 0); +} +#endif + +TEST(printf_test, wide_string) { + EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); +} + +TEST(printf_test, vprintf) { + fmt::format_arg_store<fmt::printf_context, int> as{42}; + fmt::basic_format_args<fmt::printf_context> args(as); + EXPECT_EQ(fmt::vsprintf("%d", args), "42"); + EXPECT_WRITE(stdout, fmt::vprintf("%d", args), "42"); + EXPECT_WRITE(stdout, fmt::vfprintf(stdout, "%d", args), "42"); +} + +template <typename... Args> +void check_format_string_regression(fmt::string_view s, const Args&... args) { + fmt::sprintf(s, args...); +} + +TEST(printf_test, check_format_string_regression) { + check_format_string_regression("%c%s", 'x', ""); +} + +TEST(printf_test, fixed_large_exponent) { + EXPECT_EQ("1000000000000000000000", fmt::sprintf("%.*f", -13, 1e21)); +} + +TEST(printf_test, vsprintf_make_args_example) { + fmt::format_arg_store<fmt::printf_context, int, const char*> as{42, + "something"}; + fmt::basic_format_args<fmt::printf_context> args(as); + EXPECT_EQ("[42] something happened", fmt::vsprintf("[%d] %s happened", args)); + auto as2 = fmt::make_printf_args(42, "something"); + fmt::basic_format_args<fmt::printf_context> args2(as2); + EXPECT_EQ("[42] something happened", + fmt::vsprintf("[%d] %s happened", args2)); + EXPECT_EQ("[42] something happened", + fmt::vsprintf("[%d] %s happened", + {fmt::make_printf_args(42, "something")})); +} + +TEST(printf_test, vsprintf_make_wargs_example) { + fmt::format_arg_store<fmt::wprintf_context, int, const wchar_t*> as{ + 42, L"something"}; + fmt::basic_format_args<fmt::wprintf_context> args(as); + EXPECT_EQ(L"[42] something happened", + fmt::vsprintf(L"[%d] %s happened", args)); + auto as2 = fmt::make_wprintf_args(42, L"something"); + fmt::basic_format_args<fmt::wprintf_context> args2(as2); + EXPECT_EQ(L"[42] something happened", + fmt::vsprintf(L"[%d] %s happened", args2)); + EXPECT_EQ(L"[42] something happened", + fmt::vsprintf(L"[%d] %s happened", + {fmt::make_wprintf_args(42, L"something")})); +} diff --git a/src/fmt/test/ranges-odr-test.cc b/src/fmt/test/ranges-odr-test.cc new file mode 100644 index 000000000..031354d3c --- /dev/null +++ b/src/fmt/test/ranges-odr-test.cc @@ -0,0 +1,17 @@ +// Formatting library for C++ - the core API +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include <vector> + +#include "fmt/ranges.h" +#include "gtest/gtest.h" + +// call fmt::format from another translation unit to test ODR +TEST(ranges_odr_test, format_vector) { + auto v = std::vector<int>{1, 2, 3, 5, 7, 11}; + EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]"); +} diff --git a/src/fmt/test/ranges-test.cc b/src/fmt/test/ranges-test.cc new file mode 100644 index 000000000..3221e2ed0 --- /dev/null +++ b/src/fmt/test/ranges-test.cc @@ -0,0 +1,424 @@ +// Formatting library for C++ - the core API +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. +// +// Copyright (c) 2018 - present, Remotion (Igor Schulz) +// All Rights Reserved +// {fmt} support for ranges, containers and types tuple interface. + +#include "fmt/ranges.h" + +#include <map> +#include <string> +#include <vector> + +#include "gtest/gtest.h" + +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 601 +# define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY +#endif + +#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1910 +# define FMT_RANGES_TEST_ENABLE_JOIN +# define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT +#endif + +#ifdef FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY +TEST(ranges_test, format_array) { + int arr[] = {1, 2, 3, 5, 7, 11}; + EXPECT_EQ(fmt::format("{}", arr), "[1, 2, 3, 5, 7, 11]"); +} + +TEST(ranges_test, format_2d_array) { + int arr[][2] = {{1, 2}, {3, 5}, {7, 11}}; + EXPECT_EQ(fmt::format("{}", arr), "[[1, 2], [3, 5], [7, 11]]"); +} + +TEST(ranges_test, format_array_of_literals) { + const char* arr[] = {"1234", "abcd"}; + EXPECT_EQ(fmt::format("{}", arr), "[\"1234\", \"abcd\"]"); + EXPECT_EQ(fmt::format("{:n}", arr), "\"1234\", \"abcd\""); + EXPECT_EQ(fmt::format("{:n:}", arr), "1234, abcd"); +} +#endif // FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY + +TEST(ranges_test, format_vector) { + auto v = std::vector<int>{1, 2, 3, 5, 7, 11}; + EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]"); + EXPECT_EQ(fmt::format("{::#x}", v), "[0x1, 0x2, 0x3, 0x5, 0x7, 0xb]"); + EXPECT_EQ(fmt::format("{:n:#x}", v), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb"); +} + +TEST(ranges_test, format_vector2) { + auto v = std::vector<std::vector<int>>{{1, 2}, {3, 5}, {7, 11}}; + EXPECT_EQ(fmt::format("{}", v), "[[1, 2], [3, 5], [7, 11]]"); + EXPECT_EQ(fmt::format("{:::#x}", v), "[[0x1, 0x2], [0x3, 0x5], [0x7, 0xb]]"); + EXPECT_EQ(fmt::format("{:n:n:#x}", v), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb"); +} + +TEST(ranges_test, format_map) { + auto m = std::map<std::string, int>{{"one", 1}, {"two", 2}}; + EXPECT_EQ(fmt::format("{}", m), "{\"one\": 1, \"two\": 2}"); + EXPECT_EQ(fmt::format("{:n}", m), "\"one\": 1, \"two\": 2"); +} + +TEST(ranges_test, format_set) { + EXPECT_EQ(fmt::format("{}", std::set<std::string>{"one", "two"}), + "{\"one\", \"two\"}"); +} + +namespace adl { +struct box { + int value; +}; + +auto begin(const box& b) -> const int* { return &b.value; } + +auto end(const box& b) -> const int* { return &b.value + 1; } +} // namespace adl + +TEST(ranges_test, format_adl_begin_end) { + auto b = adl::box{42}; + EXPECT_EQ(fmt::format("{}", b), "[42]"); +} + +TEST(ranges_test, format_pair) { + auto p = std::pair<int, float>(42, 1.5f); + EXPECT_EQ(fmt::format("{}", p), "(42, 1.5)"); +} + +struct unformattable {}; + +TEST(ranges_test, format_tuple) { + auto t = + std::tuple<int, float, std::string, char>(42, 1.5f, "this is tuple", 'i'); + EXPECT_EQ(fmt::format("{}", t), "(42, 1.5, \"this is tuple\", 'i')"); + EXPECT_EQ(fmt::format("{}", std::tuple<>()), "()"); + + EXPECT_TRUE((fmt::is_formattable<std::tuple<>>::value)); + EXPECT_FALSE((fmt::is_formattable<unformattable>::value)); + EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable>>::value)); + EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable, int>>::value)); + EXPECT_FALSE((fmt::is_formattable<std::tuple<int, unformattable>>::value)); + EXPECT_FALSE( + (fmt::is_formattable<std::tuple<unformattable, unformattable>>::value)); + EXPECT_TRUE((fmt::is_formattable<std::tuple<int, float>>::value)); +} + +#ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT +struct tuple_like { + int i; + std::string str; + + template <size_t N> fmt::enable_if_t<N == 0, int> get() const noexcept { + return i; + } + template <size_t N> + fmt::enable_if_t<N == 1, fmt::string_view> get() const noexcept { + return str; + } +}; + +template <size_t N> +auto get(const tuple_like& t) noexcept -> decltype(t.get<N>()) { + return t.get<N>(); +} + +namespace std { +template <> +struct tuple_size<tuple_like> : std::integral_constant<size_t, 2> {}; + +template <size_t N> struct tuple_element<N, tuple_like> { + using type = decltype(std::declval<tuple_like>().get<N>()); +}; +} // namespace std + +TEST(ranges_test, format_struct) { + auto t = tuple_like{42, "foo"}; + EXPECT_EQ(fmt::format("{}", t), "(42, \"foo\")"); +} +#endif // FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT + +TEST(ranges_test, format_to) { + char buf[10]; + auto end = fmt::format_to(buf, "{}", std::vector<int>{1, 2, 3}); + *end = '\0'; + EXPECT_STREQ(buf, "[1, 2, 3]"); +} + +struct path_like { + const path_like* begin() const; + const path_like* end() const; + + operator std::string() const; +}; + +TEST(ranges_test, path_like) { + EXPECT_FALSE((fmt::is_range<path_like, char>::value)); +} + +#ifdef FMT_USE_STRING_VIEW +struct string_like { + const char* begin(); + const char* end(); + operator fmt::string_view() const { return "foo"; } + operator std::string_view() const { return "foo"; } +}; + +TEST(ranges_test, format_string_like) { + EXPECT_EQ(fmt::format("{}", string_like()), "foo"); +} +#endif // FMT_USE_STRING_VIEW + +// A range that provides non-const only begin()/end() to test fmt::join handles +// that. +// +// Some ranges (e.g. those produced by range-v3's views::filter()) can cache +// information during iteration so they only provide non-const begin()/end(). +template <typename T> class non_const_only_range { + private: + std::vector<T> vec; + + public: + using const_iterator = typename ::std::vector<T>::const_iterator; + + template <typename... Args> + explicit non_const_only_range(Args&&... args) + : vec(std::forward<Args>(args)...) {} + + const_iterator begin() { return vec.begin(); } + const_iterator end() { return vec.end(); } +}; + +template <typename T> class noncopyable_range { + private: + std::vector<T> vec; + + public: + using const_iterator = typename ::std::vector<T>::const_iterator; + + template <typename... Args> + explicit noncopyable_range(Args&&... args) + : vec(std::forward<Args>(args)...) {} + + noncopyable_range(noncopyable_range const&) = delete; + noncopyable_range(noncopyable_range&) = delete; + + const_iterator begin() const { return vec.begin(); } + const_iterator end() const { return vec.end(); } +}; + +TEST(ranges_test, range) { + noncopyable_range<int> w(3u, 0); + EXPECT_EQ(fmt::format("{}", w), "[0, 0, 0]"); + EXPECT_EQ(fmt::format("{}", noncopyable_range<int>(3u, 0)), "[0, 0, 0]"); + + non_const_only_range<int> x(3u, 0); + EXPECT_EQ(fmt::format("{}", x), "[0, 0, 0]"); + EXPECT_EQ(fmt::format("{}", non_const_only_range<int>(3u, 0)), "[0, 0, 0]"); + + auto y = std::vector<int>(3u, 0); + EXPECT_EQ(fmt::format("{}", y), "[0, 0, 0]"); + EXPECT_EQ(fmt::format("{}", std::vector<int>(3u, 0)), "[0, 0, 0]"); + + const auto z = std::vector<int>(3u, 0); + EXPECT_EQ(fmt::format("{}", z), "[0, 0, 0]"); +} + +enum test_enum { foo }; +auto format_as(test_enum e) -> int { return e; } + +TEST(ranges_test, enum_range) { + auto v = std::vector<test_enum>{test_enum::foo}; + EXPECT_EQ(fmt::format("{}", v), "[0]"); +} + +#if !FMT_MSC_VERSION + +TEST(ranges_test, unformattable_range) { + EXPECT_FALSE((fmt::has_formatter<std::vector<unformattable>, + fmt::format_context>::value)); +} +#endif + +#ifdef FMT_RANGES_TEST_ENABLE_JOIN +TEST(ranges_test, join_tuple) { + // Value tuple args. + auto t1 = std::tuple<char, int, float>('a', 1, 2.0f); + EXPECT_EQ(fmt::format("({})", fmt::join(t1, ", ")), "(a, 1, 2)"); + + // Testing lvalue tuple args. + int x = 4; + auto t2 = std::tuple<char, int&>('b', x); + EXPECT_EQ(fmt::format("{}", fmt::join(t2, " + ")), "b + 4"); + + // Empty tuple. + auto t3 = std::tuple<>(); + EXPECT_EQ(fmt::format("{}", fmt::join(t3, "|")), ""); + + // Single element tuple. + auto t4 = std::tuple<float>(4.0f); + EXPECT_EQ(fmt::format("{}", fmt::join(t4, "/")), "4"); + +# if FMT_TUPLE_JOIN_SPECIFIERS + // Specs applied to each element. + auto t5 = std::tuple<int, int, long>(-3, 100, 1); + EXPECT_EQ(fmt::format("{:+03}", fmt::join(t5, ", ")), "-03, +100, +01"); + + auto t6 = std::tuple<float, double, long double>(3, 3.14, 3.1415); + EXPECT_EQ(fmt::format("{:5.5f}", fmt::join(t6, ", ")), + "3.00000, 3.14000, 3.14150"); + + // Testing lvalue tuple args. + int y = -1; + auto t7 = std::tuple<int, int&, const int&>(3, y, y); + EXPECT_EQ(fmt::format("{:03}", fmt::join(t7, ", ")), "003, -01, -01"); +# endif +} + +TEST(ranges_test, join_initializer_list) { + EXPECT_EQ(fmt::format("{}", fmt::join({1, 2, 3}, ", ")), "1, 2, 3"); + EXPECT_EQ(fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " ")), + "fmt rocks !"); +} + +struct zstring_sentinel {}; + +bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; } +bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; } + +struct zstring { + const char* p; + const char* begin() const { return p; } + zstring_sentinel end() const { return {}; } +}; + +# ifdef __cpp_lib_ranges +struct cpp20_only_range { + struct iterator { + int val = 0; + + using value_type = int; + using difference_type = std::ptrdiff_t; + using iterator_concept = std::input_iterator_tag; + + iterator() = default; + iterator(int i) : val(i) {} + int operator*() const { return val; } + iterator& operator++() { + ++val; + return *this; + } + void operator++(int) { ++*this; } + bool operator==(const iterator& rhs) const { return val == rhs.val; } + }; + + int lo; + int hi; + + iterator begin() const { return iterator(lo); } + iterator end() const { return iterator(hi); } +}; + +static_assert(std::input_iterator<cpp20_only_range::iterator>); +# endif + +TEST(ranges_test, join_sentinel) { + auto hello = zstring{"hello"}; + EXPECT_EQ(fmt::format("{}", hello), "['h', 'e', 'l', 'l', 'o']"); + EXPECT_EQ(fmt::format("{::}", hello), "[h, e, l, l, o]"); + EXPECT_EQ(fmt::format("{}", fmt::join(hello, "_")), "h_e_l_l_o"); +} + +TEST(ranges_test, join_range) { + noncopyable_range<int> w(3u, 0); + EXPECT_EQ(fmt::format("{}", fmt::join(w, ",")), "0,0,0"); + EXPECT_EQ(fmt::format("{}", fmt::join(noncopyable_range<int>(3u, 0), ",")), + "0,0,0"); + + non_const_only_range<int> x(3u, 0); + EXPECT_EQ(fmt::format("{}", fmt::join(x, ",")), "0,0,0"); + EXPECT_EQ(fmt::format("{}", fmt::join(non_const_only_range<int>(3u, 0), ",")), + "0,0,0"); + + auto y = std::vector<int>(3u, 0); + EXPECT_EQ(fmt::format("{}", fmt::join(y, ",")), "0,0,0"); + EXPECT_EQ(fmt::format("{}", fmt::join(std::vector<int>(3u, 0), ",")), + "0,0,0"); + + const auto z = std::vector<int>(3u, 0); + EXPECT_EQ(fmt::format("{}", fmt::join(z, ",")), "0,0,0"); + +# ifdef __cpp_lib_ranges + EXPECT_EQ(fmt::format("{}", cpp20_only_range{.lo = 0, .hi = 5}), + "[0, 1, 2, 3, 4]"); + EXPECT_EQ( + fmt::format("{}", fmt::join(cpp20_only_range{.lo = 0, .hi = 5}, ",")), + "0,1,2,3,4"); +# endif +} +#endif // FMT_RANGES_TEST_ENABLE_JOIN + +TEST(ranges_test, is_printable) { + using fmt::detail::is_printable; + EXPECT_TRUE(is_printable(0x0323)); + EXPECT_FALSE(is_printable(0x0378)); + EXPECT_FALSE(is_printable(0x110000)); +} + +TEST(ranges_test, escape_string) { + using vec = std::vector<std::string>; + EXPECT_EQ(fmt::format("{}", vec{"\n\r\t\"\\"}), "[\"\\n\\r\\t\\\"\\\\\"]"); + EXPECT_EQ(fmt::format("{}", vec{"\x07"}), "[\"\\x07\"]"); + EXPECT_EQ(fmt::format("{}", vec{"\x7f"}), "[\"\\x7f\"]"); + EXPECT_EQ(fmt::format("{}", vec{"n\xcc\x83"}), "[\"n\xcc\x83\"]"); + + if (fmt::detail::is_utf8()) { + EXPECT_EQ(fmt::format("{}", vec{"\xcd\xb8"}), "[\"\\u0378\"]"); + // Unassigned Unicode code points. + EXPECT_EQ(fmt::format("{}", vec{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]"); + // Broken utf-8. + EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xc0"}), + "[\"\\xf4\\x8f\\xbf\\xc0\"]"); + EXPECT_EQ(fmt::format("{}", vec{"\xf0\x28"}), "[\"\\xf0(\"]"); + EXPECT_EQ(fmt::format("{}", vec{"\xe1\x28"}), "[\"\\xe1(\"]"); + EXPECT_EQ(fmt::format("{}", vec{std::string("\xf0\x28\0\0anything", 12)}), + "[\"\\xf0(\\x00\\x00anything\"]"); + + // Correct utf-8. + EXPECT_EQ(fmt::format("{}", vec{"понедельник"}), "[\"понедельник\"]"); + } +} + +#ifdef FMT_USE_STRING_VIEW +struct convertible_to_string_view { + operator std::string_view() const { return "foo"; } +}; + +TEST(ranges_test, escape_convertible_to_string_view) { + EXPECT_EQ(fmt::format("{}", std::vector<convertible_to_string_view>(1)), + "[\"foo\"]"); +} +#endif // FMT_USE_STRING_VIEW + +template <typename R> struct fmt_ref_view { + R* r; + + auto begin() const -> decltype(r->begin()) { return r->begin(); } + auto end() const -> decltype(r->end()) { return r->end(); } +}; + +TEST(ranges_test, range_of_range_of_mixed_const) { + std::vector<std::vector<int>> v = {{1, 2, 3}, {4, 5}}; + EXPECT_EQ(fmt::format("{}", v), "[[1, 2, 3], [4, 5]]"); + + fmt_ref_view<decltype(v)> r{&v}; + EXPECT_EQ(fmt::format("{}", r), "[[1, 2, 3], [4, 5]]"); +} + +TEST(ranges_test, vector_char) { + EXPECT_EQ(fmt::format("{}", std::vector<char>{'a', 'b'}), "['a', 'b']"); +} diff --git a/src/fmt/test/scan-test.cc b/src/fmt/test/scan-test.cc new file mode 100644 index 000000000..7e327ea3f --- /dev/null +++ b/src/fmt/test/scan-test.cc @@ -0,0 +1,116 @@ +// Formatting library for C++ - scanning API test +// +// Copyright (c) 2019 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "scan.h" + +#include <time.h> + +#include <climits> + +#include "gmock/gmock.h" +#include "gtest-extra.h" + +TEST(scan_test, read_text) { + auto s = fmt::string_view("foo"); + auto end = fmt::scan(s, "foo"); + EXPECT_EQ(end, s.end()); + EXPECT_THROW_MSG(fmt::scan("fob", "foo"), fmt::format_error, "invalid input"); +} + +TEST(scan_test, read_int) { + auto n = int(); + fmt::scan("42", "{}", n); + EXPECT_EQ(n, 42); + fmt::scan("-42", "{}", n); + EXPECT_EQ(n, -42); +} + +TEST(scan_test, read_longlong) { + long long n = 0; + fmt::scan("42", "{}", n); + EXPECT_EQ(n, 42); + fmt::scan("-42", "{}", n); + EXPECT_EQ(n, -42); +} + +TEST(scan_test, read_uint) { + auto n = unsigned(); + fmt::scan("42", "{}", n); + EXPECT_EQ(n, 42); + EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error, + "invalid input"); +} + +TEST(scan_test, read_ulonglong) { + unsigned long long n = 0; + fmt::scan("42", "{}", n); + EXPECT_EQ(n, 42); + EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error, + "invalid input"); +} + +TEST(scan_test, read_string) { + auto s = std::string(); + fmt::scan("foo", "{}", s); + EXPECT_EQ(s, "foo"); +} + +TEST(scan_test, read_string_view) { + auto s = fmt::string_view(); + fmt::scan("foo", "{}", s); + EXPECT_EQ(s, "foo"); +} + +#ifndef _WIN32 +namespace fmt { +template <> struct scanner<tm> { + std::string format; + + scan_parse_context::iterator parse(scan_parse_context& ctx) { + auto it = ctx.begin(); + if (it != ctx.end() && *it == ':') ++it; + auto end = it; + while (end != ctx.end() && *end != '}') ++end; + format.reserve(detail::to_unsigned(end - it + 1)); + format.append(it, end); + format.push_back('\0'); + return end; + } + + template <class ScanContext> + typename ScanContext::iterator scan(tm& t, ScanContext& ctx) { + auto result = strptime(ctx.begin(), format.c_str(), &t); + if (!result) throw format_error("failed to parse time"); + return result; + } +}; +} // namespace fmt + +TEST(scan_test, read_custom) { + auto input = "Date: 1985-10-25"; + auto t = tm(); + fmt::scan(input, "Date: {0:%Y-%m-%d}", t); + EXPECT_EQ(t.tm_year, 85); + EXPECT_EQ(t.tm_mon, 9); + EXPECT_EQ(t.tm_mday, 25); +} +#endif + +TEST(scan_test, invalid_format) { + EXPECT_THROW_MSG(fmt::scan("", "{}"), fmt::format_error, + "argument index out of range"); + EXPECT_THROW_MSG(fmt::scan("", "{"), fmt::format_error, + "invalid format string"); +} + +TEST(scan_test, example) { + auto key = std::string(); + auto value = int(); + fmt::scan("answer = 42", "{} = {}", key, value); + EXPECT_EQ(key, "answer"); + EXPECT_EQ(value, 42); +} diff --git a/src/fmt/test/scan.h b/src/fmt/test/scan.h new file mode 100644 index 000000000..41748ae89 --- /dev/null +++ b/src/fmt/test/scan.h @@ -0,0 +1,241 @@ +// Formatting library for C++ - scanning API proof of concept +// +// Copyright (c) 2019 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include <array> +#include <cassert> +#include <climits> + +#include "fmt/format.h" + +FMT_BEGIN_NAMESPACE +template <typename T, typename Char = char> struct scanner { + // A deleted default constructor indicates a disabled scanner. + scanner() = delete; +}; + +class scan_parse_context { + private: + string_view format_; + + public: + using iterator = string_view::iterator; + + explicit FMT_CONSTEXPR scan_parse_context(string_view format) + : format_(format) {} + + FMT_CONSTEXPR iterator begin() const { return format_.begin(); } + FMT_CONSTEXPR iterator end() const { return format_.end(); } + + void advance_to(iterator it) { + format_.remove_prefix(detail::to_unsigned(it - begin())); + } +}; + +struct scan_context { + private: + string_view input_; + + public: + using iterator = const char*; + + explicit scan_context(string_view input) : input_(input) {} + + iterator begin() const { return input_.data(); } + iterator end() const { return begin() + input_.size(); } + + void advance_to(iterator it) { + input_.remove_prefix(detail::to_unsigned(it - begin())); + } +}; + +namespace detail { +enum class scan_type { + none_type, + int_type, + uint_type, + long_long_type, + ulong_long_type, + string_type, + string_view_type, + custom_type +}; + +struct custom_scan_arg { + void* value; + void (*scan)(void* arg, scan_parse_context& parse_ctx, scan_context& ctx); +}; + +class scan_arg { + public: + scan_type type; + union { + int* int_value; + unsigned* uint_value; + long long* long_long_value; + unsigned long long* ulong_long_value; + std::string* string; + fmt::string_view* string_view; + custom_scan_arg custom; + // TODO: more types + }; + + scan_arg() : type(scan_type::none_type) {} + scan_arg(int& value) : type(scan_type::int_type), int_value(&value) {} + scan_arg(unsigned& value) : type(scan_type::uint_type), uint_value(&value) {} + scan_arg(long long& value) + : type(scan_type::long_long_type), long_long_value(&value) {} + scan_arg(unsigned long long& value) + : type(scan_type::ulong_long_type), ulong_long_value(&value) {} + scan_arg(std::string& value) : type(scan_type::string_type), string(&value) {} + scan_arg(fmt::string_view& value) + : type(scan_type::string_view_type), string_view(&value) {} + template <typename T> scan_arg(T& value) : type(scan_type::custom_type) { + custom.value = &value; + custom.scan = scan_custom_arg<T>; + } + + private: + template <typename T> + static void scan_custom_arg(void* arg, scan_parse_context& parse_ctx, + scan_context& ctx) { + scanner<T> s; + parse_ctx.advance_to(s.parse(parse_ctx)); + ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx)); + } +}; +} // namespace detail + +struct scan_args { + int size; + const detail::scan_arg* data; + + template <size_t N> + scan_args(const std::array<detail::scan_arg, N>& store) + : size(N), data(store.data()) { + static_assert(N < INT_MAX, "too many arguments"); + } +}; + +namespace detail { + +struct scan_handler : error_handler { + private: + scan_parse_context parse_ctx_; + scan_context scan_ctx_; + scan_args args_; + int next_arg_id_; + scan_arg arg_; + + template <typename T = unsigned> T read_uint() { + T value = 0; + auto it = scan_ctx_.begin(), end = scan_ctx_.end(); + while (it != end) { + char c = *it++; + if (c < '0' || c > '9') on_error("invalid input"); + // TODO: check overflow + value = value * 10 + static_cast<unsigned>(c - '0'); + } + scan_ctx_.advance_to(it); + return value; + } + + template <typename T = int> T read_int() { + auto it = scan_ctx_.begin(), end = scan_ctx_.end(); + bool negative = it != end && *it == '-'; + if (negative) ++it; + scan_ctx_.advance_to(it); + const auto value = read_uint<typename std::make_unsigned<T>::type>(); + if (negative) return -static_cast<T>(value); + return static_cast<T>(value); + } + + public: + scan_handler(string_view format, string_view input, scan_args args) + : parse_ctx_(format), scan_ctx_(input), args_(args), next_arg_id_(0) {} + + const char* pos() const { return scan_ctx_.begin(); } + + void on_text(const char* begin, const char* end) { + auto size = to_unsigned(end - begin); + auto it = scan_ctx_.begin(); + if (it + size > scan_ctx_.end() || + !std::equal(begin, end, make_checked(it, size))) { + on_error("invalid input"); + } + scan_ctx_.advance_to(it + size); + } + + FMT_CONSTEXPR int on_arg_id() { return on_arg_id(next_arg_id_++); } + FMT_CONSTEXPR int on_arg_id(int id) { + if (id >= args_.size) on_error("argument index out of range"); + arg_ = args_.data[id]; + return id; + } + FMT_CONSTEXPR int on_arg_id(string_view id) { + if (id.data()) on_error("invalid format"); + return 0; + } + + void on_replacement_field(int, const char*) { + auto it = scan_ctx_.begin(), end = scan_ctx_.end(); + switch (arg_.type) { + case scan_type::int_type: + *arg_.int_value = read_int(); + break; + case scan_type::uint_type: + *arg_.uint_value = read_uint(); + break; + case scan_type::long_long_type: + *arg_.long_long_value = read_int<long long>(); + break; + case scan_type::ulong_long_type: + *arg_.ulong_long_value = read_uint<unsigned long long>(); + break; + case scan_type::string_type: + while (it != end && *it != ' ') arg_.string->push_back(*it++); + scan_ctx_.advance_to(it); + break; + case scan_type::string_view_type: { + auto s = it; + while (it != end && *it != ' ') ++it; + *arg_.string_view = fmt::string_view(s, to_unsigned(it - s)); + scan_ctx_.advance_to(it); + break; + } + case scan_type::none_type: + case scan_type::custom_type: + assert(false); + } + } + + const char* on_format_specs(int, const char* begin, const char*) { + if (arg_.type != scan_type::custom_type) return begin; + parse_ctx_.advance_to(begin); + arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_); + return parse_ctx_.begin(); + } +}; +} // namespace detail + +template <typename... Args> +std::array<detail::scan_arg, sizeof...(Args)> make_scan_args(Args&... args) { + return {{args...}}; +} + +string_view::iterator vscan(string_view input, string_view format_str, + scan_args args) { + detail::scan_handler h(format_str, input, args); + detail::parse_format_string<false>(format_str, h); + return input.begin() + (h.pos() - &*input.begin()); +} + +template <typename... Args> +string_view::iterator scan(string_view input, string_view format_str, + Args&... args) { + return vscan(input, format_str, make_scan_args(args...)); +} +FMT_END_NAMESPACE diff --git a/src/fmt/test/static-export-test/CMakeLists.txt b/src/fmt/test/static-export-test/CMakeLists.txt new file mode 100644 index 000000000..7690d7b5a --- /dev/null +++ b/src/fmt/test/static-export-test/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.1...3.18) + +project(fmt-link CXX) + +set(BUILD_SHARED_LIBS OFF) +set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE) +set(CMAKE_CXX_VISIBILITY_PRESET "hidden") + +# Broken LTO on GCC 4 +if (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5) + set(BROKEN_LTO ON) +endif () + +if (NOT BROKEN_LTO AND CMAKE_VERSION VERSION_GREATER "3.8") + # CMake 3.9+ + include(CheckIPOSupported) + check_ipo_supported(RESULT HAVE_IPO) + if (HAVE_IPO) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + endif () +endif () + +add_subdirectory(../.. fmt) +set_property(TARGET fmt PROPERTY POSITION_INDEPENDENT_CODE ON) + +add_library(library-test SHARED library.cc) +target_link_libraries(library-test PRIVATE fmt::fmt) + +add_executable(exe-test main.cc) +target_link_libraries(exe-test PRIVATE library-test) diff --git a/src/fmt/test/static-export-test/library.cc b/src/fmt/test/static-export-test/library.cc new file mode 100644 index 000000000..fe4801ba5 --- /dev/null +++ b/src/fmt/test/static-export-test/library.cc @@ -0,0 +1,5 @@ +#include <fmt/compile.h> + +__attribute__((visibility("default"))) std::string foo() { + return fmt::format(FMT_COMPILE("foo bar {}"), 4242); +} diff --git a/src/fmt/test/static-export-test/main.cc b/src/fmt/test/static-export-test/main.cc new file mode 100644 index 000000000..38f7999ed --- /dev/null +++ b/src/fmt/test/static-export-test/main.cc @@ -0,0 +1,6 @@ +#include <iostream> +#include <string> + +extern std::string foo(); + +int main() { std::cout << foo() << std::endl; } diff --git a/src/fmt/test/std-test.cc b/src/fmt/test/std-test.cc new file mode 100644 index 000000000..c22b3e38f --- /dev/null +++ b/src/fmt/test/std-test.cc @@ -0,0 +1,79 @@ +// Formatting library for C++ - tests of formatters for standard library types +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/std.h" +#include "fmt/ranges.h" + +#include <string> +#include <vector> + +#include "gtest/gtest.h" + +TEST(std_test, path) { +#ifdef __cpp_lib_filesystem + EXPECT_EQ(fmt::format("{:8}", std::filesystem::path("foo")), "\"foo\" "); + EXPECT_EQ(fmt::format("{}", std::filesystem::path("foo\"bar.txt")), + "\"foo\\\"bar.txt\""); + +# ifdef _WIN32 + // File.txt in Russian. + const wchar_t unicode_path[] = {0x424, 0x430, 0x439, 0x43b, 0x2e, + 0x74, 0x78, 0x74, 0}; + const char unicode_u8path[] = {'"', char(0xd0), char(0xa4), char(0xd0), + char(0xb0), char(0xd0), char(0xb9), char(0xd0), + char(0xbb), '.', 't', 'x', + 't', '"', '\0'}; + EXPECT_EQ(fmt::format("{}", std::filesystem::path(unicode_path)), + unicode_u8path); +# endif +#endif +} + +TEST(ranges_std_test, format_vector_path) { +// Test ambiguity problem described in #2954. +#ifdef __cpp_lib_filesystem + auto p = std::filesystem::path("foo/bar.txt"); + auto c = std::vector<std::string>{"abc", "def"}; + EXPECT_EQ(fmt::format("path={}, range={}", p, c), + "path=\"foo/bar.txt\", range=[\"abc\", \"def\"]"); +#endif +} + +TEST(std_test, thread_id) { + EXPECT_FALSE(fmt::format("{}", std::this_thread::get_id()).empty()); +} + +TEST(std_test, variant) { +#ifdef __cpp_lib_variant + EXPECT_EQ(fmt::format("{}", std::monostate{}), "monostate"); + using V0 = std::variant<int, float, std::string, char>; + V0 v0(42); + V0 v1(1.5f); + V0 v2("hello"); + V0 v3('i'); + EXPECT_EQ(fmt::format("{}", v0), "variant(42)"); + EXPECT_EQ(fmt::format("{}", v1), "variant(1.5)"); + EXPECT_EQ(fmt::format("{}", v2), "variant(\"hello\")"); + EXPECT_EQ(fmt::format("{}", v3), "variant('i')"); + + struct unformattable {}; + EXPECT_FALSE((fmt::is_formattable<unformattable>::value)); + EXPECT_FALSE((fmt::is_formattable<std::variant<unformattable>>::value)); + EXPECT_FALSE((fmt::is_formattable<std::variant<unformattable, int>>::value)); + EXPECT_FALSE((fmt::is_formattable<std::variant<int, unformattable>>::value)); + EXPECT_FALSE( + (fmt::is_formattable<std::variant<unformattable, unformattable>>::value)); + EXPECT_TRUE((fmt::is_formattable<std::variant<int, float>>::value)); + + using V1 = std::variant<std::monostate, std::string, std::string>; + V1 v4{}; + V1 v5{std::in_place_index<1>, "yes, this is variant"}; + + EXPECT_EQ(fmt::format("{}", v4), "variant(monostate)"); + EXPECT_EQ(fmt::format("{}", v5), "variant(\"yes, this is variant\")"); +#endif +} diff --git a/src/fmt/test/test-assert.h b/src/fmt/test/test-assert.h new file mode 100644 index 000000000..ab7b2bf73 --- /dev/null +++ b/src/fmt/test/test-assert.h @@ -0,0 +1,39 @@ +// Formatting library for C++ - test version of FMT_ASSERT +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_TEST_ASSERT_H_ +#define FMT_TEST_ASSERT_H_ + +#include <stdexcept> + +void throw_assertion_failure(const char* message); +#define FMT_ASSERT(condition, message) \ + if (!(condition)) throw_assertion_failure(message); + +#include "gtest/gtest.h" + +class assertion_failure : public std::logic_error { + public: + explicit assertion_failure(const char* message) : std::logic_error(message) {} + + private: + virtual void avoid_weak_vtable(); +}; + +void assertion_failure::avoid_weak_vtable() {} + +// We use a separate function (rather than throw directly from FMT_ASSERT) to +// avoid GCC's -Wterminate warning when FMT_ASSERT is used in a destructor. +inline void throw_assertion_failure(const char* message) { + throw assertion_failure(message); +} + +// Expects an assertion failure. +#define EXPECT_ASSERT(stmt, message) \ + FMT_TEST_THROW_(stmt, assertion_failure, message, GTEST_NONFATAL_FAILURE_) + +#endif // FMT_TEST_ASSERT_H_ diff --git a/src/fmt/test/test-main.cc b/src/fmt/test/test-main.cc new file mode 100644 index 000000000..39d2789d3 --- /dev/null +++ b/src/fmt/test/test-main.cc @@ -0,0 +1,43 @@ +// Formatting library for C++ - test main function. +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include <cstdlib> + +#include "gtest/gtest.h" + +#ifdef _WIN32 +# include <windows.h> +#endif + +#ifdef _MSC_VER +# include <crtdbg.h> +#endif + +int main(int argc, char** argv) { +#ifdef _WIN32 + // Don't display any error dialogs. This also suppresses message boxes + // on assertion failures in MinGW where _set_error_mode/CrtSetReportMode + // doesn't help. + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | + SEM_NOOPENFILEERRORBOX); +#endif +#ifdef _MSC_VER + // Disable message boxes on assertion failures. + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); +#endif + try { + testing::InitGoogleTest(&argc, argv); + testing::FLAGS_gtest_death_test_style = "threadsafe"; + return RUN_ALL_TESTS(); + } catch (...) { + // Catch all exceptions to make Coverity happy. + } + return EXIT_FAILURE; +} diff --git a/src/fmt/test/unicode-test.cc b/src/fmt/test/unicode-test.cc new file mode 100644 index 000000000..e9ea5e5e0 --- /dev/null +++ b/src/fmt/test/unicode-test.cc @@ -0,0 +1,48 @@ +// Formatting library for C++ - Unicode tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include <iomanip> +#include <locale> +#include <vector> + +#include "fmt/chrono.h" +#include "gmock/gmock.h" +#include "util.h" // get_locale + +using testing::Contains; + +TEST(unicode_test, is_utf8) { EXPECT_TRUE(fmt::detail::is_utf8()); } + +TEST(unicode_test, legacy_locale) { + auto loc = get_locale("be_BY.CP1251", "Belarusian_Belarus.1251"); + if (loc == std::locale::classic()) return; + + auto s = std::string(); + try { + s = fmt::format(loc, "Дзень тыдня: {:L}", fmt::weekday(1)); + } catch (const fmt::format_error& e) { + // Formatting can fail due to an unsupported encoding. + fmt::print("Format error: {}\n", e.what()); + return; + } + +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 500 + auto&& os = std::ostringstream(); + os.imbue(loc); + auto tm = std::tm(); + tm.tm_wday = 1; + os << std::put_time(&tm, "%a"); + auto wd = os.str(); + if (wd == "??") { + EXPECT_EQ(s, "Дзень тыдня: ??"); + fmt::print("std::locale gives ?? as a weekday.\n"); + return; + } +#endif + EXPECT_THAT((std::vector<std::string>{"Дзень тыдня: пн", "Дзень тыдня: Пан"}), + Contains(s)); +} diff --git a/src/fmt/test/util.cc b/src/fmt/test/util.cc new file mode 100644 index 000000000..f4cb74ca0 --- /dev/null +++ b/src/fmt/test/util.cc @@ -0,0 +1,46 @@ +// Formatting library for C++ - test utilities +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "util.h" + +#include <cstring> + +const char* const file_content = "Don't panic!"; + +fmt::buffered_file open_buffered_file(FILE** fp) { +#if FMT_USE_FCNTL + fmt::file read_end, write_end; + fmt::file::pipe(read_end, write_end); + write_end.write(file_content, std::strlen(file_content)); + write_end.close(); + fmt::buffered_file f = read_end.fdopen("r"); + if (fp) *fp = f.get(); +#else + fmt::buffered_file f("test-file", "w"); + fputs(file_content, f.get()); + if (fp) *fp = f.get(); +#endif + return f; +} + +std::locale do_get_locale(const char* name) { + try { + return std::locale(name); + } catch (const std::runtime_error&) { + } + return std::locale::classic(); +} + +std::locale get_locale(const char* name, const char* alt_name) { + auto loc = do_get_locale(name); + if (loc == std::locale::classic() && alt_name) { + loc = do_get_locale(alt_name); + } + if (loc == std::locale::classic()) + fmt::print(stderr, "{} locale is missing.\n", name); + return loc; +} diff --git a/src/fmt/test/util.h b/src/fmt/test/util.h new file mode 100644 index 000000000..2e58ad950 --- /dev/null +++ b/src/fmt/test/util.h @@ -0,0 +1,85 @@ +// Formatting library for C++ - test utilities +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include <cstdarg> +#include <cstdio> +#include <locale> +#include <string> + +#ifdef FMT_MODULE_TEST +import fmt; +#else +# include "fmt/os.h" +#endif // FMT_MODULE_TEST + +#ifdef _MSC_VER +# define FMT_VSNPRINTF vsprintf_s +#else +# define FMT_VSNPRINTF vsnprintf +#endif + +template <size_t SIZE> +void safe_sprintf(char (&buffer)[SIZE], const char* format, ...) { + std::va_list args; + va_start(args, format); + FMT_VSNPRINTF(buffer, SIZE, format, args); + va_end(args); +} + +extern const char* const file_content; + +// Opens a buffered file for reading. +fmt::buffered_file open_buffered_file(FILE** fp = nullptr); + +inline FILE* safe_fopen(const char* filename, const char* mode) { +#if defined(_WIN32) && !defined(__MINGW32__) + // Fix MSVC warning about "unsafe" fopen. + FILE* f = nullptr; + errno = fopen_s(&f, filename, mode); + return f; +#else + return std::fopen(filename, mode); +#endif +} + +template <typename Char> class basic_test_string { + private: + std::basic_string<Char> value_; + + static const Char empty[]; + + public: + explicit basic_test_string(const Char* value = empty) : value_(value) {} + + const std::basic_string<Char>& value() const { return value_; } +}; + +template <typename Char> const Char basic_test_string<Char>::empty[] = {0}; + +typedef basic_test_string<char> test_string; +typedef basic_test_string<wchar_t> test_wstring; + +template <typename Char> +std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& os, + const basic_test_string<Char>& s) { + os << s.value(); + return os; +} + +class date { + int year_, month_, day_; + + public: + date(int year, int month, int day) : year_(year), month_(month), day_(day) {} + + int year() const { return year_; } + int month() const { return month_; } + int day() const { return day_; } +}; + +// Returns a locale with the given name if available or classic locale othewise. +std::locale get_locale(const char* name, const char* alt_name = nullptr); diff --git a/src/fmt/test/xchar-test.cc b/src/fmt/test/xchar-test.cc new file mode 100644 index 000000000..290042a63 --- /dev/null +++ b/src/fmt/test/xchar-test.cc @@ -0,0 +1,521 @@ +// Formatting library for C++ - formatting library tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/xchar.h" + +#include <complex> +#include <cwchar> +#include <vector> + +#include "fmt/chrono.h" +#include "fmt/color.h" +#include "fmt/ostream.h" +#include "fmt/ranges.h" +#include "gtest-extra.h" // Contains +#include "util.h" // get_locale + +using fmt::detail::max_value; +using testing::Contains; + +namespace test_ns { +template <typename Char> class test_string { + private: + std::basic_string<Char> s_; + + public: + test_string(const Char* s) : s_(s) {} + const Char* data() const { return s_.data(); } + size_t length() const { return s_.size(); } + operator const Char*() const { return s_.c_str(); } +}; + +template <typename Char> +fmt::basic_string_view<Char> to_string_view(const test_string<Char>& s) { + return {s.data(), s.length()}; +} + +struct non_string {}; +} // namespace test_ns + +template <typename T> class is_string_test : public testing::Test {}; + +using string_char_types = testing::Types<char, wchar_t, char16_t, char32_t>; +TYPED_TEST_SUITE(is_string_test, string_char_types); + +template <typename Char> +struct derived_from_string_view : fmt::basic_string_view<Char> {}; + +TYPED_TEST(is_string_test, is_string) { + EXPECT_TRUE(fmt::detail::is_string<TypeParam*>::value); + EXPECT_TRUE(fmt::detail::is_string<const TypeParam*>::value); + EXPECT_TRUE(fmt::detail::is_string<TypeParam[2]>::value); + EXPECT_TRUE(fmt::detail::is_string<const TypeParam[2]>::value); + EXPECT_TRUE(fmt::detail::is_string<std::basic_string<TypeParam>>::value); + EXPECT_TRUE(fmt::detail::is_string<fmt::basic_string_view<TypeParam>>::value); + EXPECT_TRUE( + fmt::detail::is_string<derived_from_string_view<TypeParam>>::value); + using fmt_string_view = fmt::detail::std_string_view<TypeParam>; + EXPECT_TRUE(std::is_empty<fmt_string_view>::value != + fmt::detail::is_string<fmt_string_view>::value); + EXPECT_TRUE(fmt::detail::is_string<test_ns::test_string<TypeParam>>::value); + EXPECT_FALSE(fmt::detail::is_string<test_ns::non_string>::value); +} + +// std::is_constructible is broken in MSVC until version 2015. +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1900 +struct explicitly_convertible_to_wstring_view { + explicit operator fmt::wstring_view() const { return L"foo"; } +}; + +TEST(xchar_test, format_explicitly_convertible_to_wstring_view) { + // Types explicitly convertible to wstring_view are not formattable by + // default because it may introduce ODR violations. + static_assert( + !fmt::is_formattable<explicitly_convertible_to_wstring_view>::value, ""); +} +#endif + +TEST(xchar_test, format) { + EXPECT_EQ(L"42", fmt::format(L"{}", 42)); + EXPECT_EQ(L"4.2", fmt::format(L"{}", 4.2)); + EXPECT_EQ(L"abc", fmt::format(L"{}", L"abc")); + EXPECT_EQ(L"z", fmt::format(L"{}", L'z')); + EXPECT_THROW(fmt::format(fmt::runtime(L"{:*\x343E}"), 42), fmt::format_error); + EXPECT_EQ(L"true", fmt::format(L"{}", true)); + EXPECT_EQ(L"a", fmt::format(L"{0}", 'a')); + EXPECT_EQ(L"a", fmt::format(L"{0}", L'a')); + EXPECT_EQ(L"Cyrillic letter \x42e", + fmt::format(L"Cyrillic letter {}", L'\x42e')); + EXPECT_EQ(L"abc1", fmt::format(L"{}c{}", L"ab", 1)); +} + +TEST(xchar_test, is_formattable) { + static_assert(!fmt::is_formattable<const wchar_t*>::value, ""); +} + +TEST(xchar_test, compile_time_string) { + EXPECT_EQ(fmt::format(fmt::wformat_string<int>(L"{}"), 42), L"42"); +#if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L + EXPECT_EQ(fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42), L"42"); +#endif +} + +#if FMT_CPLUSPLUS > 201103L +struct custom_char { + int value; + custom_char() = default; + + template <typename T> + constexpr custom_char(T val) : value(static_cast<int>(val)) {} + + operator int() const { return value; } +}; + +int to_ascii(custom_char c) { return c; } + +FMT_BEGIN_NAMESPACE +template <> struct is_char<custom_char> : std::true_type {}; +FMT_END_NAMESPACE + +TEST(xchar_test, format_custom_char) { + const custom_char format[] = {'{', '}', 0}; + auto result = fmt::format(format, custom_char('x')); + EXPECT_EQ(result.size(), 1); + EXPECT_EQ(result[0], custom_char('x')); +} +#endif + +// Convert a char8_t string to std::string. Otherwise GTest will insist on +// inserting `char8_t` NTBS into a `char` stream which is disabled by P1423. +template <typename S> std::string from_u8str(const S& str) { + return std::string(str.begin(), str.end()); +} + +TEST(xchar_test, format_utf8_precision) { + using str_type = std::basic_string<fmt::detail::char8_type>; + auto format = + str_type(reinterpret_cast<const fmt::detail::char8_type*>(u8"{:.4}")); + auto str = str_type(reinterpret_cast<const fmt::detail::char8_type*>( + u8"caf\u00e9s")); // cafés + auto result = fmt::format(format, str); + EXPECT_EQ(fmt::detail::compute_width(result), 4); + EXPECT_EQ(result.size(), 5); + EXPECT_EQ(from_u8str(result), from_u8str(str.substr(0, 5))); +} + +TEST(xchar_test, format_to) { + auto buf = std::vector<wchar_t>(); + fmt::format_to(std::back_inserter(buf), L"{}{}", 42, L'\0'); + EXPECT_STREQ(buf.data(), L"42"); +} + +TEST(xchar_test, vformat_to) { + using wcontext = fmt::wformat_context; + fmt::basic_format_arg<wcontext> warg = fmt::detail::make_arg<wcontext>(42); + auto wargs = fmt::basic_format_args<wcontext>(&warg, 1); + auto w = std::wstring(); + fmt::vformat_to(std::back_inserter(w), L"{}", wargs); + EXPECT_EQ(L"42", w); + w.clear(); + fmt::vformat_to(std::back_inserter(w), FMT_STRING(L"{}"), wargs); + EXPECT_EQ(L"42", w); +} + +TEST(format_test, wide_format_to_n) { + wchar_t buffer[4]; + buffer[3] = L'x'; + auto result = fmt::format_to_n(buffer, 3, L"{}", 12345); + EXPECT_EQ(5u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ(L"123x", fmt::wstring_view(buffer, 4)); + buffer[0] = L'x'; + buffer[1] = L'x'; + buffer[2] = L'x'; + result = fmt::format_to_n(buffer, 3, L"{}", L'A'); + EXPECT_EQ(1u, result.size); + EXPECT_EQ(buffer + 1, result.out); + EXPECT_EQ(L"Axxx", fmt::wstring_view(buffer, 4)); + result = fmt::format_to_n(buffer, 3, L"{}{} ", L'B', L'C'); + EXPECT_EQ(3u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ(L"BC x", fmt::wstring_view(buffer, 4)); +} + +#if FMT_USE_USER_DEFINED_LITERALS +TEST(xchar_test, named_arg_udl) { + using namespace fmt::literals; + auto udl_a = + fmt::format(L"{first}{second}{first}{third}", L"first"_a = L"abra", + L"second"_a = L"cad", L"third"_a = 99); + EXPECT_EQ( + fmt::format(L"{first}{second}{first}{third}", fmt::arg(L"first", L"abra"), + fmt::arg(L"second", L"cad"), fmt::arg(L"third", 99)), + udl_a); +} +#endif // FMT_USE_USER_DEFINED_LITERALS + +TEST(xchar_test, print) { + // Check that the wide print overload compiles. + if (fmt::detail::const_check(false)) fmt::print(L"test"); +} + +TEST(xchar_test, join) { + int v[3] = {1, 2, 3}; + EXPECT_EQ(fmt::format(L"({})", fmt::join(v, v + 3, L", ")), L"(1, 2, 3)"); + auto t = std::tuple<wchar_t, int, float>('a', 1, 2.0f); + EXPECT_EQ(fmt::format(L"({})", fmt::join(t, L", ")), L"(a, 1, 2)"); +} + +enum streamable_enum {}; + +std::wostream& operator<<(std::wostream& os, streamable_enum) { + return os << L"streamable_enum"; +} + +namespace fmt { +template <> +struct formatter<streamable_enum, wchar_t> : basic_ostream_formatter<wchar_t> { +}; +} // namespace fmt + +enum unstreamable_enum {}; +auto format_as(unstreamable_enum e) -> int { return e; } + +TEST(xchar_test, enum) { + EXPECT_EQ(L"streamable_enum", fmt::format(L"{}", streamable_enum())); + EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum())); +} + +struct streamable_and_unformattable {}; + +auto operator<<(std::wostream& os, streamable_and_unformattable) + -> std::wostream& { + return os << L"foo"; +} + +TEST(xchar_test, streamed) { + EXPECT_FALSE(fmt::is_formattable<streamable_and_unformattable>()); + EXPECT_EQ(fmt::format(L"{}", fmt::streamed(streamable_and_unformattable())), + L"foo"); +} + +TEST(xchar_test, sign_not_truncated) { + wchar_t format_str[] = { + L'{', L':', + '+' | static_cast<wchar_t>(1 << fmt::detail::num_bits<char>()), L'}', 0}; + EXPECT_THROW(fmt::format(fmt::runtime(format_str), 42), fmt::format_error); +} + +TEST(xchar_test, chrono) { + auto tm = std::tm(); + tm.tm_year = 116; + tm.tm_mon = 3; + tm.tm_mday = 25; + tm.tm_hour = 11; + tm.tm_min = 22; + tm.tm_sec = 33; + EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm), + "The date is 2016-04-25 11:22:33."); + EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42))); + EXPECT_EQ(fmt::format(L"{:%F}", tm), L"2016-04-25"); + EXPECT_EQ(fmt::format(L"{:%T}", tm), L"11:22:33"); +} + +std::wstring system_wcsftime(const std::wstring& format, const std::tm* timeptr, + std::locale* locptr = nullptr) { + auto loc = locptr ? *locptr : std::locale::classic(); + auto& facet = std::use_facet<std::time_put<wchar_t>>(loc); + std::wostringstream os; + os.imbue(loc); + facet.put(os, os, L' ', timeptr, format.c_str(), + format.c_str() + format.size()); +#ifdef _WIN32 + // Workaround a bug in older versions of Universal CRT. + auto str = os.str(); + if (str == L"-0000") str = L"+0000"; + return str; +#else + return os.str(); +#endif +} + +TEST(chrono_test_wchar, time_point) { + auto t1 = std::chrono::system_clock::now(); + + std::vector<std::wstring> spec_list = { + L"%%", L"%n", L"%t", L"%Y", L"%EY", L"%y", L"%Oy", L"%Ey", L"%C", + L"%EC", L"%G", L"%g", L"%b", L"%h", L"%B", L"%m", L"%Om", L"%U", + L"%OU", L"%W", L"%OW", L"%V", L"%OV", L"%j", L"%d", L"%Od", L"%e", + L"%Oe", L"%a", L"%A", L"%w", L"%Ow", L"%u", L"%Ou", L"%H", L"%OH", + L"%I", L"%OI", L"%M", L"%OM", L"%S", L"%OS", L"%x", L"%Ex", L"%X", + L"%EX", L"%D", L"%F", L"%R", L"%T", L"%p", L"%z", L"%Z"}; +#ifndef _WIN32 + // Disabled on Windows, because these formats is not consistent among + // platforms. + spec_list.insert(spec_list.end(), {L"%c", L"%Ec", L"%r"}); +#elif defined(__MINGW32__) && !defined(_UCRT) + // Only C89 conversion specifiers when using MSVCRT instead of UCRT + spec_list = {L"%%", L"%Y", L"%y", L"%b", L"%B", L"%m", L"%U", + L"%W", L"%j", L"%d", L"%a", L"%A", L"%w", L"%H", + L"%I", L"%M", L"%S", L"%x", L"%X", L"%p", L"%Z"}; +#endif + spec_list.push_back(L"%Y-%m-%d %H:%M:%S"); + + for (const auto& spec : spec_list) { + auto t = std::chrono::system_clock::to_time_t(t1); + auto tm = *std::localtime(&t); + + auto sys_output = system_wcsftime(spec, &tm); + + auto fmt_spec = fmt::format(L"{{:{}}}", spec); + EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1)); + EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm)); + } +} + +TEST(xchar_test, color) { + EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), L"rgb(255,20,30) wide"), + L"\x1b[38;2;255;020;030mrgb(255,20,30) wide\x1b[0m"); +} + +TEST(xchar_test, ostream) { +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 409 + std::wostringstream wos; + fmt::print(wos, L"Don't {}!", L"panic"); + EXPECT_EQ(wos.str(), L"Don't panic!"); +#endif +} + +TEST(xchar_test, format_map) { + auto m = std::map<std::wstring, int>{{L"one", 1}, {L"t\"wo", 2}}; + EXPECT_EQ(fmt::format(L"{}", m), L"{\"one\": 1, \"t\\\"wo\": 2}"); +} + +TEST(xchar_test, escape_string) { + using vec = std::vector<std::wstring>; + EXPECT_EQ(fmt::format(L"{}", vec{L"\n\r\t\"\\"}), L"[\"\\n\\r\\t\\\"\\\\\"]"); + EXPECT_EQ(fmt::format(L"{}", vec{L"понедельник"}), L"[\"понедельник\"]"); +} + +TEST(xchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); } + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +template <typename Char> struct numpunct : std::numpunct<Char> { + protected: + Char do_decimal_point() const override { return '?'; } + std::string do_grouping() const override { return "\03"; } + Char do_thousands_sep() const override { return '~'; } +}; + +template <typename Char> struct no_grouping : std::numpunct<Char> { + protected: + Char do_decimal_point() const override { return '.'; } + std::string do_grouping() const override { return ""; } + Char do_thousands_sep() const override { return ','; } +}; + +template <typename Char> struct special_grouping : std::numpunct<Char> { + protected: + Char do_decimal_point() const override { return '.'; } + std::string do_grouping() const override { return "\03\02"; } + Char do_thousands_sep() const override { return ','; } +}; + +template <typename Char> struct small_grouping : std::numpunct<Char> { + protected: + Char do_decimal_point() const override { return '.'; } + std::string do_grouping() const override { return "\01"; } + Char do_thousands_sep() const override { return ','; } +}; + +TEST(locale_test, localized_double) { + auto loc = std::locale(std::locale(), new numpunct<char>()); + EXPECT_EQ(fmt::format(loc, "{:L}", 1.23), "1?23"); + EXPECT_EQ(fmt::format(loc, "{:Lf}", 1.23), "1?230000"); + EXPECT_EQ(fmt::format(loc, "{:L}", 1234.5), "1~234?5"); + EXPECT_EQ(fmt::format(loc, "{:L}", 12000.0), "12~000"); + EXPECT_EQ(fmt::format(loc, "{:8L}", 1230.0), " 1~230"); +} + +TEST(locale_test, format) { + auto loc = std::locale(std::locale(), new numpunct<char>()); + EXPECT_EQ("1234567", fmt::format(std::locale(), "{:L}", 1234567)); + EXPECT_EQ("1~234~567", fmt::format(loc, "{:L}", 1234567)); + EXPECT_EQ("-1~234~567", fmt::format(loc, "{:L}", -1234567)); + EXPECT_EQ("-256", fmt::format(loc, "{:L}", -256)); + fmt::format_arg_store<fmt::format_context, int> as{1234567}; + EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:L}", fmt::format_args(as))); + auto s = std::string(); + fmt::format_to(std::back_inserter(s), loc, "{:L}", 1234567); + EXPECT_EQ("1~234~567", s); + + auto no_grouping_loc = std::locale(std::locale(), new no_grouping<char>()); + EXPECT_EQ("1234567", fmt::format(no_grouping_loc, "{:L}", 1234567)); + + auto special_grouping_loc = + std::locale(std::locale(), new special_grouping<char>()); + EXPECT_EQ("1,23,45,678", fmt::format(special_grouping_loc, "{:L}", 12345678)); + EXPECT_EQ("12,345", fmt::format(special_grouping_loc, "{:L}", 12345)); + + auto small_grouping_loc = + std::locale(std::locale(), new small_grouping<char>()); + EXPECT_EQ("4,2,9,4,9,6,7,2,9,5", + fmt::format(small_grouping_loc, "{:L}", max_value<uint32_t>())); +} + +TEST(locale_test, format_detault_align) { + auto loc = std::locale({}, new special_grouping<char>()); + EXPECT_EQ(" 12,345", fmt::format(loc, "{:8L}", 12345)); +} + +TEST(locale_test, format_plus) { + auto loc = std::locale({}, new special_grouping<char>()); + EXPECT_EQ("+100", fmt::format(loc, "{:+L}", 100)); +} + +TEST(locale_test, wformat) { + auto loc = std::locale(std::locale(), new numpunct<wchar_t>()); + EXPECT_EQ(L"1234567", fmt::format(std::locale(), L"{:L}", 1234567)); + EXPECT_EQ(L"1~234~567", fmt::format(loc, L"{:L}", 1234567)); + using wcontext = fmt::buffer_context<wchar_t>; + fmt::format_arg_store<wcontext, int> as{1234567}; + EXPECT_EQ(L"1~234~567", + fmt::vformat(loc, L"{:L}", fmt::basic_format_args<wcontext>(as))); + EXPECT_EQ(L"1234567", fmt::format(std::locale("C"), L"{:L}", 1234567)); + + auto no_grouping_loc = std::locale(std::locale(), new no_grouping<wchar_t>()); + EXPECT_EQ(L"1234567", fmt::format(no_grouping_loc, L"{:L}", 1234567)); + + auto special_grouping_loc = + std::locale(std::locale(), new special_grouping<wchar_t>()); + EXPECT_EQ(L"1,23,45,678", + fmt::format(special_grouping_loc, L"{:L}", 12345678)); + + auto small_grouping_loc = + std::locale(std::locale(), new small_grouping<wchar_t>()); + EXPECT_EQ(L"4,2,9,4,9,6,7,2,9,5", + fmt::format(small_grouping_loc, L"{:L}", max_value<uint32_t>())); +} + +TEST(locale_test, double_formatter) { + auto loc = std::locale(std::locale(), new special_grouping<char>()); + auto f = fmt::formatter<int>(); + auto parse_ctx = fmt::format_parse_context("L"); + f.parse(parse_ctx); + char buf[10] = {}; + fmt::basic_format_context<char*, char> format_ctx( + buf, {}, fmt::detail::locale_ref(loc)); + *f.format(12345, format_ctx) = 0; + EXPECT_STREQ("12,345", buf); +} + +FMT_BEGIN_NAMESPACE +template <class charT> struct formatter<std::complex<double>, charT> { + private: + detail::dynamic_format_specs<char> specs_; + + public: + FMT_CONSTEXPR typename basic_format_parse_context<charT>::iterator parse( + basic_format_parse_context<charT>& ctx) { + using handler_type = + detail::dynamic_specs_handler<basic_format_parse_context<charT>>; + 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::parse_float_type_spec(specs_, ctx.error_handler()); + return it; + } + + template <class FormatContext> + typename FormatContext::iterator format(const std::complex<double>& c, + FormatContext& ctx) { + detail::handle_dynamic_spec<detail::precision_checker>( + specs_.precision, specs_.precision_ref, ctx); + auto specs = std::string(); + if (specs_.precision > 0) specs = fmt::format(".{}", specs_.precision); + if (specs_.type == presentation_type::fixed_lower) specs += 'f'; + auto real = fmt::format(ctx.locale().template get<std::locale>(), + fmt::runtime("{:" + specs + "}"), c.real()); + auto imag = fmt::format(ctx.locale().template get<std::locale>(), + fmt::runtime("{:" + specs + "}"), c.imag()); + auto fill_align_width = std::string(); + if (specs_.width > 0) fill_align_width = fmt::format(">{}", specs_.width); + return format_to(ctx.out(), runtime("{:" + fill_align_width + "}"), + c.real() != 0 ? fmt::format("({}+{}i)", real, imag) + : fmt::format("{}i", imag)); + } +}; +FMT_END_NAMESPACE + +TEST(locale_test, complex) { + std::string s = fmt::format("{}", std::complex<double>(1, 2)); + EXPECT_EQ(s, "(1+2i)"); + EXPECT_EQ(fmt::format("{:.2f}", std::complex<double>(1, 2)), "(1.00+2.00i)"); + EXPECT_EQ(fmt::format("{:8}", std::complex<double>(1, 2)), " (1+2i)"); +} + +TEST(locale_test, chrono_weekday) { + auto loc = get_locale("ru_RU.UTF-8", "Russian_Russia.1251"); + auto loc_old = std::locale::global(loc); + auto mon = fmt::weekday(1); + EXPECT_EQ(fmt::format(L"{}", mon), L"Mon"); + if (loc != std::locale::classic()) { + // {L"\x43F\x43D", L"\x41F\x43D", L"\x43F\x43D\x434", L"\x41F\x43D\x434"} + // {L"пн", L"Пн", L"пнд", L"Пнд"} + EXPECT_THAT( + (std::vector<std::wstring>{L"\x43F\x43D", L"\x41F\x43D", + L"\x43F\x43D\x434", L"\x41F\x43D\x434"}), + Contains(fmt::format(loc, L"{:L}", mon))); + } + std::locale::global(loc_old); +} + +TEST(locale_test, sign) { + EXPECT_EQ(fmt::format(std::locale(), L"{:L}", -50), L"-50"); +} + +#endif // FMT_STATIC_THOUSANDS_SEPARATOR |