From b65fa338c9f6b8d240b276c9c75c35d1caf4db62 Mon Sep 17 00:00:00 2001 From: pineappleEA Date: Sat, 30 Jul 2022 11:08:16 +0200 Subject: [PATCH] early-access version 2875 --- CMakeLists.txt | 23 +- README.md | 2 +- .../dynarmic/.github/workflows/x86-64.yml | 102 + externals/dynarmic/CMakeLists.txt | 2 +- .../externals/fmt/.github/issue_template.md | 6 + .../externals/fmt/.github/workflows/doc.yml | 3 + .../externals/fmt/.github/workflows/linux.yml | 15 + .../externals/fmt/.github/workflows/macos.yml | 3 + .../fmt/.github/workflows/windows.yml | 23 +- .../dynarmic/externals/fmt/CMakeLists.txt | 30 +- .../dynarmic/externals/fmt/ChangeLog.rst | 395 ++- externals/dynarmic/externals/fmt/README.rst | 20 +- externals/dynarmic/externals/fmt/doc/api.rst | 141 +- externals/dynarmic/externals/fmt/doc/build.py | 6 +- .../dynarmic/externals/fmt/doc/index.rst | 2 +- .../dynarmic/externals/fmt/doc/syntax.rst | 37 +- .../dynarmic/externals/fmt/include/fmt/args.h | 8 +- .../externals/fmt/include/fmt/chrono.h | 81 +- .../externals/fmt/include/fmt/color.h | 213 +- .../externals/fmt/include/fmt/compile.h | 85 +- .../dynarmic/externals/fmt/include/fmt/core.h | 804 +++--- .../externals/fmt/include/fmt/format-inl.h | 2514 ++++++----------- .../externals/fmt/include/fmt/format.h | 1888 ++++++++++--- .../dynarmic/externals/fmt/include/fmt/os.h | 101 +- .../externals/fmt/include/fmt/ostream.h | 126 +- .../externals/fmt/include/fmt/printf.h | 31 +- .../externals/fmt/include/fmt/ranges.h | 496 ++-- .../dynarmic/externals/fmt/include/fmt/std.h | 176 ++ .../externals/fmt/include/fmt/xchar.h | 58 +- .../dynarmic/externals/fmt/src/format.cc | 109 +- externals/dynarmic/externals/fmt/src/os.cc | 42 +- .../externals/fmt/support/bazel/.bazelversion | 2 +- .../externals/fmt/support/bazel/BUILD.bazel | 7 +- .../externals/fmt/support/cmake/cxx14.cmake | 48 +- .../fmt/support/cmake/fmt-config.cmake.in | 5 +- .../externals/fmt/support/printable.py | 2 +- .../externals/fmt/test/CMakeLists.txt | 24 +- .../externals/fmt/test/chrono-test.cc | 7 + .../dynarmic/externals/fmt/test/color-test.cc | 6 + .../test/compile-error-test/CMakeLists.txt | 42 +- .../externals/fmt/test/compile-fp-test.cc | 2 +- .../externals/fmt/test/compile-test.cc | 20 +- .../dynarmic/externals/fmt/test/core-test.cc | 76 +- .../externals/fmt/test/detect-stdfs.cc | 18 + .../externals/fmt/test/format-impl-test.cc | 196 +- .../externals/fmt/test/format-test.cc | 257 +- .../externals/fmt/test/fuzzing/one-arg.cc | 4 +- .../externals/fmt/test/fuzzing/two-args.cc | 4 +- .../externals/fmt/test/gtest-extra-test.cc | 2 +- .../externals/fmt/test/gtest-extra.cc | 2 +- .../dynarmic/externals/fmt/test/gtest-extra.h | 2 +- .../externals/fmt/test/gtest/CMakeLists.txt | 2 +- .../externals/fmt/test/module-test.cc | 8 - .../dynarmic/externals/fmt/test/os-test.cc | 22 +- .../externals/fmt/test/ostream-test.cc | 115 +- .../externals/fmt/test/posix-mock-test.cc | 85 +- .../externals/fmt/test/printf-test.cc | 6 +- .../externals/fmt/test/ranges-test.cc | 55 +- .../dynarmic/externals/fmt/test/std-test.cc | 84 + .../dynarmic/externals/fmt/test/test-main.cc | 5 +- .../dynarmic/externals/fmt/test/xchar-test.cc | 70 +- .../src/dynarmic/backend/x64/a32_emit_x64.cpp | 82 +- .../dynarmic/backend/x64/a32_interface.cpp | 2 +- .../src/dynarmic/backend/x64/emit_x64.cpp | 44 + .../dynarmic/backend/x64/emit_x64_vector.cpp | 92 +- .../dynarmic/frontend/A32/a32_ir_emitter.cpp | 28 +- .../dynarmic/frontend/A32/a32_ir_emitter.h | 8 +- .../frontend/A32/a32_location_descriptor.cpp | 17 +- .../frontend/A32/a32_location_descriptor.h | 14 +- .../src/dynarmic/frontend/A32/a32_types.cpp | 20 - .../src/dynarmic/frontend/A32/a32_types.h | 39 +- .../A32/translate/impl/data_processing.cpp | 96 +- .../frontend/A32/translate/impl/multiply.cpp | 18 +- .../frontend/A32/translate/impl/thumb16.cpp | 52 +- ...b32_data_processing_modified_immediate.cpp | 36 +- .../impl/thumb32_data_processing_register.cpp | 4 +- ...umb32_data_processing_shifted_register.cpp | 36 +- .../A32/translate/translate_thumb.cpp | 2 +- .../frontend/A64/a64_location_descriptor.cpp | 7 +- .../frontend/A64/a64_location_descriptor.h | 14 +- .../src/dynarmic/frontend/A64/a64_types.cpp | 10 - .../src/dynarmic/frontend/A64/a64_types.h | 21 +- .../dynarmic/src/dynarmic/ir/ir_emitter.cpp | 4 + .../dynarmic/src/dynarmic/ir/ir_emitter.h | 15 +- .../src/dynarmic/ir/location_descriptor.cpp | 7 +- .../src/dynarmic/ir/location_descriptor.h | 13 +- .../src/dynarmic/ir/microinstruction.cpp | 124 +- .../src/dynarmic/ir/microinstruction.h | 14 +- .../dynarmic/src/dynarmic/ir/opcodes.cpp | 4 - externals/dynarmic/src/dynarmic/ir/opcodes.h | 12 +- .../dynarmic/src/dynarmic/ir/opcodes.inc | 8 +- .../ir/opt/a32_constant_memory_reads_pass.cpp | 7 - .../ir/opt/a32_get_set_elimination_pass.cpp | 94 +- .../ir/opt/a64_callback_config_pass.cpp | 2 +- .../dynarmic/src/dynarmic/ir/opt/passes.h | 7 +- .../src/dynarmic/ir/opt/polyfill_pass.cpp | 2 +- externals/dynarmic/src/dynarmic/ir/type.cpp | 4 - externals/dynarmic/src/dynarmic/ir/type.h | 12 +- externals/dynarmic/src/dynarmic/ir/value.cpp | 6 + externals/dynarmic/src/dynarmic/ir/value.h | 2 + externals/dynarmic/tests/A32/fuzz_thumb.cpp | 2 +- .../tests/A32/test_thumb_instructions.cpp | 96 + externals/dynarmic/tests/A64/a64.cpp | 20 + .../dynarmic/tests/A64/fuzz_with_unicorn.cpp | 2 +- externals/dynarmic/tests/print_info.cpp | 4 +- .../tests/unicorn_emu/a32_unicorn.cpp | 14 +- .../tests/unicorn_emu/a64_unicorn.cpp | 12 +- .../renderer/effect/effect_info_base.h | 8 +- src/common/threadsafe_queue.h | 2 +- .../renderer_opengl/gl_shader_cache.cpp | 2 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 2 +- 111 files changed, 5509 insertions(+), 4354 deletions(-) create mode 100755 externals/dynarmic/.github/workflows/x86-64.yml create mode 100755 externals/dynarmic/externals/fmt/.github/issue_template.md create mode 100755 externals/dynarmic/externals/fmt/include/fmt/std.h create mode 100755 externals/dynarmic/externals/fmt/test/detect-stdfs.cc create mode 100755 externals/dynarmic/externals/fmt/test/std-test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 83e6c9dea..eed57cc69 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,9 +38,13 @@ option(YUZU_USE_BUNDLED_OPUS "Compile bundled opus" ON) option(YUZU_TESTS "Compile tests" ON) -option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" OFF) +option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}") if (YUZU_USE_BUNDLED_VCPKG) + if (YUZU_TESTS) + list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests") + endif() + include(${CMAKE_SOURCE_DIR}/externals/vcpkg/scripts/buildsystems/vcpkg.cmake) elseif(NOT "$ENV{VCPKG_TOOLCHAIN_FILE}" STREQUAL "") # Disable manifest mode (use vcpkg classic mode) when using a custom vcpkg installation @@ -165,7 +169,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) # ======================================================================= find_package(fmt 8.0.1 REQUIRED CONFIG) -find_package(lz4 1.8 REQUIRED) find_package(nlohmann_json 3.8 REQUIRED CONFIG) find_package(ZLIB 1.2 REQUIRED) @@ -175,6 +178,12 @@ if (NOT zstd_FOUND) find_package(zstd 1.5 REQUIRED) endif() +# lz4 1.8 is required, but vcpkg's lz4-config.cmake does not have version info +find_package(lz4 CONFIG) +if (NOT lz4_FOUND) + find_package(lz4 1.8 REQUIRED) +endif() + if (YUZU_TESTS) find_package(Catch2 2.13.7 REQUIRED CONFIG) endif() @@ -360,16 +369,10 @@ if (ENABLE_SDL2) endif() endif() -# TODO(lat9nq): Determine what if any of this we still need -# -# Reexport some targets that are named differently when using the upstream CmakeConfig vs the generated Conan config +# Reexport some targets that are named differently when using the upstream CmakeConfig # In order to ALIAS targets to a new name, they first need to be IMPORTED_GLOBAL # Dynarmic checks for target `boost` and so we want to make sure it can find it through our system instead of using their external -if (TARGET Boost::Boost) - set_target_properties(Boost::Boost PROPERTIES IMPORTED_GLOBAL TRUE) - add_library(Boost::boost ALIAS Boost::Boost) - add_library(boost ALIAS Boost::Boost) -elseif (TARGET Boost::boost) +if (TARGET Boost::boost) set_target_properties(Boost::boost PROPERTIES IMPORTED_GLOBAL TRUE) add_library(boost ALIAS Boost::boost) endif() diff --git a/README.md b/README.md index 02381f46f..5bbb3e96e 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 2874. +This is the source code for early-access 2875. ## Legal Notice diff --git a/externals/dynarmic/.github/workflows/x86-64.yml b/externals/dynarmic/.github/workflows/x86-64.yml new file mode 100755 index 000000000..751133ffb --- /dev/null +++ b/externals/dynarmic/.github/workflows/x86-64.yml @@ -0,0 +1,102 @@ +name: x86-64 + +on: [ push, pull_request ] + +env: + BUILD_TYPE: Release + +jobs: + build: + strategy: + matrix: + os: [ windows-latest, ubuntu-latest, macos-latest ] + cpu_detection: [ 0, 1 ] + fail-fast: false + + runs-on: ${{matrix.os}} + + steps: + + - name: Install build dependencies + if: ${{matrix.os == 'ubuntu-latest'}} + run: sudo apt-get install llvm ninja-build + + - name: Install build dependencies + if: ${{matrix.os == 'macos-latest'}} + run: | + brew install llvm ninja + echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH + + - name: Checkout dynarmic repo + uses: actions/checkout@v2 + + - name: Checkout ext-boost repo + uses: actions/checkout@v2 + with: + repository: MerryMage/ext-boost + path: externals/ext-boost + + - name: Checkout unicorn repo + if: ${{matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest'}} + uses: actions/checkout@v2 + with: + repository: MerryMage/unicorn + path: externals/unicorn + + - name: Build unicorn + if: ${{matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest'}} + working-directory: externals/unicorn + run: UNICORN_ARCHS=aarch64,arm ./make.sh + + - name: Configure CMake + if: ${{matrix.os == 'ubuntu-latest'}} + env: + CC: gcc-10 + CXX: g++-10 + CXXFLAGS: -Wp,-D_GLIBCXX_ASSERTIONS + run: > + cmake + -B ${{github.workspace}}/build + -DBoost_INCLUDE_DIRS=${{github.workspace}}/externals/ext-boost + -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + -DDYNARMIC_ENABLE_CPU_FEATURE_DETECTION=${{matrix.cpu_detection}} + -DDYNARMIC_TESTS_USE_UNICORN=1 + -DDYNARMIC_USE_LLVM=1 + -DLIBUNICORN_INCLUDE_DIR=${{github.workspace}}/externals/unicorn/include + -DLIBUNICORN_LIBRARY=${{github.workspace}}/externals/unicorn/libunicorn.a + -G Ninja + + - name: Configure CMake + if: ${{matrix.os == 'macos-latest'}} + run: > + cmake + -B ${{github.workspace}}/build + -DBoost_INCLUDE_DIRS=${{github.workspace}}/externals/ext-boost + -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + -DDYNARMIC_ENABLE_CPU_FEATURE_DETECTION=${{matrix.cpu_detection}} + -DDYNARMIC_TESTS_USE_UNICORN=1 + -DDYNARMIC_USE_LLVM=1 + -DLIBUNICORN_INCLUDE_DIR=${{github.workspace}}/externals/unicorn/include + -DLIBUNICORN_LIBRARY=${{github.workspace}}/externals/unicorn/libunicorn.a + -G Ninja + + - name: Configure CMake + if: ${{matrix.os == 'windows-latest'}} + run: > + cmake + -B ${{github.workspace}}/build + -DBoost_INCLUDE_DIRS=${{github.workspace}}/externals/ext-boost + -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + -DDYNARMIC_ENABLE_CPU_FEATURE_DETECTION=${{matrix.cpu_detection}} + -G "Visual Studio 17 2022" + -A x64 + + - name: Build + working-directory: ${{github.workspace}}/build + run: cmake --build . --config Release + + - name: Test + env: + DYLD_FALLBACK_LIBRARY_PATH: ${{github.workspace}}/externals/unicorn + working-directory: ${{github.workspace}}/build + run: ctest --extra-verbose -C ${{env.BUILD_TYPE}} diff --git a/externals/dynarmic/CMakeLists.txt b/externals/dynarmic/CMakeLists.txt index 04cefa4d2..7fbd9db7e 100755 --- a/externals/dynarmic/CMakeLists.txt +++ b/externals/dynarmic/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.8) -project(dynarmic LANGUAGES C CXX ASM VERSION 6.2.1) +project(dynarmic LANGUAGES C CXX ASM VERSION 6.2.3) # Determine if we're built as a subproject (using add_subdirectory) # or if this is the master project. diff --git a/externals/dynarmic/externals/fmt/.github/issue_template.md b/externals/dynarmic/externals/fmt/.github/issue_template.md new file mode 100755 index 000000000..8e39c5ba7 --- /dev/null +++ b/externals/dynarmic/externals/fmt/.github/issue_template.md @@ -0,0 +1,6 @@ + diff --git a/externals/dynarmic/externals/fmt/.github/workflows/doc.yml b/externals/dynarmic/externals/fmt/.github/workflows/doc.yml index 5f154a92b..47c097746 100755 --- a/externals/dynarmic/externals/fmt/.github/workflows/doc.yml +++ b/externals/dynarmic/externals/fmt/.github/workflows/doc.yml @@ -2,6 +2,9 @@ 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. diff --git a/externals/dynarmic/externals/fmt/.github/workflows/linux.yml b/externals/dynarmic/externals/fmt/.github/workflows/linux.yml index 7f8500ef3..70085f5c6 100755 --- a/externals/dynarmic/externals/fmt/.github/workflows/linux.yml +++ b/externals/dynarmic/externals/fmt/.github/workflows/linux.yml @@ -2,6 +2,9 @@ name: linux on: [push, pull_request] +permissions: + contents: read + jobs: build: runs-on: ${{ matrix.os }} @@ -20,6 +23,11 @@ jobs: 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 @@ -29,6 +37,12 @@ jobs: 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 @@ -52,6 +66,7 @@ jobs: - name: Create Build Environment run: | ${{matrix.install}} + sudo apt update sudo apt install locales-all cmake -E make_directory ${{runner.workspace}}/build diff --git a/externals/dynarmic/externals/fmt/.github/workflows/macos.yml b/externals/dynarmic/externals/fmt/.github/workflows/macos.yml index e300a22bd..24dd59588 100755 --- a/externals/dynarmic/externals/fmt/.github/workflows/macos.yml +++ b/externals/dynarmic/externals/fmt/.github/workflows/macos.yml @@ -2,6 +2,9 @@ name: macos on: [push, pull_request] +permissions: + contents: read + jobs: build: runs-on: macos-10.15 diff --git a/externals/dynarmic/externals/fmt/.github/workflows/windows.yml b/externals/dynarmic/externals/fmt/.github/workflows/windows.yml index ece0372bc..7d0c4a1f0 100755 --- a/externals/dynarmic/externals/fmt/.github/workflows/windows.yml +++ b/externals/dynarmic/externals/fmt/.github/workflows/windows.yml @@ -2,31 +2,34 @@ name: windows on: [push, pull_request] +permissions: + contents: read + jobs: build: runs-on: ${{matrix.os}} strategy: matrix: - # windows-2016 and windows-2019 have MSVC 2017 and 2019 installed - # respectively: https://github.com/actions/virtual-environments. - os: [windows-2016, windows-2019] + # windows-2019 has MSVC 2019 installed; + # windows-2022 has MSVC 2022 installed: + # https://github.com/actions/virtual-environments. + os: [windows-2019] platform: [Win32, x64] build_type: [Debug, Release] standard: [11, 17, 20] include: - - os: windows-2016 + - os: windows-2019 platform: Win32 build_type: Debug shared: -DBUILD_SHARED_LIBS=ON - exclude: - - os: windows-2016 - platform: Win32 - - os: windows-2016 - standard: 17 - - os: windows-2016 + - os: windows-2022 + platform: x64 + build_type: Debug standard: 20 + exclude: - os: windows-2019 standard: 11 + platform: Win32 - os: windows-2019 standard: 20 platform: Win32 diff --git a/externals/dynarmic/externals/fmt/CMakeLists.txt b/externals/dynarmic/externals/fmt/CMakeLists.txt index 3bf80f802..4033ddfc0 100755 --- a/externals/dynarmic/externals/fmt/CMakeLists.txt +++ b/externals/dynarmic/externals/fmt/CMakeLists.txt @@ -125,7 +125,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/support/cmake") include(cxx14) -include(CheckCXXCompilerFlag) include(JoinPaths) list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index) @@ -209,18 +208,6 @@ if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio") ${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*") endif () -set(strtod_l_headers stdlib.h) -if (APPLE) - set(strtod_l_headers ${strtod_l_headers} xlocale.h) -endif () - -include(CheckSymbolExists) -if (WIN32) - check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L) -else () - check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L) -endif () - function(add_headers VAR) set(headers ${${VAR}}) foreach (header ${ARGN}) @@ -231,7 +218,7 @@ 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 locale.h os.h ostream.h printf.h ranges.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) @@ -244,17 +231,6 @@ endif () add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst) add_library(fmt::fmt ALIAS fmt) -if (HAVE_STRTOD_L) - target_compile_definitions(fmt PUBLIC FMT_LOCALE) -endif () - -if (MINGW) - check_cxx_compiler_flag("-Wa,-mbig-obj" FMT_HAS_MBIG_OBJ) - if (${FMT_HAS_MBIG_OBJ}) - target_compile_options(fmt PUBLIC "-Wa,-mbig-obj") - endif() -endif () - if (FMT_WERROR) target_compile_options(fmt PRIVATE ${WERROR_FLAG}) endif () @@ -275,6 +251,7 @@ 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 @@ -352,6 +329,8 @@ if (FMT_INSTALL) 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 @@ -368,7 +347,6 @@ if (FMT_INSTALL) install(FILES $ DESTINATION ${FMT_LIB_DIR} OPTIONAL) - install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt") install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") endif () diff --git a/externals/dynarmic/externals/fmt/ChangeLog.rst b/externals/dynarmic/externals/fmt/ChangeLog.rst index 18b84c7df..966d039f8 100755 --- a/externals/dynarmic/externals/fmt/ChangeLog.rst +++ b/externals/dynarmic/externals/fmt/ChangeLog.rst @@ -1,3 +1,389 @@ +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 `__): + + .. code:: c++ + + #include + #include + + consteval auto compile_time_dtoa(double value) -> std::array { + auto result = std::array(); + fmt::format_to(result.data(), FMT_COMPILE("{}"), value); + return result; + } + + constexpr auto answer = compile_time_itoa(0.42); + + works with the default settings. + +* Improved the implementation of + `Dragonbox `_, the algorithm used for + the default floating-point formatting + (`#2713 `_, + `#2750 `_). + Thanks `@jk-jeon (Junekey 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 `__): + + .. code:: c++ + + #include + + 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 : 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 `__): + + .. code:: c++ + + #include + #include + + 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 `_). + For example (`godbolt `__): + + .. code:: c++ + + #include + #include + + int main() { + auto v = std::variant(42); + fmt::print("{}\n", v); + } + + prints:: + + variant(42) + + Thanks `@jehelset `_. + +* Added experimental ``std::filesystem::path`` formatting support + (`#2865 `_, + `#2902 `_, + `#2917 `_, + `#2918 `_). + For example (`godbolt `__): + + .. code:: c++ + + #include + #include + + int main() { + fmt::print("There is no place like {}.", std::filesystem::path("/home")); + } + + prints:: + + There is no place like "/home". + + Thanks `@phprus (Vladislav Shchapov) `_. + +* Added a ``std::thread::id`` formatter to ``fmt/std.h``. + For example (`godbolt `__): + + .. code:: c++ + + #include + #include + + 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 `_). + For example (`godbolt `__): + + .. code:: c++ + + #include + #include + + 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) `_. + +* Made ``fmt::print`` overload for text styles correctly handle UTF-8 + (`#2681 `_, + `#2701 `_). + Thanks `@AlexGuteniev (Alex Guteniev) `_. + +* Fixed Unicode handling when writing to an ostream. + +* Added support for nested specifiers to range formatting + (`#2673 `_). + For example (`godbolt `__): + + .. code:: c++ + + #include + #include + + int main() { + fmt::print("{::#x}\n", std::vector{10, 20, 30}); + } + + prints ``[0xa, 0x14, 0x1e]``. + + Thanks `@BRevzin (Barry Revzin) `_. + +* Implemented escaping of wide strings in ranges + (`#2904 `_). + Thanks `@phprus (Vladislav Shchapov) `_. + +* Added support for ranges with ``begin`` / ``end`` found via the + argument-dependent lookup + (`#2807 `_). + Thanks `@rbrugo (Riccardo Brugo) `_. + +* Fixed formatting of certain kinds of ranges of ranges + (`#2787 `_). + Thanks `@BRevzin (Barry Revzin) `_. + +* Fixed handling of maps with element types other than ``std::pair`` + (`#2944 `_). + Thanks `@BrukerJWD (Jonathan W) `_. + +* Made tuple formatter enabled only if elements are formattable + (`#2939 `_, + `#2940 `_). + Thanks `@jehelset `_. + +* Made ``fmt::join`` compatible with format string compilation + (`#2719 `_, + `#2720 `_). + Thanks `@phprus (Vladislav Shchapov) `_. + +* Made compile-time checks work with named arguments of custom types and + ``std::ostream`` ``print`` overloads + (`#2816 `_, + `#2817 `_, + `#2819 `_). + Thanks `@timsong-cpp `_. + +* Removed ``make_args_checked`` because it is no longer needed for compile-time + checks (`#2760 `_). + Thanks `@phprus (Vladislav Shchapov) `_. + +* 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 `_). + Thanks `@arogge (Andreas Rogge) `_. + +* Removed a redundant buffer resize when formatting to ``std::ostream`` + (`#2842 `_, + `#2843 `_). + Thanks `@jcelerier (Jean-Michaël Celerier) `_. + +* Made precision computation for strings consistent with width + (`#2888 `_). + +* Fixed handling of locale separators in floating point formatting + (`#2830 `_). + +* Made sign specifiers work with ``__int128_t`` + (`#2773 `_). + +* Improved support for systems such as CHERI with extra data stored in pointers + (`#2932 `_). + Thanks `@davidchisnall (David Chisnall) `_. + +* Improved documentation + (`#2706 `_, + `#2712 `_, + `#2789 `_, + `#2803 `_, + `#2805 `_, + `#2815 `_, + `#2924 `_). + Thanks `@BRevzin (Barry Revzin) `_, + `@Pokechu22 `_, + `@setoye (Alta) `_, + `@rtobar `_, + `@rbrugo (Riccardo Brugo) `_, + `@anoonD (cre) `_, + `@leha-bot (Alex) `_. + +* Improved build configuration + (`#2766 `_, + `#2772 `_, + `#2836 `_, + `#2852 `_, + `#2907 `_, + `#2913 `_, + `#2914 `_). + Thanks `@kambala-decapitator (Andrey Filipenkov) + `_, + `@mattiasljungstrom (Mattias Ljungström) + `_, + `@kieselnb (Nick Kiesel) `_, + `@nathannaveen `_, + `@Vertexwahn `_. + +* Fixed various warnings and compilation issues + (`#2408 `_, + `#2507 `_, + `#2697 `_, + `#2715 `_, + `#2717 `_, + `#2722 `_, + `#2724 `_, + `#2725 `_, + `#2726 `_, + `#2728 `_, + `#2732 `_, + `#2738 `_, + `#2742 `_, + `#2744 `_, + `#2745 `_, + `#2746 `_, + `#2754 `_, + `#2755 `_, + `#2757 `_, + `#2758 `_, + `#2761 `_, + `#2762 `_, + `#2763 `_, + `#2765 `_, + `#2769 `_, + `#2770 `_, + `#2771 `_, + `#2777 `_, + `#2779 `_, + `#2782 `_, + `#2783 `_, + `#2794 `_, + `#2796 `_, + `#2797 `_, + `#2801 `_, + `#2802 `_, + `#2808 `_, + `#2818 `_, + `#2819 `_, + `#2829 `_, + `#2835 `_, + `#2848 `_, + `#2860 `_, + `#2861 `_, + `#2882 `_, + `#2886 `_, + `#2891 `_, + `#2892 `_, + `#2895 `_, + `#2896 `_, + `#2903 `_, + `#2906 `_, + `#2908 `_, + `#2909 `_, + `#2920 `_, + `#2922 `_, + `#2927 `_, + `#2929 `_, + `#2936 `_, + `#2937 `_, + `#2938 `_, + `#2951 `_, + `#2954 `_, + `#2957 `_, + `#2958 `_, + `#2960 `_). + Thanks `@matrackif `_ + `@Tobi823 (Tobias Hellmann) `_, + `@ivan-volnov (Ivan Volnov) `_, + `@VasiliPupkin256 `_, + `@federico-busato (Federico) `_, + `@barcharcraz (Charlie Barto) `_, + `@jk-jeon (Junekey Jeon) `_, + `@HazardyKnusperkeks (Björn Schäpers) + `_, + `@dalboris (Boris Dalstein) `_, + `@seanm (Sean McBride) `_, + `@gsjaardema (Greg Sjaardema) `_, + `@timsong-cpp `_, + `@seanm (Sean McBride) `_, + `@frithrah `_, + `@chronoxor (Ivan Shynkarenka) `_, + `@Agga `_, + `@madmaxoft (Mattes D) `_, + `@JurajX (Juraj) `_, + `@phprus (Vladislav Shchapov) `_, + `@Dani-Hub (Daniel Krügler) `_. + 8.1.1 - 2022-01-06 ------------------ @@ -6,7 +392,7 @@ `#2696 `_). Thanks `@saraedum (Julian Rüth) `_. -* Fixed chorno formatting on big endian systems +* Fixed chrono formatting on big endian systems (`#2698 `_, `#2699 `_). Thanks `@phprus (Vladislav Shchapov) `_ and @@ -148,6 +534,10 @@ [" aan"] +* Added an experimental ``?`` specifier for escaping strings. + (`#2674 `_). + Thanks `@BRevzin (Barry Revzin) `_. + * Switched to JSON-like representation of maps and sets for consistency with Python's ``str.format``. For example (`godbolt `__): @@ -367,7 +757,8 @@ `@alexezeder (Alexey Ochapov) `_, `@andrewcorrigan (Andrew Corrigan) `_, `@lucpelletier `_, - `@HazardyKnusperkeks (Björn Schäpers) `_. + `@HazardyKnusperkeks (Björn Schäpers) + `_. 8.0.1 - 2021-07-02 ------------------ diff --git a/externals/dynarmic/externals/fmt/README.rst b/externals/dynarmic/externals/fmt/README.rst index 394f28d97..4a3addf08 100755 --- a/externals/dynarmic/externals/fmt/README.rst +++ b/externals/dynarmic/externals/fmt/README.rst @@ -1,5 +1,7 @@ -{fmt} -===== +.. 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 @@ -26,12 +28,13 @@ **{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 the BYSOL -Foundation that helps victims of political repressions in Belarus: -https://bysol.org/en/bs/general/. +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 `__ +`Cheat Sheets `__ + Q&A: ask questions on `StackOverflow with the tag fmt `_. @@ -123,7 +126,7 @@ Output:: Default format: 42s 100ms strftime-like format: 03:15:30 -**Print a container** (`run `_) +**Print a container** (`run `_) .. code:: c++ @@ -341,9 +344,12 @@ Projects using this library * `Folly `_: Facebook open-source library +* `GemRB `_: a portable open-source implementation of + Bioware’s Infinity Engine + * `Grand Mountain Adventure `_: - A beautiful open-world ski & snowboarding game + a beautiful open-world ski & snowboarding game * `HarpyWar/pvpgn `_: Player vs Player Gaming Network with tweaks diff --git a/externals/dynarmic/externals/fmt/doc/api.rst b/externals/dynarmic/externals/fmt/doc/api.rst index 93fb3faf9..f5bc65269 100755 --- a/externals/dynarmic/externals/fmt/doc/api.rst +++ b/externals/dynarmic/externals/fmt/doc/api.rst @@ -12,6 +12,7 @@ The {fmt} library API consists of the following parts: formatting functions and locale support * :ref:`fmt/ranges.h `: formatting of ranges and tuples * :ref:`fmt/chrono.h `: date and time formatting +* :ref:`fmt/std.h `: formatters for standard library types * :ref:`fmt/compile.h `: format string compilation * :ref:`fmt/color.h `: terminal color and text style * :ref:`fmt/os.h `: system APIs @@ -66,7 +67,7 @@ checked at compile time in C++20. To pass a runtime format string wrap it in .. doxygenfunction:: print(std::FILE *f, format_string fmt, T&&... args) .. doxygenfunction:: vprint(std::FILE *f, string_view fmt, format_args args) -Compile-time Format String Checks +Compile-Time Format String Checks --------------------------------- Compile-time checks are enabled when using ``FMT_STRING``. They support built-in @@ -113,8 +114,7 @@ binary footprint, for example (https://godbolt.org/z/oba4Mc): template void log(const char* file, int line, const S& format, Args&&... args) { - vlog(file, line, format, - fmt::make_args_checked(format, args...)); + vlog(file, line, format, fmt::make_format_args(args...)); } #define MY_LOG(format, ...) \ @@ -125,8 +125,6 @@ binary footprint, for example (https://godbolt.org/z/oba4Mc): 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_args_checked(const S&, const remove_reference_t&...) - .. doxygenfunction:: fmt::make_format_args(const Args&...) .. doxygenclass:: fmt::format_arg_store @@ -143,6 +141,9 @@ times and reduces binary code size compared to a fully parameterized version. .. doxygenclass:: fmt::basic_format_arg :members: +.. doxygenclass:: fmt::basic_format_parse_context + :members: + .. doxygenclass:: fmt::basic_format_context :members: @@ -179,9 +180,15 @@ functions and locale support. .. _udt: -Formatting User-defined Types +Formatting User-Defined Types ----------------------------- +The {fmt} library provides formatters for many standard C++ types. +See :ref:`fmt/ranges.h ` for ranges and tuples including standard +containers such as ``std::vector``, :ref:`fmt/chrono.h ` for date +and time formatting and :ref:`fmt/std.h ` for path and variant +formatting. + To make a user-defined type formattable, specialize the ``formatter`` struct template and implement ``parse`` and ``format`` methods:: @@ -207,6 +214,10 @@ template and implement ``parse`` and ``format`` methods:: // 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(); @@ -222,11 +233,11 @@ template and implement ``parse`` and ``format`` methods:: // Formats the point p using the parsed format specification (presentation) // stored in this formatter. template - auto format(const point& p, FormatContext& ctx) -> decltype(ctx.out()) { + auto format(const point& p, FormatContext& ctx) const -> decltype(ctx.out()) { // ctx.out() is an output iterator to write to. return presentation == 'f' - ? format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y) - : format_to(ctx.out(), "({:.1e}, {:.1e})", p.x, p.y); + ? fmt::format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y) + : fmt::format_to(ctx.out(), "({:.1e}, {:.1e})", p.x, p.y); } }; @@ -244,7 +255,7 @@ example:: template <> struct fmt::formatter: formatter { // parse is inherited from formatter. template - auto format(color c, FormatContext& ctx) { + auto format(color c, FormatContext& ctx) const { string_view name = "unknown"; switch (c) { case color::red: name = "red"; break; @@ -282,7 +293,7 @@ You can also write a formatter for a hierarchy of classes:: struct fmt::formatter::value, char>> : fmt::formatter { template - auto format(const A& a, FormatCtx& ctx) { + auto format(const A& a, FormatCtx& ctx) const { return fmt::formatter::format(a.name(), ctx); } }; @@ -297,17 +308,32 @@ If a type provides both a ``formatter`` specialization and an implicit conversion to a formattable type, the specialization takes precedence over the conversion. -.. doxygenclass:: fmt::basic_format_parse_context - :members: +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. -Literal-based API +Example (https://godbolt.org/z/r7vvGE1v7):: + + #include + + 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""_format(const char *s, size_t n) -> detail::udl_formatter - -.. doxygenfunction:: operator""_a(const char *s, size_t) -> detail::udl_arg +.. doxygenfunction:: operator""_a() Utilities --------- @@ -316,9 +342,9 @@ Utilities .. doxygenfunction:: fmt::ptr(const std::unique_ptr &p) -> const void* .. doxygenfunction:: fmt::ptr(const std::shared_ptr &p) -> const void* -.. doxygenfunction:: fmt::to_string(const T &value) -> std::string +.. doxygenfunction:: fmt::underlying(Enum e) -> typename std::underlying_type::type -.. doxygenfunction:: fmt::to_string_view(const Char *s) -> basic_string_view +.. doxygenfunction:: fmt::to_string(const T &value) -> std::string .. doxygenfunction:: fmt::join(Range &&range, string_view sep) -> join_view, detail::sentinel_t> @@ -381,8 +407,8 @@ non-default floating-point formatting that occasionally falls back on .. _ranges-api: -Ranges and Tuple Formatting -=========================== +Range and Tuple Formatting +========================== The library also supports convenient formatting of ranges and tuples:: @@ -441,24 +467,56 @@ The format syntax is described in :ref:`chrono-specs`. .. doxygenfunction:: gmtime(std::time_t time) +.. _std-api: + +Standard Library Types Formatting +================================= + +``fmt/std.h`` provides formatters for: + +* `std::filesystem::path `_ +* `std::thread::id `_ +* `std::monostate `_ +* `std::variant `_ + +Formatting Variants +------------------- + +A ``std::variant`` is only formattable if every variant alternative is formattable, and requires the +``__cpp_lib_variant`` `library feature `_. + +**Example**:: + + #include + + std::variant v0{'x'}; + // Prints "variant('x')" + fmt::print("{}", v0); + + std::variant v1; + // Prints "variant(monostate)" + .. _compile-api: -Format string compilation +Format String Compilation ========================= -``fmt/compile.h`` provides format string compilation support when using -``FMT_COMPILE``. Format strings 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. +``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 +Terminal Color and Text Style ============================= ``fmt/color.h`` provides support for terminal color and text style output. @@ -469,6 +527,8 @@ Terminal color and text style .. doxygenfunction:: bg(detail::color_type) +.. doxygenfunction:: styled(const T& value, text_style ts) + .. _os-api: System APIs @@ -486,27 +546,28 @@ System APIs ======================== ``fmt/ostream.h`` provides ``std::ostream`` support including formatting of -user-defined types that have an overloaded insertion operator (``operator<<``):: +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 - class date { - int year_, month_, day_; - public: - date(int year, int month, int day): year_(year), month_(month), day_(day) {} + struct date { + int year, month, day; friend std::ostream& operator<<(std::ostream& os, const date& d) { - return os << d.year_ << '-' << d.month_ << '-' << d.day_; + return os << d.year << '-' << d.month << '-' << d.day; } }; - std::string s = fmt::format("The date is {}", date(2012, 12, 9)); + template <> struct fmt::formatter : ostream_formatter {}; + + std::string s = fmt::format("The date is {}", date{2012, 12, 9}); // s == "The date is 2012-12-9" -{fmt} only supports insertion operators that are defined in the same namespaces -as the types they format and can be found with the argument-dependent lookup. +.. doxygenfunction:: streamed(const T &) -.. doxygenfunction:: print(std::basic_ostream &os, const S &format_str, Args&&... args) +.. doxygenfunction:: print(std::ostream &os, format_string fmt, T&&... args) .. _printf-api: diff --git a/externals/dynarmic/externals/fmt/doc/build.py b/externals/dynarmic/externals/fmt/doc/build.py index ae1ccfc8f..1e37be301 100755 --- a/externals/dynarmic/externals/fmt/doc/build.py +++ b/externals/dynarmic/externals/fmt/doc/build.py @@ -4,7 +4,7 @@ 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'] +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'] class Pip: def __init__(self, venv_dir): @@ -28,6 +28,9 @@ def create_build_env(venv_dir='virtualenv'): 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') @@ -65,6 +68,7 @@ def build_docs(version='dev', **kwargs): 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=}}" \ diff --git a/externals/dynarmic/externals/fmt/doc/index.rst b/externals/dynarmic/externals/fmt/doc/index.rst index 92221225a..d5c4fa5f5 100755 --- a/externals/dynarmic/externals/fmt/doc/index.rst +++ b/externals/dynarmic/externals/fmt/doc/index.rst @@ -101,7 +101,7 @@ The code format(FMT_STRING("The answer is {:d}"), "forty-two"); reports a compile-time error on compilers that support relaxed ``constexpr``. -See `here `_ for details. +See `here `_ for details. The following code diff --git a/externals/dynarmic/externals/fmt/doc/syntax.rst b/externals/dynarmic/externals/fmt/doc/syntax.rst index 9d3cb57cf..77d8035ea 100755 --- a/externals/dynarmic/externals/fmt/doc/syntax.rst +++ b/externals/dynarmic/externals/fmt/doc/syntax.rst @@ -304,7 +304,8 @@ The available presentation types for pointers are: Chrono Format Specifications ============================ -Format specifications for chrono types have the following syntax: +Format specifications for chrono types and ``std::tm`` have the following +syntax: .. productionlist:: sf chrono_format_spec: [[`fill`]`align`][`width`]["." `precision`][`chrono_specs`] @@ -345,12 +346,38 @@ points are: | | command ``%OS`` produces the locale's alternative representation. | +---------+--------------------------------------------------------------------+ -Specifiers that have a calendaric component such as `'d'` (the day of month) +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. -``std::tm`` uses the system's `strftime -`_ so refer to its -documentation for details on supported conversion specifiers. +.. 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: diff --git a/externals/dynarmic/externals/fmt/include/fmt/args.h b/externals/dynarmic/externals/fmt/include/fmt/args.h index 9a8e4ed2c..a3966d140 100755 --- a/externals/dynarmic/externals/fmt/include/fmt/args.h +++ b/externals/dynarmic/externals/fmt/include/fmt/args.h @@ -95,10 +95,10 @@ class dynamic_format_arg_store }; template - using stored_type = conditional_t::value && - !has_formatter::value && - !detail::is_reference_wrapper::value, - std::basic_string, T>; + using stored_type = conditional_t< + std::is_convertible>::value && + !detail::is_reference_wrapper::value, + std::basic_string, T>; // Storage of basic_format_arg must be contiguous. std::vector> data_; diff --git a/externals/dynarmic/externals/fmt/include/fmt/chrono.h b/externals/dynarmic/externals/fmt/include/fmt/chrono.h index 682efd8d2..c3c52bf59 100755 --- a/externals/dynarmic/externals/fmt/include/fmt/chrono.h +++ b/externals/dynarmic/externals/fmt/include/fmt/chrono.h @@ -10,6 +10,8 @@ #include #include +#include // std::isfinite +#include // std::memcpy #include #include #include @@ -321,14 +323,13 @@ constexpr const size_t codecvt_result::max_size; template void write_codecvt(codecvt_result& out, string_view in_buf, const std::locale& loc) { - using codecvt = std::codecvt; #if FMT_CLANG_VERSION # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdeprecated" - auto& f = std::use_facet(loc); + auto& f = std::use_facet>(loc); # pragma clang diagnostic pop #else - auto& f = std::use_facet(loc); + auto& f = std::use_facet>(loc); #endif auto mb = std::mbstate_t(); const char* from_next = nullptr; @@ -344,7 +345,7 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) 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_VER != 0 || \ +#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. @@ -468,7 +469,7 @@ inline std::tm localtime(std::time_t time) { bool fallback(int res) { return res == 0; } -#if !FMT_MSC_VER +#if !FMT_MSC_VERSION bool fallback(detail::null<>) { using namespace fmt::detail; std::tm* tm = std::localtime(&time_); @@ -514,7 +515,7 @@ inline std::tm gmtime(std::time_t time) { bool fallback(int res) { return res == 0; } -#if !FMT_MSC_VER +#if !FMT_MSC_VERSION bool fallback(detail::null<>) { std::tm* tm = std::gmtime(&time_); if (tm) tm_ = *tm; @@ -562,10 +563,10 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b, constexpr const size_t len = 8; if (const_check(is_big_endian())) { char tmp[len]; - memcpy(tmp, &digits, len); + std::memcpy(tmp, &digits, len); std::reverse_copy(tmp, tmp + len, buf); } else { - memcpy(buf, &digits, len); + std::memcpy(buf, &digits, len); } } @@ -1214,7 +1215,7 @@ template class tm_writer { char buf[10]; size_t offset = 0; if (year >= 0 && year < 10000) { - copy2(buf, digits2(to_unsigned(year / 100))); + copy2(buf, digits2(static_cast(year / 100))); } else { offset = 4; write_year_extended(year); @@ -1387,15 +1388,6 @@ struct chrono_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_duration_unit() {} }; -template ::value)> -inline bool isnan(T) { - return false; -} -template ::value)> -inline bool isnan(T value) { - return std::isnan(value); -} - template ::value)> inline bool isfinite(T) { return true; @@ -1470,14 +1462,22 @@ inline std::chrono::duration get_milliseconds( #endif } -// Returns the number of fractional digits in the range [0, 18] according to the +// 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. -constexpr int count_fractional_digits(long long num, long long den, int n = 0) { - return num % den == 0 - ? n - : (n > 18 ? 6 : count_fractional_digits(num * 10, den, n + 1)); -} +template () / 10)> +struct count_fractional_digits { + static constexpr int value = + Num % Den == 0 ? N : count_fractional_digits::value; +}; + +// Base case that doesn't instantiate any more templates +// in order to avoid overflow. +template +struct count_fractional_digits { + 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); @@ -1663,9 +1663,11 @@ struct chrono_formatter { out = format_decimal(out, n, num_digits).end; } - template void write_fractional_seconds(Duration d) { + template void write_fractional_seconds(Duration d) { + FMT_ASSERT(!std::is_floating_point::value, ""); constexpr auto num_fractional_digits = - count_fractional_digits(Duration::period::num, Duration::period::den); + count_fractional_digits::value; using subsecond_precision = std::chrono::duration< typename std::common_type::value) { *out++ = '.'; - // Don't convert long double to integer seconds to avoid overflow. - using sec = conditional_t< - std::is_same::value, - std::chrono::duration, std::chrono::seconds>; - auto fractional = detail::abs(d) - std::chrono::duration_cast(d); - const auto subseconds = + auto fractional = + detail::abs(d) - std::chrono::duration_cast(d); + auto subseconds = std::chrono::treat_as_floating_point< typename subsecond_precision::rep>::value ? fractional.count() @@ -1770,8 +1769,22 @@ struct chrono_formatter { if (handle_nan_inf()) return; if (ns == numeric_system::standard) { - write(second(), 2); - write_fractional_seconds(std::chrono::duration{val}); + if (std::is_floating_point::value) { + constexpr auto num_fractional_digits = + count_fractional_digits::value; + auto buf = memory_buffer(); + format_to(std::back_inserter(buf), runtime("{:.{}f}"), + std::fmod(val * static_cast(Period::num) / + static_cast(Period::den), + 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(val)); + } return; } auto time = tm(); diff --git a/externals/dynarmic/externals/fmt/include/fmt/color.h b/externals/dynarmic/externals/fmt/include/fmt/color.h index dfbe48293..e6212d249 100755 --- a/externals/dynarmic/externals/fmt/include/fmt/color.h +++ b/externals/dynarmic/externals/fmt/include/fmt/color.h @@ -10,13 +10,6 @@ #include "format.h" -// __declspec(deprecated) is broken in some MSVC versions. -#if FMT_MSC_VER -# define FMT_DEPRECATED_NONMSVC -#else -# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED -#endif - FMT_BEGIN_NAMESPACE FMT_MODULE_EXPORT_BEGIN @@ -214,17 +207,16 @@ FMT_BEGIN_DETAIL_NAMESPACE // color is a struct of either a rgb color or a terminal color. struct color_type { - FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} - FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), - value{} { + 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(rgb_color); } - FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} { + FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} { value.rgb_color = (static_cast(rgb_color.r) << 16) | (static_cast(rgb_color.g) << 8) | rgb_color.b; } - FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), - value{} { + FMT_CONSTEXPR color_type(terminal_color term_color) noexcept + : is_rgb(), value{} { value.term_color = static_cast(term_color); } bool is_rgb; @@ -239,10 +231,8 @@ 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()) FMT_NOEXCEPT - : set_foreground_color(), - set_background_color(), - ems(em) {} + 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) { @@ -273,44 +263,32 @@ class text_style { return lhs |= rhs; } - FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=( - const text_style& rhs) { - return and_assign(rhs); - } - - FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style - operator&(text_style lhs, const text_style& rhs) { - return lhs.and_assign(rhs); - } - - FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { + FMT_CONSTEXPR bool has_foreground() const noexcept { return set_foreground_color; } - FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { + FMT_CONSTEXPR bool has_background() const noexcept { return set_background_color; } - FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { + FMT_CONSTEXPR bool has_emphasis() const noexcept { return static_cast(ems) != 0; } - FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT { + 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 FMT_NOEXCEPT { + 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 FMT_NOEXCEPT { + 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) FMT_NOEXCEPT - : set_foreground_color(), - set_background_color(), - ems() { + detail::color_type text_color) noexcept + : set_foreground_color(), set_background_color(), ems() { if (is_foreground) { foreground_color = text_color; set_foreground_color = true; @@ -320,36 +298,9 @@ class text_style { } } - // DEPRECATED! - FMT_CONSTEXPR text_style& and_assign(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 AND a terminal color")); - foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; - } + friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept; - 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 AND a terminal color")); - background_color.value.rgb_color &= rhs.background_color.value.rgb_color; - } - - ems = static_cast(static_cast(ems) & - static_cast(rhs.ems)); - return *this; - } - - friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground) - FMT_NOEXCEPT; - - friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background) - FMT_NOEXCEPT; + friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept; detail::color_type foreground_color; detail::color_type background_color; @@ -359,17 +310,16 @@ class text_style { }; /** Creates a text style from the foreground (text) color. */ -FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) FMT_NOEXCEPT { +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) FMT_NOEXCEPT { +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) FMT_NOEXCEPT { +FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept { return text_style(lhs) | rhs; } @@ -377,7 +327,7 @@ FMT_BEGIN_DETAIL_NAMESPACE template struct ansi_color_escape { FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, - const char* esc) FMT_NOEXCEPT { + const char* esc) noexcept { // If we have a terminal color, we need to output another escape code // sequence. if (!text_color.is_rgb) { @@ -412,7 +362,7 @@ template struct ansi_color_escape { to_esc(color.b, buffer + 15, 'm'); buffer[19] = static_cast(0); } - FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { + 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; @@ -433,10 +383,10 @@ template struct ansi_color_escape { } buffer[index++] = static_cast(0); } - FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } + FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } - FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } - FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT { + FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; } + FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept { return buffer + std::char_traits::length(buffer); } @@ -445,59 +395,64 @@ template struct ansi_color_escape { Char buffer[7u + 3u * num_emphases + 1u]; static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, - char delimiter) FMT_NOEXCEPT { + char delimiter) noexcept { out[0] = static_cast('0' + c / 100); out[1] = static_cast('0' + c / 10 % 10); out[2] = static_cast('0' + c % 10); out[3] = static_cast(delimiter); } - static FMT_CONSTEXPR bool has_emphasis(emphasis em, - emphasis mask) FMT_NOEXCEPT { + static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept { return static_cast(em) & static_cast(mask); } }; template FMT_CONSTEXPR ansi_color_escape make_foreground_color( - detail::color_type foreground) FMT_NOEXCEPT { + detail::color_type foreground) noexcept { return ansi_color_escape(foreground, "\x1b[38;2;"); } template FMT_CONSTEXPR ansi_color_escape make_background_color( - detail::color_type background) FMT_NOEXCEPT { + detail::color_type background) noexcept { return ansi_color_escape(background, "\x1b[48;2;"); } template -FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) FMT_NOEXCEPT { +FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) noexcept { return ansi_color_escape(em); } -template -inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { - std::fputs(chars, stream); +template 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(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { - std::fputws(chars, stream); +template <> inline void fputs(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 inline void reset_color(FILE* stream) FMT_NOEXCEPT { +template inline void reset_color(FILE* stream) { fputs("\x1b[0m", stream); } -template <> inline void reset_color(FILE* stream) FMT_NOEXCEPT { +template <> inline void reset_color(FILE* stream) { fputs(L"\x1b[0m", stream); } -template -inline void reset_color(buffer& buffer) FMT_NOEXCEPT { +template inline void reset_color(buffer& buffer) { auto reset_color = string_view("\x1b[0m"); buffer.append(reset_color.begin(), reset_color.end()); } +template struct styled_arg { + const T& value; + text_style style; +}; + template void vformat_to(buffer& buf, const text_style& ts, basic_string_view format_str, @@ -528,9 +483,13 @@ template > void vprint(std::FILE* f, const text_style& ts, const S& format, basic_format_args>> args) { basic_memory_buffer buf; - detail::vformat_to(buf, ts, to_string_view(format), args); - buf.push_back(Char(0)); - detail::fputs(buf.data(), f); + detail::vformat_to(buf, ts, detail::to_string_view(format), args); + if (detail::is_utf8()) { + detail::print(f, basic_string_view(buf.begin(), buf.size())); + } else { + buf.push_back(Char(0)); + detail::fputs(buf.data(), f); + } } /** @@ -549,7 +508,7 @@ template (format_str, args...)); + fmt::make_format_args>>(args...)); } /** @@ -574,7 +533,7 @@ inline std::basic_string vformat( const text_style& ts, const S& format_str, basic_format_args>> args) { basic_memory_buffer buf; - detail::vformat_to(buf, ts, to_string_view(format_str), args); + detail::vformat_to(buf, ts, detail::to_string_view(format_str), args); return fmt::to_string(buf); } @@ -593,8 +552,8 @@ inline std::basic_string vformat( template > inline std::basic_string format(const text_style& ts, const S& format_str, const Args&... args) { - return fmt::vformat(ts, to_string_view(format_str), - fmt::make_args_checked(format_str, args...)); + return fmt::vformat(ts, detail::to_string_view(format_str), + fmt::make_format_args>(args...)); } /** @@ -628,8 +587,62 @@ template typename std::enable_if::type { - return vformat_to(out, ts, to_string_view(format_str), - fmt::make_args_checked(format_str, args...)); + return vformat_to(out, ts, detail::to_string_view(format_str), + fmt::make_format_args>>(args...)); +} + +template +struct formatter, Char> : formatter { + template + auto format(const detail::styled_arg& 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(ts.get_emphasis()); + out = std::copy(emphasis.begin(), emphasis.end(), out); + } + if (ts.has_foreground()) { + has_style = true; + auto foreground = + detail::make_foreground_color(ts.get_foreground()); + out = std::copy(foreground.begin(), foreground.end(), out); + } + if (ts.has_background()) { + has_style = true; + auto background = + detail::make_background_color(ts.get_background()); + out = std::copy(background.begin(), background.end(), out); + } + out = formatter::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: {s:.2f} seconds", + fmt::styled(1.23, fmt::fg(fmt::color::green) | + fmt::bg(fmt::color::blue))); + \endrst + */ +template +FMT_CONSTEXPR auto styled(const T& value, text_style ts) + -> detail::styled_arg> { + return detail::styled_arg>{value, ts}; } FMT_MODULE_EXPORT_END diff --git a/externals/dynarmic/externals/fmt/include/fmt/compile.h b/externals/dynarmic/externals/fmt/include/fmt/compile.h index 1dba3ddb5..c09dd6f2a 100755 --- a/externals/dynarmic/externals/fmt/include/fmt/compile.h +++ b/externals/dynarmic/externals/fmt/include/fmt/compile.h @@ -13,45 +13,6 @@ FMT_BEGIN_NAMESPACE namespace detail { -// An output iterator that counts the number of objects written to it and -// discards them. -class counting_iterator { - private: - size_t count_; - - public: - using iterator_category = std::output_iterator_tag; - using difference_type = std::ptrdiff_t; - using pointer = void; - using reference = void; - using _Unchecked_type = counting_iterator; // Mark iterator as checked. - - struct value_type { - template void operator=(const T&) {} - }; - - counting_iterator() : count_(0) {} - - size_t count() const { return count_; } - - counting_iterator& operator++() { - ++count_; - return *this; - } - counting_iterator operator++(int) { - auto it = *this; - ++*this; - return it; - } - - friend counting_iterator operator+(counting_iterator it, difference_type n) { - it.count_ += static_cast(n); - return it; - } - - value_type operator*() const { return {}; } -}; - template inline counting_iterator copy_str(InputIt begin, InputIt end, counting_iterator it) { @@ -75,8 +36,7 @@ template class truncating_iterator_base { using difference_type = std::ptrdiff_t; using pointer = void; using reference = void; - using _Unchecked_type = - truncating_iterator_base; // Mark iterator as checked. + FMT_UNCHECKED_ITERATOR(truncating_iterator_base); OutputIt base() const { return out_; } size_t count() const { return count_; } @@ -163,12 +123,12 @@ struct is_compiled_string : std::is_base_of {}; # define FMT_COMPILE(s) FMT_STRING(s) #endif -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS template Str> struct udl_compiled_string : compiled_string { using char_type = Char; - constexpr operator basic_string_view() const { + explicit constexpr operator basic_string_view() const { return {Str.data, N - 1}; } }; @@ -377,7 +337,8 @@ template constexpr parse_specs_result parse_specs(basic_string_view str, size_t pos, int next_arg_id) { str.remove_prefix(pos); - auto ctx = basic_format_parse_context(str, {}, next_arg_id); + auto ctx = compile_parse_context(str, max_value(), nullptr, {}, + next_arg_id); auto f = formatter(); auto end = f.parse(ctx); return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1, @@ -573,10 +534,11 @@ FMT_INLINE std::basic_string format(const S&, constexpr auto compiled = detail::compile(S()); if constexpr (std::is_same, detail::unknown_format>()) { - return format(static_cast>(S()), - std::forward(args)...); + return fmt::format( + static_cast>(S()), + std::forward(args)...); } else { - return format(compiled, std::forward(args)...); + return fmt::format(compiled, std::forward(args)...); } } @@ -586,11 +548,11 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { constexpr auto compiled = detail::compile(S()); if constexpr (std::is_same, detail::unknown_format>()) { - return format_to(out, - static_cast>(S()), - std::forward(args)...); + return fmt::format_to( + out, static_cast>(S()), + std::forward(args)...); } else { - return format_to(out, compiled, std::forward(args)...); + return fmt::format_to(out, compiled, std::forward(args)...); } } #endif @@ -599,22 +561,23 @@ template ::value)> format_to_n_result format_to_n(OutputIt out, size_t n, const S& format_str, Args&&... args) { - auto it = format_to(detail::truncating_iterator(out, n), format_str, - std::forward(args)...); + auto it = fmt::format_to(detail::truncating_iterator(out, n), + format_str, std::forward(args)...); return {it.base(), it.count()}; } template ::value)> size_t formatted_size(const S& format_str, const Args&... args) { - return format_to(detail::counting_iterator(), format_str, args...).count(); + return fmt::format_to(detail::counting_iterator(), format_str, args...) + .count(); } template ::value)> void print(std::FILE* f, const S& format_str, const Args&... args) { memory_buffer buffer; - format_to(std::back_inserter(buffer), format_str, args...); + fmt::format_to(std::back_inserter(buffer), format_str, args...); detail::print(f, {buffer.data(), buffer.size()}); } @@ -624,14 +587,12 @@ void print(const S& format_str, const Args&... args) { print(stdout, format_str, args...); } -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS inline namespace literals { -template -constexpr detail::udl_compiled_string< - remove_cvref_t, - sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> -operator""_cf() { - return {}; +template constexpr auto operator""_cf() { + using char_t = remove_cvref_t; + return detail::udl_compiled_string(); } } // namespace literals #endif diff --git a/externals/dynarmic/externals/fmt/include/fmt/core.h b/externals/dynarmic/externals/fmt/include/fmt/core.h index 92a7aa1df..0e7843b85 100755 --- a/externals/dynarmic/externals/fmt/include/fmt/core.h +++ b/externals/dynarmic/externals/fmt/include/fmt/core.h @@ -10,14 +10,14 @@ #include // std::byte #include // std::FILE -#include +#include // std::strlen #include #include #include #include // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 80101 +#define FMT_VERSION 90000 #if defined(__clang__) && !defined(__ibmxl__) # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) @@ -49,29 +49,29 @@ # define FMT_ICC_VERSION 0 #endif -#ifdef __NVCC__ -# define FMT_NVCC __NVCC__ -#else -# define FMT_NVCC 0 -#endif - #ifdef _MSC_VER -# define FMT_MSC_VER _MSC_VER +# define FMT_MSC_VERSION _MSC_VER # define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) #else -# define FMT_MSC_VER 0 +# 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) && \ - (!defined(__INTELLISENSE__) || FMT_MSC_VER > 1900) && \ - (!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600) +#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 @@ -83,12 +83,6 @@ # define FMT_HAS_CPP_ATTRIBUTE(x) 0 #endif -#ifdef _MSVC_LANG -# define FMT_CPLUSPLUS _MSVC_LANG -#else -# define FMT_CPLUSPLUS __cplusplus -#endif - #define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) @@ -98,37 +92,38 @@ // Check if relaxed C++14 constexpr is supported. // GCC doesn't allow throw in constexpr until version 6 (bug 67371). #ifndef FMT_USE_CONSTEXPR -# define FMT_USE_CONSTEXPR \ - (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1912 || \ - (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ - !FMT_NVCC && !FMT_ICC_VERSION +# 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 -# define FMT_CONSTEXPR_DECL constexpr #else # define FMT_CONSTEXPR -# define FMT_CONSTEXPR_DECL #endif -#if ((__cplusplus >= 202002L) && \ +#if ((FMT_CPLUSPLUS >= 202002L) && \ (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \ - (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002) + (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002) # define FMT_CONSTEXPR20 constexpr #else # define FMT_CONSTEXPR20 #endif -// Check if constexpr std::char_traits<>::compare,length is supported. +// Check if constexpr std::char_traits<>::{compare,length} are supported. #if defined(__GLIBCXX__) -# if __cplusplus >= 201703L && defined(_GLIBCXX_RELEASE) && \ +# 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) && __cplusplus >= 201703L && \ +#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \ _LIBCPP_VERSION >= 4000 # define FMT_CONSTEXPR_CHAR_TRAITS constexpr -#elif FMT_MSC_VER >= 1914 && _MSVC_LANG >= 201703L +#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L # define FMT_CONSTEXPR_CHAR_TRAITS constexpr #endif #ifndef FMT_CONSTEXPR_CHAR_TRAITS @@ -138,57 +133,43 @@ // Check if exceptions are disabled. #ifndef FMT_EXCEPTIONS # if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ - FMT_MSC_VER && !_HAS_EXCEPTIONS + (FMT_MSC_VERSION && !_HAS_EXCEPTIONS) # define FMT_EXCEPTIONS 0 # else # define FMT_EXCEPTIONS 1 # endif #endif -// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). -#ifndef FMT_USE_NOEXCEPT -# define FMT_USE_NOEXCEPT 0 -#endif - -#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ - FMT_GCC_VERSION >= 408 || FMT_MSC_VER >= 1900 -# define FMT_DETECTED_NOEXCEPT noexcept -# define FMT_HAS_CXX11_NOEXCEPT 1 -#else -# define FMT_DETECTED_NOEXCEPT throw() -# define FMT_HAS_CXX11_NOEXCEPT 0 -#endif - -#ifndef FMT_NOEXCEPT -# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT -# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT +#ifndef FMT_DEPRECATED +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900 +# define FMT_DEPRECATED [[deprecated]] # else -# define FMT_NOEXCEPT +# 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_VER && \ - !FMT_NVCC +#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \ + !defined(__NVCC__) # define FMT_NORETURN [[noreturn]] #else # define FMT_NORETURN #endif -#if __cplusplus == 201103L || __cplusplus == 201402L -# if defined(__INTEL_COMPILER) || defined(__PGI) -# define FMT_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 -#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) +#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 @@ -219,24 +200,17 @@ # endif #endif -#ifndef FMT_DEPRECATED -# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 -# define FMT_DEPRECATED [[deprecated]] -# else -# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) -# define FMT_DEPRECATED __attribute__((deprecated)) -# elif FMT_MSC_VER -# define FMT_DEPRECATED __declspec(deprecated) -# else -# define FMT_DEPRECATED /* deprecated */ -# endif -# endif +#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 v8 { + inline namespace v9 { # define FMT_END_NAMESPACE \ } \ } @@ -270,25 +244,24 @@ #endif // libc++ supports string_view in pre-c++17. -#if (FMT_HAS_INCLUDE() && \ - (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ - (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) +#if FMT_HAS_INCLUDE() && \ + (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) # include # define FMT_USE_STRING_VIEW -#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L +#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L # include # define FMT_USE_EXPERIMENTAL_STRING_VIEW #endif #ifndef FMT_UNICODE -# define FMT_UNICODE !FMT_MSC_VER +# define FMT_UNICODE !FMT_MSC_VERSION #endif #ifndef FMT_CONSTEVAL -# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ - __cplusplus > 201703L && !defined(__apple_build_version__)) || \ - (defined(__cpp_consteval) && \ - (!FMT_MSC_VER || _MSC_FULL_VER >= 193030704)) +# 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 @@ -297,19 +270,19 @@ # endif #endif -#ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -# if defined(__cpp_nontype_template_args) && \ - ((FMT_GCC_VERSION >= 903 && __cplusplus >= 201709L) || \ +#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS +# if defined(__cpp_nontype_template_args) && \ + ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \ __cpp_nontype_template_args >= 201911L) -# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1 +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 # else -# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0 +# 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") -#ifndef __OPTIMIZE__ +#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) FMT_GCC_PRAGMA("GCC optimize(\"Og\")") #endif @@ -330,6 +303,20 @@ template using remove_cvref_t = typename std::remove_cv>::type; template struct type_identity { using type = T; }; template using type_identity_t = typename type_identity::type; +template +using underlying_t = typename std::underlying_type::type; + +template struct disjunction : std::false_type {}; +template struct disjunction

: P {}; +template +struct disjunction + : conditional_t> {}; + +template struct conjunction : std::true_type {}; +template struct conjunction

: P {}; +template +struct conjunction + : conditional_t, P1> {}; struct monostate { constexpr monostate() {} @@ -346,13 +333,13 @@ struct monostate { FMT_BEGIN_DETAIL_NAMESPACE -// Suppress "unused variable" warnings with the method described in +// 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 FMT_CONSTEXPR void ignore_unused(const T&...) {} -constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false) - FMT_NOEXCEPT -> bool { +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(); @@ -361,7 +348,7 @@ constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false) #endif } -// A function to suppress "conditional expression is constant" warnings. +// Suppresses "conditional expression is constant" warnings. template constexpr FMT_INLINE auto const_check(T value) -> T { return value; } @@ -371,7 +358,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, #ifndef FMT_ASSERT # ifdef NDEBUG -// FMT_ASSERT is not empty to avoid -Werror=empty-body. +// FMT_ASSERT is not empty to avoid -Wempty-body. # define FMT_ASSERT(condition, message) \ ::fmt::detail::ignore_unused((condition), (message)) # else @@ -382,12 +369,6 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, # endif #endif -#ifdef __cpp_lib_byte -using byte = std::byte; -#else -enum class byte : unsigned char {}; -#endif - #if defined(FMT_USE_STRING_VIEW) template using std_string_view = std::basic_string_view; #elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) @@ -399,11 +380,11 @@ template struct std_string_view {}; #ifdef FMT_USE_INT128 // Do nothing. -#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \ - !(FMT_CLANG_VERSION && FMT_MSC_VER) +#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ + !(FMT_CLANG_VERSION && FMT_MSC_VERSION) # define FMT_USE_INT128 1 -using int128_t = __int128_t; -using uint128_t = __uint128_t; +using int128_opt = __int128_t; // An optional native 128-bit integer. +using uint128_opt = __uint128_t; template inline auto convert_for_visit(T value) -> T { return value; } @@ -411,12 +392,10 @@ template inline auto convert_for_visit(T value) -> T { # define FMT_USE_INT128 0 #endif #if !FMT_USE_INT128 -enum class int128_t {}; -enum class uint128_t {}; +enum class int128_opt {}; +enum class uint128_opt {}; // Reduce template instantiations. -template inline auto convert_for_visit(T) -> monostate { - return {}; -} +template auto convert_for_visit(T) -> monostate { return {}; } #endif // Casts a nonnegative integer to unsigned. @@ -430,8 +409,7 @@ FMT_CONSTEXPR auto to_unsigned(Int 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. - // https://developercommunity.visualstudio.com/t/C-difference-in-behavior-for-unsigned/1233612 + // 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); @@ -454,12 +432,11 @@ template class basic_string_view { using value_type = Char; using iterator = const Char*; - constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {} + 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) FMT_NOEXCEPT - : data_(s), - size_(count) {} + constexpr basic_string_view(const Char* s, size_t count) noexcept + : data_(s), size_(count) {} /** \rst @@ -479,29 +456,28 @@ template class basic_string_view { /** Constructs a string reference from a ``std::basic_string`` object. */ template FMT_CONSTEXPR basic_string_view( - const std::basic_string& s) FMT_NOEXCEPT - : data_(s.data()), - size_(s.size()) {} + const std::basic_string& s) noexcept + : data_(s.data()), size_(s.size()) {} template >::value)> - FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()), - size_(s.size()) {} + 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 FMT_NOEXCEPT -> const Char* { return data_; } + constexpr auto data() const noexcept -> const Char* { return data_; } /** Returns the string size. */ - constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; } + constexpr auto size() const noexcept -> size_t { return size_; } - constexpr auto begin() const FMT_NOEXCEPT -> iterator { return data_; } - constexpr auto end() const FMT_NOEXCEPT -> iterator { return data_ + 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 FMT_NOEXCEPT -> const Char& { + constexpr auto operator[](size_t pos) const noexcept -> const Char& { return data_[pos]; } - FMT_CONSTEXPR void remove_prefix(size_t n) FMT_NOEXCEPT { + FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { data_ += n; size_ -= n; } @@ -543,6 +519,14 @@ using string_view = basic_string_view; template struct is_char : std::false_type {}; template <> struct is_char : std::true_type {}; +FMT_BEGIN_DETAIL_NAMESPACE + +// A base class for compile-time strings. +struct compile_string {}; + +template +struct is_compile_string : std::is_base_of {}; + // Returns a string view of `s`. template ::value)> FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view { @@ -559,33 +543,21 @@ constexpr auto to_string_view(basic_string_view s) return s; } template >::value)> -inline auto to_string_view(detail::std_string_view s) - -> basic_string_view { + FMT_ENABLE_IF(!std::is_empty>::value)> +inline auto to_string_view(std_string_view s) -> basic_string_view { return s; } - -// A base class for compile-time strings. It is defined in the fmt namespace to -// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42). -struct compile_string {}; - -template -struct is_compile_string : std::is_base_of {}; - template ::value)> constexpr auto to_string_view(const S& s) -> basic_string_view { return basic_string_view(s); } - -FMT_BEGIN_DETAIL_NAMESPACE - void to_string_view(...); -using fmt::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 struct is_string : std::is_class()))> { }; @@ -596,17 +568,60 @@ template struct char_t_impl::value>> { using type = typename result::value_type; }; -// Reports a compile-time error if S is not a valid format string. -template ::value)> -FMT_INLINE void check_format_string(const S&) { -#ifdef FMT_ENFORCE_COMPILE_STRING - static_assert(is_compile_string::value, - "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " - "FMT_STRING."); -#endif +enum class type { + none_type, + // Integer types should go first, + int_type, + uint_type, + long_long_type, + ulong_long_type, + int128_type, + uint128_type, + bool_type, + char_type, + last_integer_type = char_type, + // followed by floating-point types. + float_type, + double_type, + long_double_type, + last_numeric_type = long_double_type, + cstring_type, + string_type, + pointer_type, + custom_type +}; + +// Maps core type T to the corresponding type enum constant. +template +struct type_constant : std::integral_constant {}; + +#define FMT_TYPE_CONSTANT(Type, constant) \ + template \ + struct type_constant \ + : std::integral_constant {} + +FMT_TYPE_CONSTANT(int, int_type); +FMT_TYPE_CONSTANT(unsigned, uint_type); +FMT_TYPE_CONSTANT(long long, long_long_type); +FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); +FMT_TYPE_CONSTANT(int128_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, string_type); +FMT_TYPE_CONSTANT(const void*, pointer_type); + +constexpr bool is_integral_type(type t) { + return t > type::none_type && t <= type::last_integer_type; +} + +constexpr bool is_arithmetic_type(type t) { + return t > type::none_type && t <= type::last_numeric_type; } -template ::value)> -void check_format_string(S); FMT_NORETURN FMT_API void throw_format_error(const char* message); @@ -615,7 +630,9 @@ struct error_handler { constexpr error_handler(const error_handler&) = default; // This function is intentionally not constexpr to give a compile-time error. - FMT_NORETURN FMT_API void on_error(const char* message); + FMT_NORETURN void on_error(const char* message) { + throw_format_error(message); + } }; FMT_END_DETAIL_NAMESPACE @@ -635,6 +652,8 @@ class basic_format_parse_context : private ErrorHandler { basic_string_view 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::iterator; @@ -648,16 +667,14 @@ class basic_format_parse_context : private ErrorHandler { Returns an iterator to the beginning of the format string range being parsed. */ - constexpr auto begin() const FMT_NOEXCEPT -> iterator { + 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 FMT_NOEXCEPT -> iterator { - return format_str_.end(); - } + constexpr auto end() const noexcept -> iterator { return format_str_.end(); } /** Advances the begin iterator to ``it``. */ FMT_CONSTEXPR void advance_to(iterator it) { @@ -669,22 +686,26 @@ class basic_format_parse_context : private ErrorHandler { the next argument index and switches to the automatic indexing. */ FMT_CONSTEXPR auto next_arg_id() -> int { - // Don't check if the argument id is valid to avoid overhead and because it - // will be checked during formatting anyway. - if (next_arg_id_ >= 0) return next_arg_id_++; - on_error("cannot switch from manual to automatic argument indexing"); - return 0; + 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) { - if (next_arg_id_ > 0) + FMT_CONSTEXPR void check_arg_id(int id) { + if (next_arg_id_ > 0) { on_error("cannot switch from automatic to manual argument indexing"); - else - next_arg_id_ = -1; + return; + } + next_arg_id_ = -1; + do_check_arg_id(id); } FMT_CONSTEXPR void check_arg_id(basic_string_view) {} @@ -698,6 +719,50 @@ class basic_format_parse_context : private ErrorHandler { using format_parse_context = basic_format_parse_context; +FMT_BEGIN_DETAIL_NAMESPACE +// A parse context with extra data used only in compile-time checks. +template +class compile_parse_context + : public basic_format_parse_context { + private: + int num_args_; + const type* types_; + using base = basic_format_parse_context; + + public: + explicit FMT_CONSTEXPR compile_parse_context( + basic_string_view 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 int num_args() const { return num_args_; } + + 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_END_DETAIL_NAMESPACE + +template +FMT_CONSTEXPR void +basic_format_parse_context::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; + if (id >= static_cast(this)->num_args()) + on_error("argument not found"); + } +} + template class basic_format_arg; template class basic_format_args; template class dynamic_format_arg_store; @@ -744,10 +809,10 @@ constexpr auto has_const_formatter() -> bool { template inline auto get_container(std::back_insert_iterator it) -> Container& { - using bi_iterator = std::back_insert_iterator; - struct accessor : bi_iterator { - accessor(bi_iterator iter) : bi_iterator(iter) {} - using bi_iterator::container; + using base = std::back_insert_iterator; + struct accessor : base { + accessor(base b) : base(b) {} + using base::container; }; return *accessor(it).container; } @@ -784,18 +849,16 @@ template class buffer { protected: // Don't initialize ptr_ since it is not accessed to save a few cycles. FMT_MSC_WARNING(suppress : 26495) - buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {} + buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {} - FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, - size_t cap = 0) FMT_NOEXCEPT : ptr_(p), - size_(sz), - capacity_(cap) {} + 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) FMT_NOEXCEPT { + FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { ptr_ = buf_data; capacity_ = buf_capacity; } @@ -810,23 +873,23 @@ template class buffer { buffer(const buffer&) = delete; void operator=(const buffer&) = delete; - auto begin() FMT_NOEXCEPT -> T* { return ptr_; } - auto end() FMT_NOEXCEPT -> T* { return ptr_ + size_; } + auto begin() noexcept -> T* { return ptr_; } + auto end() noexcept -> T* { return ptr_ + size_; } - auto begin() const FMT_NOEXCEPT -> const T* { return ptr_; } - auto end() const FMT_NOEXCEPT -> const 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 FMT_NOEXCEPT -> size_t { return size_; } + constexpr auto size() const noexcept -> size_t { return size_; } /** Returns the capacity of this buffer. */ - constexpr auto capacity() const FMT_NOEXCEPT -> size_t { return capacity_; } + constexpr auto capacity() const noexcept -> size_t { return capacity_; } /** Returns a pointer to the buffer data. */ - FMT_CONSTEXPR auto data() FMT_NOEXCEPT -> T* { return ptr_; } + FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } /** Returns a pointer to the buffer data. */ - FMT_CONSTEXPR auto data() const FMT_NOEXCEPT -> const T* { return ptr_; } + FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } /** Clears this buffer. */ void clear() { size_ = 0; } @@ -993,6 +1056,7 @@ class iterator_buffer, : buffer(c.size()), container_(c) {} explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) : iterator_buffer(get_container(out)) {} + auto out() -> std::back_insert_iterator { return std::back_inserter(container_); } @@ -1044,7 +1108,11 @@ struct fallback_formatter { // Specifies if T has an enabled fallback_formatter specialization. template using has_fallback_formatter = +#ifdef FMT_DEPRECATED_OSTREAM std::is_constructible>; +#else + std::false_type; +#endif struct view {}; @@ -1128,61 +1196,6 @@ constexpr auto count_statically_named_args() -> size_t { return count::value...>(); } -enum class type { - none_type, - // Integer types should go first, - int_type, - uint_type, - long_long_type, - ulong_long_type, - int128_type, - uint128_type, - bool_type, - char_type, - last_integer_type = char_type, - // followed by floating-point types. - float_type, - double_type, - long_double_type, - last_numeric_type = long_double_type, - cstring_type, - string_type, - pointer_type, - custom_type -}; - -// Maps core type T to the corresponding type enum constant. -template -struct type_constant : std::integral_constant {}; - -#define FMT_TYPE_CONSTANT(Type, constant) \ - template \ - struct type_constant \ - : std::integral_constant {} - -FMT_TYPE_CONSTANT(int, int_type); -FMT_TYPE_CONSTANT(unsigned, uint_type); -FMT_TYPE_CONSTANT(long long, long_long_type); -FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); -FMT_TYPE_CONSTANT(int128_t, int128_type); -FMT_TYPE_CONSTANT(uint128_t, uint128_type); -FMT_TYPE_CONSTANT(bool, bool_type); -FMT_TYPE_CONSTANT(Char, char_type); -FMT_TYPE_CONSTANT(float, float_type); -FMT_TYPE_CONSTANT(double, double_type); -FMT_TYPE_CONSTANT(long double, long_double_type); -FMT_TYPE_CONSTANT(const Char*, cstring_type); -FMT_TYPE_CONSTANT(basic_string_view, string_type); -FMT_TYPE_CONSTANT(const void*, pointer_type); - -constexpr bool is_integral_type(type t) { - return t > type::none_type && t <= type::last_integer_type; -} - -constexpr bool is_arithmetic_type(type t) { - return t > type::none_type && t <= type::last_numeric_type; -} - struct unformattable {}; struct unformattable_char : unformattable {}; struct unformattable_const : unformattable {}; @@ -1215,8 +1228,8 @@ template class value { unsigned uint_value; long long long_long_value; unsigned long long ulong_long_value; - int128_t int128_value; - uint128_t uint128_value; + int128_opt int128_value; + uint128_opt uint128_value; bool bool_value; char_type char_value; float float_value; @@ -1233,8 +1246,8 @@ template class value { 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_t val) : int128_value(val) {} - FMT_INLINE value(uint128_t val) : uint128_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) {} @@ -1284,7 +1297,7 @@ template class value { }; template -FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg; +FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg; // 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. @@ -1292,6 +1305,21 @@ enum { long_short = sizeof(long) == sizeof(int) }; using long_type = conditional_t; using ulong_type = conditional_t; +#ifdef __cpp_lib_byte +inline auto format_as(std::byte b) -> unsigned char { + return static_cast(b); +} +#endif + +template struct has_format_as { + template ::value&& std::is_integral::value)> + static auto check(U*) -> std::true_type; + static auto check(...) -> std::false_type; + + enum { value = decltype(check(static_cast(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. @@ -1317,8 +1345,12 @@ template struct arg_mapper { -> unsigned long long { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(int128_t val) -> int128_t { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(uint128_t val) -> uint128_t { 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 ::value || @@ -1365,45 +1397,24 @@ template struct arg_mapper { } template , T>::value && + std::is_convertible>::value && !is_string::value && !has_formatter::value && !has_fallback_formatter::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> basic_string_view { return basic_string_view(val); } - template < - typename T, - FMT_ENABLE_IF( - std::is_constructible, T>::value && - !std::is_constructible, T>::value && - !is_string::value && !has_formatter::value && - !has_fallback_formatter::value)> + template >::value && + !std::is_convertible>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> basic_string_view { return std_string_view(val); } - using cstring_result = conditional_t::value, - const char*, unformattable_pointer>; - - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val) - -> cstring_result { - return map(reinterpret_cast(val)); - } - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val) - -> cstring_result { - return map(reinterpret_cast(val)); - } - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(signed char* val) - -> cstring_result { - return map(reinterpret_cast(val)); - } - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val) - -> cstring_result { - return map(reinterpret_cast(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; @@ -1417,10 +1428,11 @@ template struct arg_mapper { template < typename T, FMT_ENABLE_IF( - std::is_member_pointer::value || + std::is_pointer::value || std::is_member_pointer::value || std::is_function::type>::value || (std::is_convertible::value && - !std::is_convertible::value))> + !std::is_convertible::value && + !has_formatter::value))> FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { return {}; } @@ -1434,16 +1446,19 @@ template struct arg_mapper { template ::value&& std::is_convertible::value && - !has_formatter::value && + !has_format_as::value && !has_formatter::value && !has_fallback_formatter::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(std::declval().map( - static_cast::type>(val))) { - return map(static_cast::type>(val)); + static_cast>(val))) { + return map(static_cast>(val)); } - FMT_CONSTEXPR FMT_INLINE auto map(detail::byte val) -> unsigned { - return map(static_cast(val)); + template ::value && + !has_formatter::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> decltype(std::declval().map(format_as(T()))) { + return map(format_as(val)); } template > @@ -1452,8 +1467,9 @@ template struct arg_mapper { !std::is_const>::value || has_fallback_formatter::value> {}; -#if FMT_MSC_VER != 0 && FMT_MSC_VER < 1910 - // Workaround a bug in MSVC. +#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 FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { return val; } @@ -1471,6 +1487,8 @@ template struct arg_mapper { template , FMT_ENABLE_IF(!is_string::value && !is_char::value && !std::is_array::value && + !std::is_pointer::value && + !has_format_as::value && (has_formatter::value || has_fallback_formatter::value))> FMT_CONSTEXPR FMT_INLINE auto map(T&& val) @@ -1513,12 +1531,11 @@ class appender : public std::back_insert_iterator> { public: using std::back_insert_iterator>::back_insert_iterator; - appender(base it) FMT_NOEXCEPT : base(it) {} - using _Unchecked_type = appender; // Mark iterator as checked. + appender(base it) noexcept : base(it) {} + FMT_UNCHECKED_ITERATOR(appender); - auto operator++() FMT_NOEXCEPT -> appender& { return *this; } - - auto operator++(int) FMT_NOEXCEPT -> appender { return *this; } + auto operator++() noexcept -> appender& { return *this; } + auto operator++(int) noexcept -> appender { return *this; } }; // A formatting argument. It is a trivially copyable/constructible type to @@ -1529,7 +1546,7 @@ template class basic_format_arg { detail::type type_; template - friend FMT_CONSTEXPR auto detail::make_arg(const T& value) + friend FMT_CONSTEXPR auto detail::make_arg(T&& value) -> basic_format_arg; template @@ -1564,7 +1581,7 @@ template class basic_format_arg { constexpr basic_format_arg() : type_(detail::type::none_type) {} - constexpr explicit operator bool() const FMT_NOEXCEPT { + constexpr explicit operator bool() const noexcept { return type_ != detail::type::none_type; } @@ -1665,7 +1682,7 @@ struct is_contiguous_back_insert_iterator> template <> struct is_contiguous_back_insert_iterator : std::true_type {}; -// A type-erased reference to an std::locale to avoid heavy include. +// A type-erased reference to an std::locale to avoid a heavy include. class locale_ref { private: const void* locale_; // A type-erased pointer to std::locale. @@ -1674,7 +1691,7 @@ class locale_ref { constexpr locale_ref() : locale_(nullptr) {} template explicit locale_ref(const Locale& loc); - explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } + explicit operator bool() const noexcept { return locale_ != nullptr; } template auto get() const -> Locale; }; @@ -1690,19 +1707,7 @@ constexpr auto encode_types() -> unsigned long long { } template -FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg { - basic_format_arg arg; - arg.type_ = mapped_type_constant::value; - arg.value_ = arg_mapper().map(value); - return arg; -} - -// The type template parameter is there to avoid an ODR violation when using -// a fallback formatter in one translation unit and an implicit conversion in -// another (not recommended). -template -FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { +FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value { const auto& arg = arg_mapper().map(std::forward(val)); constexpr bool formattable_char = @@ -1731,9 +1736,26 @@ FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { return {arg}; } +template +FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg { + basic_format_arg arg; + arg.type_ = mapped_type_constant::value; + arg.value_ = make_value(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 +FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { + return make_value(val); +} + template -inline auto make_arg(const T& value) -> basic_format_arg { +FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg { return make_arg(value); } FMT_END_DETAIL_NAMESPACE @@ -2015,14 +2037,22 @@ template class basic_format_args { // between clang and gcc on ARM (#1919). using format_args = basic_format_args; -// We cannot use enum classes as bit fields because of a gcc bug -// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414. +// 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 { none, left, right, center, numeric }; +enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center, + numeric}; } using align_t = align::type; namespace sign { -enum type { none, minus, plus, space }; +enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space}; } using sign_t = sign::type; @@ -2072,7 +2102,8 @@ enum class presentation_type : unsigned char { general_upper, // 'G' chr, // 'c' string, // 's' - pointer // 'p' + pointer, // 'p' + debug // '?' }; // Format specifiers for built-in and string types. @@ -2231,13 +2262,12 @@ template constexpr bool is_ascii_letter(Char c) { // Converts a character to ASCII. Returns a number > 127 on conversion failure. template ::value)> -constexpr auto to_ascii(Char value) -> Char { - return value; +constexpr auto to_ascii(Char c) -> Char { + return c; } template ::value)> -constexpr auto to_ascii(Char value) -> - typename std::underlying_type::type { - return value; +constexpr auto to_ascii(Char c) -> underlying_t { + return c; } template @@ -2302,7 +2332,7 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, FMT_ASSERT(begin != end, ""); auto align = align::none; auto p = begin + code_point_length(begin); - if (p >= end) p = begin; + if (end - p <= 0) p = begin; for (;;) { switch (to_ascii(*p)) { case '<': @@ -2488,6 +2518,8 @@ FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type { return presentation_type::string; case 'p': return presentation_type::pointer; + case '?': + return presentation_type::debug; default: return presentation_type::none; } @@ -2635,21 +2667,21 @@ FMT_CONSTEXPR FMT_INLINE void parse_format_string( return; } struct writer { - FMT_CONSTEXPR void operator()(const Char* pbegin, const Char* pend) { - if (pbegin == pend) return; + FMT_CONSTEXPR void operator()(const Char* from, const Char* to) { + if (from == to) return; for (;;) { const Char* p = nullptr; - if (!find(pbegin, pend, Char('}'), p)) - return handler_.on_text(pbegin, pend); + if (!find(from, to, Char('}'), p)) + return handler_.on_text(from, to); ++p; - if (p == pend || *p != '}') + if (p == to || *p != '}') return handler_.on_error("unmatched '}' in format string"); - handler_.on_text(pbegin, p); - pbegin = p + 1; + handler_.on_text(from, p); + from = p + 1; } } Handler& handler_; - } write{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. @@ -2661,50 +2693,29 @@ FMT_CONSTEXPR FMT_INLINE void parse_format_string( } } +template ::value> struct strip_named_arg { + using type = T; +}; +template struct strip_named_arg { + using type = remove_cvref_t; +}; + template FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) -> decltype(ctx.begin()) { using char_type = typename ParseContext::char_type; using context = buffer_context; + using stripped_type = typename strip_named_arg::type; using mapped_type = conditional_t< mapped_type_constant::value != type::custom_type, - decltype(arg_mapper().map(std::declval())), T>; + decltype(arg_mapper().map(std::declval())), + stripped_type>; auto f = conditional_t::value, formatter, - fallback_formatter>(); + fallback_formatter>(); return f.parse(ctx); } -// A parse context with extra argument id checks. It is only used at compile -// time because adding checks at runtime would introduce substantial overhead -// and would be redundant since argument ids are checked when arguments are -// retrieved anyway. -template -class compile_parse_context - : public basic_format_parse_context { - private: - int num_args_; - using base = basic_format_parse_context; - - public: - explicit FMT_CONSTEXPR compile_parse_context( - basic_string_view format_str, - int num_args = (std::numeric_limits::max)(), ErrorHandler eh = {}) - : base(format_str, eh), num_args_(num_args) {} - - 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; -}; - template FMT_CONSTEXPR void check_int_type_spec(presentation_type type, ErrorHandler&& eh) { @@ -2717,7 +2728,8 @@ template FMT_CONSTEXPR auto check_char_specs(const basic_format_specs& specs, ErrorHandler&& eh = {}) -> bool { if (specs.type != presentation_type::none && - specs.type != presentation_type::chr) { + specs.type != presentation_type::chr && + specs.type != presentation_type::debug) { check_int_type_spec(specs.type, eh); return false; } @@ -2741,7 +2753,6 @@ struct float_specs { bool upper : 1; bool locale : 1; bool binary32 : 1; - bool fallback : 1; bool showpoint : 1; }; @@ -2801,7 +2812,8 @@ FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type, template FMT_CONSTEXPR void check_string_type_spec(presentation_type type, ErrorHandler&& eh = {}) { - if (type != presentation_type::none && type != presentation_type::string) + if (type != presentation_type::none && type != presentation_type::string && + type != presentation_type::debug) eh.on_error("invalid type specifier"); } @@ -2835,7 +2847,8 @@ template class specs_checker : public Handler { 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::char_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); @@ -2864,7 +2877,7 @@ template class specs_checker : public Handler { constexpr int invalid_arg_index = -1; -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS template constexpr auto get_arg_index_by_name(basic_string_view name) -> int { if constexpr (detail::is_statically_named_arg()) { @@ -2879,7 +2892,7 @@ constexpr auto get_arg_index_by_name(basic_string_view name) -> int { template FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS if constexpr (sizeof...(Args) > 0) return get_arg_index_by_name<0, Args...>(name); #endif @@ -2890,20 +2903,25 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { template 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; - enum { num_args = sizeof...(Args) }; + 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 ? num_args : 1]; + parse_func parse_funcs_[num_args > 0 ? static_cast(num_args) : 1]; + type types_[num_args > 0 ? static_cast(num_args) : 1]; public: explicit FMT_CONSTEXPR format_string_checker( basic_string_view format_str, ErrorHandler eh) - : context_(format_str, num_args, eh), - parse_funcs_{&parse_format_specs...} {} + : context_(format_str, num_args, types_, eh), + parse_funcs_{&parse_format_specs...}, + types_{type_constant::value...} {} FMT_CONSTEXPR void on_text(const Char*, const Char*) {} @@ -2912,7 +2930,7 @@ class format_string_checker { return context_.check_arg_id(id), id; } FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS auto index = get_arg_index_by_name(id); if (index == invalid_arg_index) on_error("named argument is not found"); return context_.check_arg_id(index), index; @@ -2937,10 +2955,19 @@ class format_string_checker { } }; +// Reports a compile-time error if S is not a valid format string. +template ::value)> +FMT_INLINE void check_format_string(const S&) { +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert(is_compile_string::value, + "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " + "FMT_STRING."); +#endif +} template ::value), int>> + FMT_ENABLE_IF(is_compile_string::value)> void check_format_string(S format_str) { - FMT_CONSTEXPR auto s = to_string_view(format_str); + FMT_CONSTEXPR auto s = basic_string_view(format_str); using checker = format_string_checker...>; FMT_CONSTEXPR bool invalid_format = @@ -3043,6 +3070,27 @@ struct formatter decltype(ctx.out()); }; +#define FMT_FORMAT_AS(Type, Base) \ + template \ + struct formatter : formatter { \ + template \ + auto format(Type const& val, FormatContext& ctx) const \ + -> decltype(ctx.out()) { \ + return formatter::format(static_cast(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, basic_string_view); +FMT_FORMAT_AS(std::nullptr_t, const void*); +FMT_FORMAT_AS(detail::std_string_view, basic_string_view); + template struct basic_runtime { basic_string_view str; }; /** A compile-time format string. */ @@ -3078,10 +3126,8 @@ template class basic_format_string { #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 // Workaround broken conversion on older gcc. -template using format_string = string_view; -template auto runtime(const S& s) -> basic_string_view> { - return s; -} +template using format_string = string_view; +inline auto runtime(string_view s) -> basic_string_view { return s; } #else template using format_string = basic_format_string...>; @@ -3095,9 +3141,7 @@ using format_string = basic_format_string...>; fmt::print(fmt::runtime("{:d}"), "I am not a number"); \endrst */ -template auto runtime(const S& s) -> basic_runtime> { - return {{s}}; -} +inline auto runtime(string_view s) -> basic_runtime { return {{s}}; } #endif FMT_API auto vformat(string_view fmt, format_args args) -> std::string; diff --git a/externals/dynarmic/externals/fmt/include/fmt/format-inl.h b/externals/dynarmic/externals/fmt/include/fmt/format-inl.h index 2c51c50ae..f44df01c5 100755 --- a/externals/dynarmic/externals/fmt/include/fmt/format-inl.h +++ b/externals/dynarmic/externals/fmt/include/fmt/format-inl.h @@ -44,21 +44,8 @@ FMT_FUNC void throw_format_error(const char* message) { FMT_THROW(format_error(message)); } -#ifndef _MSC_VER -# define FMT_SNPRINTF snprintf -#else // _MSC_VER -inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) { - va_list args; - va_start(args, format); - int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); - va_end(args); - return result; -} -# define FMT_SNPRINTF fmt_snprintf -#endif // _MSC_VER - FMT_FUNC void format_error_code(detail::buffer& out, int error_code, - string_view message) FMT_NOEXCEPT { + 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. @@ -81,7 +68,7 @@ FMT_FUNC void format_error_code(detail::buffer& out, int error_code, } FMT_FUNC void report_error(format_func func, int error_code, - const char* message) FMT_NOEXCEPT { + const char* message) noexcept { memory_buffer full_message; func(full_message, error_code, message); // Don't use fwrite_fully because the latter may throw. @@ -93,7 +80,8 @@ FMT_FUNC void report_error(format_func func, int error_code, 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, "cannot write to file")); + if (written < count) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); } #ifndef FMT_STATIC_THOUSANDS_SEPARATOR @@ -129,8 +117,8 @@ template FMT_FUNC Char decimal_point_impl(locale_ref) { #endif } // namespace detail -#if !FMT_MSC_VER -FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default; +#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, @@ -141,710 +129,31 @@ FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str, namespace detail { -template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) { - // fallback_uintptr is always stored in little endian. - int i = static_cast(sizeof(void*)) - 1; - while (i > 0 && n.value[i] == 0) --i; - auto char_digits = std::numeric_limits::digits / 4; - return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1; +template inline bool operator==(basic_fp x, basic_fp y) { + return x.f == y.f && x.e == y.e; } -// log10(2) = 0x0.4d104d427de7fbcc... -static constexpr uint64_t log10_2_significand = 0x4d104d427de7fbcc; - -template struct basic_impl_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}; -}; - -// This is a struct rather than an alias to avoid shadowing warnings in gcc. -struct impl_data : basic_impl_data<> {}; - -#if __cplusplus < 201703L -template -constexpr uint64_t basic_impl_data::pow10_significands[]; -template constexpr int16_t basic_impl_data::pow10_exponents[]; -template constexpr uint64_t basic_impl_data::power_of_10_64[]; -#endif - -template struct bits { - static FMT_CONSTEXPR_DECL const int value = - static_cast(sizeof(T) * std::numeric_limits::digits); -}; - -// Returns the number of significand bits in Float excluding the implicit bit. -template constexpr int num_significand_bits() { - // Subtract 1 to account for an implicit most significant bit in the - // normalized form. - return std::numeric_limits::digits - 1; +// 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)); } -// A floating-point number f * pow(2, e). -struct fp { - uint64_t f; - int e; - - static constexpr const int num_significand_bits = bits::value; - - constexpr fp() : f(0), e(0) {} - constexpr fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} - - // Constructs fp from an IEEE754 floating-point number. It is a template to - // prevent compile errors on systems where n is not IEEE754. - template explicit FMT_CONSTEXPR fp(Float n) { assign(n); } - - template - using is_supported = bool_constant; - - // Assigns d to this and return true iff predecessor is closer than successor. - template ::value)> - FMT_CONSTEXPR bool assign(Float n) { - // Assume float is in the format [sign][exponent][significand]. - const int num_float_significand_bits = - detail::num_significand_bits(); - const uint64_t implicit_bit = 1ULL << num_float_significand_bits; - const uint64_t significand_mask = implicit_bit - 1; - constexpr bool is_double = sizeof(Float) == sizeof(uint64_t); - auto u = bit_cast>(n); - f = u & significand_mask; - const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask; - int biased_e = - static_cast((u & exponent_mask) >> 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). - bool is_predecessor_closer = f == 0 && biased_e > 1; - if (biased_e != 0) - f += implicit_bit; - else - biased_e = 1; // Subnormals use biased exponent 1 (min exponent). - const int exponent_bias = std::numeric_limits::max_exponent - 1; - e = biased_e - exponent_bias - num_float_significand_bits; - return is_predecessor_closer; - } - - template ::value)> - bool assign(Float) { - FMT_ASSERT(false, ""); - return false; - } -}; - -// Normalizes the value converted from double and multiplied by (1 << SHIFT). -template FMT_CONSTEXPR fp normalize(fp value) { - // Handle subnormals. - const uint64_t implicit_bit = 1ULL << num_significand_bits(); - 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 = - fp::num_significand_bits - num_significand_bits() - SHIFT - 1; - value.f <<= offset; - value.e -= offset; - return value; -} - -inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; } - -// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. -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(product >> 64); - return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; -#else - // Multiply 32-bit parts of significands. - uint64_t mask = (1ULL << 32) - 1; - uint64_t a = lhs >> 32, b = lhs & mask; - uint64_t c = rhs >> 32, d = rhs & mask; - uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; - // Compute mid 64-bit of result and round. - uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); - return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); -#endif -} - -FMT_CONSTEXPR inline fp operator*(fp x, fp y) { - return {multiply(x.f, y.f), x.e + y.e + 64}; -} - -// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its -// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. -FMT_CONSTEXPR inline fp get_cached_power(int min_exponent, - int& pow10_exponent) { - const int shift = 32; - const auto significand = static_cast(log10_2_significand); - int index = static_cast( - ((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; - return {impl_data::pow10_significands[index], - impl_data::pow10_exponents[index]}; -} - -// A simple accumulator to hold the sums of terms in bigint::square if uint128_t -// is not available. -struct accumulator { - uint64_t lower; - uint64_t upper; - - constexpr accumulator() : lower(0), upper(0) {} - constexpr explicit operator uint32_t() const { - return static_cast(lower); - } - - FMT_CONSTEXPR void operator+=(uint64_t n) { - lower += n; - if (lower < n) ++upper; - } - FMT_CONSTEXPR void operator>>=(int shift) { - FMT_ASSERT(shift == 32, ""); - (void)shift; - lower = (upper << 32) | (lower >> 32); - upper >>= 32; - } -}; - -class bigint { - private: - // A bigint is stored as an array of bigits (big digits), with bigit at index - // 0 being the least significant one. - using bigit = uint32_t; - using double_bigit = uint64_t; - enum { bigits_capacity = 32 }; - basic_memory_buffer bigits_; - int exp_; - - FMT_CONSTEXPR20 bigit operator[](int index) const { - return bigits_[to_unsigned(index)]; - } - FMT_CONSTEXPR20 bigit& operator[](int index) { - return bigits_[to_unsigned(index)]; - } - - static FMT_CONSTEXPR_DECL const int bigit_bits = bits::value; - - friend struct formatter; - - FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) { - auto result = static_cast((*this)[index]) - other - borrow; - (*this)[index] = static_cast(result); - borrow = static_cast(result >> (bigit_bits * 2 - 1)); - } - - FMT_CONSTEXPR20 void remove_leading_zeros() { - int num_bigits = static_cast(bigits_.size()) - 1; - while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; - bigits_.resize(to_unsigned(num_bigits + 1)); - } - - // Computes *this -= other assuming aligned bigints and *this >= other. - 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(result); - carry = static_cast(result >> bigit_bits); - } - if (carry != 0) bigits_.push_back(carry); - } - - FMT_CONSTEXPR20 void multiply(uint64_t value) { - const bigit mask = ~bigit(0); - const double_bigit lower = value & mask; - const double_bigit upper = value >> bigit_bits; - double_bigit carry = 0; - for (size_t i = 0, n = bigits_.size(); i < n; ++i) { - double_bigit result = bigits_[i] * lower + (carry & mask); - carry = - bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits); - bigits_[i] = static_cast(result); - } - while (carry != 0) { - bigits_.push_back(carry & mask); - carry >>= bigit_bits; - } - } - - public: - FMT_CONSTEXPR20 bigint() : exp_(0) {} - explicit bigint(uint64_t n) { assign(n); } - FMT_CONSTEXPR20 ~bigint() { - FMT_ASSERT(bigits_.capacity() <= bigits_capacity, ""); - } - - 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_; - } - - FMT_CONSTEXPR20 void assign(uint64_t n) { - size_t num_bigits = 0; - do { - bigits_[num_bigits++] = n & ~bigit(0); - n >>= bigit_bits; - } while (n != 0); - bigits_.resize(num_bigits); - exp_ = 0; - } - - FMT_CONSTEXPR20 int num_bigits() const { - return static_cast(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 FMT_CONSTEXPR20 bigint& operator*=(Int value) { - FMT_ASSERT(value > 0, ""); - multiply(uint32_or_64_or_128_t(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(lhs.bigits_.size()) - 1; - int j = static_cast(rhs.bigits_.size()) - 1; - int end = i - j; - if (end < 0) end = 0; - for (; i >= end; --i, --j) { - bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; - if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; - } - if (i != j) return i > j ? 1 : -1; - return 0; - } - - // Returns compare(lhs1 + lhs2, rhs). - friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2, - const bigint& rhs) { - int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits()); - int num_rhs_bigits = rhs.num_bigits(); - if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; - if (max_lhs_bigits > num_rhs_bigits) return 1; - auto get_bigit = [](const bigint& n, int i) -> bigit { - return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; - }; - double_bigit borrow = 0; - int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_); - for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { - double_bigit sum = - static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); - bigit rhs_bigit = get_bigit(rhs, i); - if (sum > rhs_bigit + borrow) return 1; - borrow = rhs_bigit + borrow - sum; - if (borrow > 1) return -1; - borrow <<= bigit_bits; - } - return borrow != 0 ? -1 : 0; - } - - // Assigns pow(10, exp) to this bigint. - FMT_CONSTEXPR20 void assign_pow10(int exp) { - FMT_ASSERT(exp >= 0, ""); - if (exp == 0) return assign(1); - // Find the top bit. - int bitmask = 1; - while (exp >= bitmask) bitmask <<= 1; - bitmask >>= 1; - // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by - // repeated squaring and multiplication. - assign(5); - bitmask >>= 1; - while (bitmask != 0) { - square(); - if ((exp & bitmask) != 0) *this *= 5; - bitmask >>= 1; - } - *this <<= exp; // Multiply by pow(2, exp) by shifting. - } - - FMT_CONSTEXPR20 void square() { - int num_bigits = static_cast(bigits_.size()); - int num_result_bigits = 2 * num_bigits; - basic_memory_buffer n(std::move(bigits_)); - bigits_.resize(to_unsigned(num_result_bigits)); - using accumulator_t = conditional_t; - auto sum = accumulator_t(); - for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) { - // Compute bigit at position bigit_index of the result by adding - // cross-product terms n[i] * n[j] such that i + j == bigit_index. - for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { - // Most terms are multiplied twice which can be optimized in the future. - sum += static_cast(n[i]) * n[j]; - } - (*this)[bigit_index] = static_cast(sum); - sum >>= bits::value; // Compute the carry. - } - // Do the same for the top half. - for (int bigit_index = num_bigits; bigit_index < num_result_bigits; - ++bigit_index) { - for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) - sum += static_cast(n[i++]) * n[j--]; - (*this)[bigit_index] = static_cast(sum); - sum >>= bits::value; - } - 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(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; - } -}; - -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; - } -}; - -// 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 digits::result grisu_gen_digits( - fp value, uint64_t error, int& exp, gen_digits_handler& handler) { - const fp one(1ULL << -value.e, value.e); - // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be - // zero because it contains a product of two 64-bit numbers with MSB set (due - // to normalization) - 1, shifted right by at most 60 bits. - auto integral = static_cast(value.f >> -one.e); - FMT_ASSERT(integral != 0, ""); - FMT_ASSERT(integral == value.f >> -one.e, ""); - // The fractional part of scaled value (p2 in Grisu) c = value % one. - uint64_t fractional = value.f & (one.f - 1); - exp = count_digits(integral); // kappa in Grisu. - // Non-fixed formats require at least one digit and no precision adjustment. - if (handler.fixed) { - // Adjust fixed precision by exponent because it is relative to decimal - // point. - int precision_offset = exp + handler.exp10; - if (precision_offset > 0 && - handler.precision > max_value() - precision_offset) { - FMT_THROW(format_error("number is too big")); - } - handler.precision += precision_offset; - // 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 = impl_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(integral) << -one.e) + fractional; - auto result = handler.on_digit(static_cast('0' + digit), - impl_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('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; - } -} - -// A 128-bit integer type used internally, -struct uint128_wrapper { - uint128_wrapper() = default; - -#if FMT_USE_INT128 - uint128_t internal_; - - constexpr uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT - : internal_{static_cast(low) | - (static_cast(high) << 64)} {} - - constexpr uint128_wrapper(uint128_t u) : internal_{u} {} - - constexpr uint64_t high() const FMT_NOEXCEPT { - return uint64_t(internal_ >> 64); - } - constexpr uint64_t low() const FMT_NOEXCEPT { return uint64_t(internal_); } - - uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { - internal_ += n; - return *this; - } -#else - uint64_t high_; - uint64_t low_; - - constexpr uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT - : high_{high}, - low_{low} {} - - constexpr uint64_t high() const FMT_NOEXCEPT { return high_; } - constexpr uint64_t low() const FMT_NOEXCEPT { return low_; } - - uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { -# if defined(_MSC_VER) && defined(_M_X64) - unsigned char carry = _addcarry_u64(0, low_, n, &low_); - _addcarry_u64(carry, high_, 0, &high_); - return *this; -# else - uint64_t sum = low_ + n; - high_ += (sum < low_ ? 1 : 0); - low_ = sum; - return *this; -# endif - } -#endif -}; - -// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. -namespace dragonbox { // Computes 128-bit result of multiplication of two 64-bit unsigned integers. -inline uint128_wrapper umul128(uint64_t x, uint64_t y) FMT_NOEXCEPT { +inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept { #if FMT_USE_INT128 - return static_cast(x) * static_cast(y); + auto p = static_cast(x) * static_cast(y); + return {static_cast(p >> 64), static_cast(p)}; #elif defined(_MSC_VER) && defined(_M_X64) - uint128_wrapper result; - result.low_ = _umul128(x, y, &result.high_); + auto result = uint128_fallback(); + result.lo_ = _umul128(x, y, &result.hi_); return result; #else - const uint64_t mask = (uint64_t(1) << 32) - uint64_t(1); + const uint64_t mask = static_cast(max_value()); uint64_t a = x >> 32; uint64_t b = x & mask; @@ -863,10 +172,12 @@ inline uint128_wrapper umul128(uint64_t x, uint64_t y) FMT_NOEXCEPT { #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) FMT_NOEXCEPT { +inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { #if FMT_USE_INT128 - auto p = static_cast(x) * static_cast(y); + auto p = static_cast(x) * static_cast(y); return static_cast(p >> 64); #elif defined(_MSC_VER) && defined(_M_X64) return __umulh(x, y); @@ -875,170 +186,105 @@ inline uint64_t umul128_upper64(uint64_t x, uint64_t y) FMT_NOEXCEPT { #endif } -// Computes upper 64 bits of multiplication of a 64-bit unsigned integer and a +// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. -inline uint64_t umul192_upper64(uint64_t x, uint128_wrapper y) FMT_NOEXCEPT { - uint128_wrapper g0 = umul128(x, y.high()); - g0 += umul128_upper64(x, y.low()); - return g0.high(); +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 32 bits of multiplication of a 32-bit unsigned integer and a +// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a // 64-bit unsigned integer. -inline uint32_t umul96_upper32(uint32_t x, uint64_t y) FMT_NOEXCEPT { - return static_cast(umul128_upper64(x, y)); +inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept { + return umul128_upper64(static_cast(x) << 32, y); } -// Computes middle 64 bits of multiplication of a 64-bit unsigned integer and a +// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. -inline uint64_t umul192_middle64(uint64_t x, uint128_wrapper y) FMT_NOEXCEPT { - uint64_t g01 = x * y.high(); - uint64_t g10 = umul128_upper64(x, y.low()); - return g01 + g10; +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) FMT_NOEXCEPT { +inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept { return x * y; } -// Computes floor(log10(pow(2, e))) for e in [-1700, 1700] using the method from -// https://fmt.dev/papers/Grisu-Exact.pdf#page=5, section 3.4. -inline int floor_log10_pow2(int e) FMT_NOEXCEPT { - FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); - const int shift = 22; - return (e * static_cast(log10_2_significand >> (64 - shift))) >> shift; +// 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) FMT_NOEXCEPT { +inline int floor_log2_pow10(int e) noexcept { FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); - const uint64_t log2_10_integer_part = 3; - const uint64_t log2_10_fractional_digits = 0x5269e12f346e2bf9; - const int shift_amount = 19; - return (e * static_cast( - (log2_10_integer_part << shift_amount) | - (log2_10_fractional_digits >> (64 - shift_amount)))) >> - shift_amount; + return (e * 1741647) >> 19; } -inline int floor_log10_pow2_minus_log10_4_over_3(int e) FMT_NOEXCEPT { - FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); - const uint64_t log10_4_over_3_fractional_digits = 0x1ffbfc2bbc780375; - const int shift_amount = 22; - return (e * static_cast(log10_2_significand >> (64 - shift_amount)) - - static_cast(log10_4_over_3_fractional_digits >> - (64 - shift_amount))) >> - shift_amount; +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; } -// Returns true iff x is divisible by pow(2, exp). -inline bool divisible_by_power_of_2(uint32_t x, int exp) FMT_NOEXCEPT { - FMT_ASSERT(exp >= 1, ""); - FMT_ASSERT(x != 0, ""); -#ifdef FMT_BUILTIN_CTZ - return FMT_BUILTIN_CTZ(x) >= exp; -#else - return exp < num_bits() && x == ((x >> exp) << exp); -#endif -} -inline bool divisible_by_power_of_2(uint64_t x, int exp) FMT_NOEXCEPT { - FMT_ASSERT(exp >= 1, ""); - FMT_ASSERT(x != 0, ""); -#ifdef FMT_BUILTIN_CTZLL - return FMT_BUILTIN_CTZLL(x) >= exp; -#else - return exp < num_bits() && x == ((x >> exp) << exp); -#endif -} +static constexpr struct { + uint32_t divisor; + int shift_amount; +} div_small_pow10_infos[] = {{10, 16}, {100, 16}}; -// Table entry type for divisibility test. -template struct divtest_table_entry { - T mod_inv; - T max_quotient; -}; - -// Returns true iff x is divisible by pow(5, exp). -inline bool divisible_by_power_of_5(uint32_t x, int exp) FMT_NOEXCEPT { - FMT_ASSERT(exp <= 10, "too large exponent"); - static constexpr const divtest_table_entry divtest_table[] = { - {0x00000001, 0xffffffff}, {0xcccccccd, 0x33333333}, - {0xc28f5c29, 0x0a3d70a3}, {0x26e978d5, 0x020c49ba}, - {0x3afb7e91, 0x0068db8b}, {0x0bcbe61d, 0x0014f8b5}, - {0x68c26139, 0x000431bd}, {0xae8d46a5, 0x0000d6bf}, - {0x22e90e21, 0x00002af3}, {0x3a2e9c6d, 0x00000897}, - {0x3ed61f49, 0x000001b7}}; - return x * divtest_table[exp].mod_inv <= divtest_table[exp].max_quotient; -} -inline bool divisible_by_power_of_5(uint64_t x, int exp) FMT_NOEXCEPT { - FMT_ASSERT(exp <= 23, "too large exponent"); - static constexpr const divtest_table_entry divtest_table[] = { - {0x0000000000000001, 0xffffffffffffffff}, - {0xcccccccccccccccd, 0x3333333333333333}, - {0x8f5c28f5c28f5c29, 0x0a3d70a3d70a3d70}, - {0x1cac083126e978d5, 0x020c49ba5e353f7c}, - {0xd288ce703afb7e91, 0x0068db8bac710cb2}, - {0x5d4e8fb00bcbe61d, 0x0014f8b588e368f0}, - {0x790fb65668c26139, 0x000431bde82d7b63}, - {0xe5032477ae8d46a5, 0x0000d6bf94d5e57a}, - {0xc767074b22e90e21, 0x00002af31dc46118}, - {0x8e47ce423a2e9c6d, 0x0000089705f4136b}, - {0x4fa7f60d3ed61f49, 0x000001b7cdfd9d7b}, - {0x0fee64690c913975, 0x00000057f5ff85e5}, - {0x3662e0e1cf503eb1, 0x000000119799812d}, - {0xa47a2cf9f6433fbd, 0x0000000384b84d09}, - {0x54186f653140a659, 0x00000000b424dc35}, - {0x7738164770402145, 0x0000000024075f3d}, - {0xe4a4d1417cd9a041, 0x000000000734aca5}, - {0xc75429d9e5c5200d, 0x000000000170ef54}, - {0xc1773b91fac10669, 0x000000000049c977}, - {0x26b172506559ce15, 0x00000000000ec1e4}, - {0xd489e3a9addec2d1, 0x000000000002f394}, - {0x90e860bb892c8d5d, 0x000000000000971d}, - {0x502e79bf1b6f4f79, 0x0000000000001e39}, - {0xdcd618596be30fe5, 0x000000000000060b}}; - return x * divtest_table[exp].mod_inv <= divtest_table[exp].max_quotient; -} - -// Replaces n by floor(n / pow(5, N)) returning true if and only if n is -// divisible by pow(5, N). -// Precondition: n <= 2 * pow(5, N + 1). +// 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 -bool check_divisibility_and_divide_by_pow5(uint32_t& n) FMT_NOEXCEPT { - static constexpr struct { - uint32_t magic_number; - int bits_for_comparison; - uint32_t threshold; - int shift_amount; - } infos[] = {{0xcccd, 16, 0x3333, 18}, {0xa429, 8, 0x0a, 20}}; - constexpr auto info = infos[N - 1]; - n *= info.magic_number; - const uint32_t comparison_mask = (1u << info.bits_for_comparison) - 1; - bool result = (n & comparison_mask) <= info.threshold; +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 uint32_t small_division_by_pow10(uint32_t n) FMT_NOEXCEPT { - static constexpr struct { - uint32_t magic_number; - int shift_amount; - uint32_t divisor_times_10; - } infos[] = {{0xcccd, 19, 100}, {0xa3d8, 22, 1000}}; - constexpr auto info = infos[N - 1]; - FMT_ASSERT(n <= info.divisor_times_10, "n is too large"); - return n * info.magic_number >> info.shift_amount; +template 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) FMT_NOEXCEPT { - return n / float_info::big_divisor; +inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept { + // 1374389535 = ceil(2^37/100) + return static_cast((static_cast(n) * 1374389535) >> 37); } // Computes floor(n / 10^(kappa + 1)) (double) -inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) FMT_NOEXCEPT { - return umul128_upper64(n, 0x83126e978d4fdf3c) >> 9; +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 @@ -1048,7 +294,7 @@ template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; using cache_entry_type = uint64_t; - static uint64_t get_cached_power(int k) FMT_NOEXCEPT { + static uint64_t get_cached_power(int k) noexcept { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); static constexpr const uint64_t pow10_significands[] = { @@ -1071,54 +317,65 @@ template <> struct cache_accessor { 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0, - 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940984, - 0xa18f07d736b90be5, 0xc9f2c9cd04674ede, 0xfc6f7c4045812296, - 0x9dc5ada82b70b59d, 0xc5371912364ce305, 0xf684df56c3e01bc6, - 0x9a130b963a6c115c, 0xc097ce7bc90715b3, 0xf0bdc21abb48db20, - 0x96769950b50d88f4, 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd, - 0x92efd1b8d0cf37be, 0xb7abc627050305ad, 0xe596b7b0c643c719, - 0x8f7e32ce7bea5c6f, 0xb35dbf821ae4f38b, 0xe0352f62a19e306e}; + 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::min_k]; } - static carrier_uint compute_mul(carrier_uint u, - const cache_entry_type& cache) FMT_NOEXCEPT { - return umul96_upper32(u, cache); + 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(r >> 32), + static_cast(r) == 0}; } static uint32_t compute_delta(const cache_entry_type& cache, - int beta_minus_1) FMT_NOEXCEPT { - return static_cast(cache >> (64 - 1 - beta_minus_1)); + int beta) noexcept { + return static_cast(cache >> (64 - 1 - beta)); } - static bool compute_mul_parity(carrier_uint two_f, - const cache_entry_type& cache, - int beta_minus_1) FMT_NOEXCEPT { - FMT_ASSERT(beta_minus_1 >= 1, ""); - FMT_ASSERT(beta_minus_1 < 64, ""); + 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, ""); - return ((umul96_lower64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0; + auto r = umul96_lower64(two_f, cache); + return {((r >> (64 - beta)) & 1) != 0, + static_cast(r >> (32 - beta)) == 0}; } static carrier_uint compute_left_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + const cache_entry_type& cache, int beta) noexcept { return static_cast( - (cache - (cache >> (float_info::significand_bits + 2))) >> - (64 - float_info::significand_bits - 1 - beta_minus_1)); + (cache - (cache >> (num_significand_bits() + 2))) >> + (64 - num_significand_bits() - 1 - beta)); } static carrier_uint compute_right_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + const cache_entry_type& cache, int beta) noexcept { return static_cast( - (cache + (cache >> (float_info::significand_bits + 1))) >> - (64 - float_info::significand_bits - 1 - beta_minus_1)); + (cache + (cache >> (num_significand_bits() + 1))) >> + (64 - num_significand_bits() - 1 - beta)); } static carrier_uint compute_round_up_for_shorter_interval_case( - const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + const cache_entry_type& cache, int beta) noexcept { return (static_cast( - cache >> - (64 - float_info::significand_bits - 2 - beta_minus_1)) + + cache >> (64 - num_significand_bits() - 2 - beta)) + 1) / 2; } @@ -1126,13 +383,13 @@ template <> struct cache_accessor { template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; - using cache_entry_type = uint128_wrapper; + using cache_entry_type = uint128_fallback; - static uint128_wrapper get_cached_power(int k) FMT_NOEXCEPT { + static uint128_fallback get_cached_power(int k) noexcept { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); - static constexpr const uint128_wrapper pow10_significands[] = { + static constexpr const uint128_fallback pow10_significands[] = { #if FMT_USE_FULL_CACHE_DRAGONBOX {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0x9faacf3df73609b1, 0x77b191618c54e9ad}, @@ -1482,278 +739,278 @@ template <> struct cache_accessor { {0x85a36366eb71f041, 0x47a6da2b7f864750}, {0xa70c3c40a64e6c51, 0x999090b65f67d924}, {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d}, - {0x82818f1281ed449f, 0xbff8f10e7a8921a4}, - {0xa321f2d7226895c7, 0xaff72d52192b6a0d}, - {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490}, - {0xfee50b7025c36a08, 0x02f236d04753d5b4}, - {0x9f4f2726179a2245, 0x01d762422c946590}, - {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5}, - {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2}, - {0x9b934c3b330c8577, 0x63cc55f49f88eb2f}, - {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb}, - {0xf316271c7fc3908a, 0x8bef464e3945ef7a}, - {0x97edd871cfda3a56, 0x97758bf0e3cbb5ac}, - {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317}, - {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd}, - {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a}, - {0xb975d6b6ee39e436, 0xb3e2fd538e122b44}, - {0xe7d34c64a9c85d44, 0x60dbbca87196b616}, - {0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd}, - {0xb51d13aea4a488dd, 0x6babab6398bdbe41}, - {0xe264589a4dcdab14, 0xc696963c7eed2dd1}, - {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2}, - {0xb0de65388cc8ada8, 0x3b25a55f43294bcb}, - {0xdd15fe86affad912, 0x49ef0eb713f39ebe}, - {0x8a2dbf142dfcc7ab, 0x6e3569326c784337}, - {0xacb92ed9397bf996, 0x49c2c37f07965404}, - {0xd7e77a8f87daf7fb, 0xdc33745ec97be906}, - {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3}, - {0xa8acd7c0222311bc, 0xc40832ea0d68ce0c}, - {0xd2d80db02aabd62b, 0xf50a3fa490c30190}, - {0x83c7088e1aab65db, 0x792667c6da79e0fa}, - {0xa4b8cab1a1563f52, 0x577001b891185938}, - {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86}, - {0x80b05e5ac60b6178, 0x544f8158315b05b4}, - {0xa0dc75f1778e39d6, 0x696361ae3db1c721}, - {0xc913936dd571c84c, 0x03bc3a19cd1e38e9}, - {0xfb5878494ace3a5f, 0x04ab48a04065c723}, - {0x9d174b2dcec0e47b, 0x62eb0d64283f9c76}, - {0xc45d1df942711d9a, 0x3ba5d0bd324f8394}, - {0xf5746577930d6500, 0xca8f44ec7ee36479}, - {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb}, - {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e}, - {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e}, - {0x95d04aee3b80ece5, 0xbba1f1d158724a12}, - {0xbb445da9ca61281f, 0x2a8a6e45ae8edc97}, - {0xea1575143cf97226, 0xf52d09d71a3293bd}, - {0x924d692ca61be758, 0x593c2626705f9c56}, - {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c}, - {0xe498f455c38b997a, 0x0b6dfb9c0f956447}, - {0x8edf98b59a373fec, 0x4724bd4189bd5eac}, - {0xb2977ee300c50fe7, 0x58edec91ec2cb657}, - {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed}, - {0x8b865b215899f46c, 0xbd79e0d20082ee74}, - {0xae67f1e9aec07187, 0xecd8590680a3aa11}, - {0xda01ee641a708de9, 0xe80e6f4820cc9495}, - {0x884134fe908658b2, 0x3109058d147fdcdd}, - {0xaa51823e34a7eede, 0xbd4b46f0599fd415}, - {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a}, - {0x850fadc09923329e, 0x03e2cf6bc604ddb0}, - {0xa6539930bf6bff45, 0x84db8346b786151c}, - {0xcfe87f7cef46ff16, 0xe612641865679a63}, - {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e}, - {0xa26da3999aef7749, 0xe3be5e330f38f09d}, - {0xcb090c8001ab551c, 0x5cadf5bfd3072cc5}, - {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6}, - {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa}, - {0xc646d63501a1511d, 0xb281e1fd541501b8}, - {0xf7d88bc24209a565, 0x1f225a7ca91a4226}, - {0x9ae757596946075f, 0x3375788de9b06958}, - {0xc1a12d2fc3978937, 0x0052d6b1641c83ae}, - {0xf209787bb47d6b84, 0xc0678c5dbd23a49a}, - {0x9745eb4d50ce6332, 0xf840b7ba963646e0}, - {0xbd176620a501fbff, 0xb650e5a93bc3d898}, - {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe}, - {0x93ba47c980e98cdf, 0xc66f336c36b10137}, - {0xb8a8d9bbe123f017, 0xb80b0047445d4184}, - {0xe6d3102ad96cec1d, 0xa60dc059157491e5}, - {0x9043ea1ac7e41392, 0x87c89837ad68db2f}, - {0xb454e4a179dd1877, 0x29babe4598c311fb}, - {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a}, - {0x8ce2529e2734bb1d, 0x1899e4a65f58660c}, - {0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f}, - {0xdc21a1171d42645d, 0x76707543f4fa1f73}, - {0x899504ae72497eba, 0x6a06494a791c53a8}, - {0xabfa45da0edbde69, 0x0487db9d17636892}, - {0xd6f8d7509292d603, 0x45a9d2845d3c42b6}, - {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2}, - {0xa7f26836f282b732, 0x8e6cac7768d7141e}, - {0xd1ef0244af2364ff, 0x3207d795430cd926}, - {0x8335616aed761f1f, 0x7f44e6bd49e807b8}, - {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6}, - {0xcd036837130890a1, 0x36dba887c37a8c0f}, - {0x802221226be55a64, 0xc2494954da2c9789}, - {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c}, - {0xc83553c5c8965d3d, 0x6f92829494e5acc7}, - {0xfa42a8b73abbf48c, 0xcb772339ba1f17f9}, - {0x9c69a97284b578d7, 0xff2a760414536efb}, - {0xc38413cf25e2d70d, 0xfef5138519684aba}, - {0xf46518c2ef5b8cd1, 0x7eb258665fc25d69}, - {0x98bf2f79d5993802, 0xef2f773ffbd97a61}, - {0xbeeefb584aff8603, 0xaafb550ffacfd8fa}, - {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38}, - {0x952ab45cfa97a0b2, 0xdd945a747bf26183}, - {0xba756174393d88df, 0x94f971119aeef9e4}, - {0xe912b9d1478ceb17, 0x7a37cd5601aab85d}, - {0x91abb422ccb812ee, 0xac62e055c10ab33a}, - {0xb616a12b7fe617aa, 0x577b986b314d6009}, - {0xe39c49765fdf9d94, 0xed5a7e85fda0b80b}, - {0x8e41ade9fbebc27d, 0x14588f13be847307}, - {0xb1d219647ae6b31c, 0x596eb2d8ae258fc8}, - {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb}, - {0x8aec23d680043bee, 0x25de7bb9480d5854}, - {0xada72ccc20054ae9, 0xaf561aa79a10ae6a}, - {0xd910f7ff28069da4, 0x1b2ba1518094da04}, - {0x87aa9aff79042286, 0x90fb44d2f05d0842}, - {0xa99541bf57452b28, 0x353a1607ac744a53}, - {0xd3fa922f2d1675f2, 0x42889b8997915ce8}, - {0x847c9b5d7c2e09b7, 0x69956135febada11}, - {0xa59bc234db398c25, 0x43fab9837e699095}, - {0xcf02b2c21207ef2e, 0x94f967e45e03f4bb}, - {0x8161afb94b44f57d, 0x1d1be0eebac278f5}, - {0xa1ba1ba79e1632dc, 0x6462d92a69731732}, - {0xca28a291859bbf93, 0x7d7b8f7503cfdcfe}, - {0xfcb2cb35e702af78, 0x5cda735244c3d43e}, - {0x9defbf01b061adab, 0x3a0888136afa64a7}, - {0xc56baec21c7a1916, 0x088aaa1845b8fdd0}, - {0xf6c69a72a3989f5b, 0x8aad549e57273d45}, - {0x9a3c2087a63f6399, 0x36ac54e2f678864b}, - {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd}, - {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5}, - {0x969eb7c47859e743, 0x9f644ae5a4b1b325}, - {0xbc4665b596706114, 0x873d5d9f0dde1fee}, - {0xeb57ff22fc0c7959, 0xa90cb506d155a7ea}, - {0x9316ff75dd87cbd8, 0x09a7f12442d588f2}, - {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb2f}, - {0xe5d3ef282a242e81, 0x8f1668c8a86da5fa}, - {0x8fa475791a569d10, 0xf96e017d694487bc}, - {0xb38d92d760ec4455, 0x37c981dcc395a9ac}, - {0xe070f78d3927556a, 0x85bbe253f47b1417}, - {0x8c469ab843b89562, 0x93956d7478ccec8e}, - {0xaf58416654a6babb, 0x387ac8d1970027b2}, - {0xdb2e51bfe9d0696a, 0x06997b05fcc0319e}, - {0x88fcf317f22241e2, 0x441fece3bdf81f03}, - {0xab3c2fddeeaad25a, 0xd527e81cad7626c3}, - {0xd60b3bd56a5586f1, 0x8a71e223d8d3b074}, - {0x85c7056562757456, 0xf6872d5667844e49}, - {0xa738c6bebb12d16c, 0xb428f8ac016561db}, - {0xd106f86e69d785c7, 0xe13336d701beba52}, - {0x82a45b450226b39c, 0xecc0024661173473}, - {0xa34d721642b06084, 0x27f002d7f95d0190}, - {0xcc20ce9bd35c78a5, 0x31ec038df7b441f4}, - {0xff290242c83396ce, 0x7e67047175a15271}, - {0x9f79a169bd203e41, 0x0f0062c6e984d386}, - {0xc75809c42c684dd1, 0x52c07b78a3e60868}, - {0xf92e0c3537826145, 0xa7709a56ccdf8a82}, - {0x9bbcc7a142b17ccb, 0x88a66076400bb691}, - {0xc2abf989935ddbfe, 0x6acff893d00ea435}, - {0xf356f7ebf83552fe, 0x0583f6b8c4124d43}, - {0x98165af37b2153de, 0xc3727a337a8b704a}, - {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c}, - {0xeda2ee1c7064130c, 0x1162def06f79df73}, - {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8}, - {0xb9a74a0637ce2ee1, 0x6d953e2bd7173692}, - {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437}, - {0x910ab1d4db9914a0, 0x1d9c9892400a22a2}, - {0xb54d5e4a127f59c8, 0x2503beb6d00cab4b}, - {0xe2a0b5dc971f303a, 0x2e44ae64840fd61d}, - {0x8da471a9de737e24, 0x5ceaecfed289e5d2}, - {0xb10d8e1456105dad, 0x7425a83e872c5f47}, - {0xdd50f1996b947518, 0xd12f124e28f77719}, - {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f}, - {0xace73cbfdc0bfb7b, 0x636cc64d1001550b}, - {0xd8210befd30efa5a, 0x3c47f7e05401aa4e}, - {0x8714a775e3e95c78, 0x65acfaec34810a71}, - {0xa8d9d1535ce3b396, 0x7f1839a741a14d0d}, - {0xd31045a8341ca07c, 0x1ede48111209a050}, - {0x83ea2b892091e44d, 0x934aed0aab460432}, - {0xa4e4b66b68b65d60, 0xf81da84d5617853f}, - {0xce1de40642e3f4b9, 0x36251260ab9d668e}, - {0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019}, - {0xa1075a24e4421730, 0xb24cf65b8612f81f}, - {0xc94930ae1d529cfc, 0xdee033f26797b627}, - {0xfb9b7cd9a4a7443c, 0x169840ef017da3b1}, - {0x9d412e0806e88aa5, 0x8e1f289560ee864e}, - {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2}, - {0xf5b5d7ec8acb58a2, 0xae10af696774b1db}, - {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29}, - {0xbff610b0cc6edd3f, 0x17fd090a58d32af3}, - {0xeff394dcff8a948e, 0xddfc4b4cef07f5b0}, - {0x95f83d0a1fb69cd9, 0x4abdaf101564f98e}, - {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1}, - {0xea53df5fd18d5513, 0x84c86189216dc5ed}, - {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4}, - {0xb7118682dbb66a77, 0x3fbc8c33221dc2a1}, - {0xe4d5e82392a40515, 0x0fabaf3feaa5334a}, - {0x8f05b1163ba6832d, 0x29cb4d87f2a7400e}, - {0xb2c71d5bca9023f8, 0x743e20e9ef511012}, - {0xdf78e4b2bd342cf6, 0x914da9246b255416}, - {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e}, - {0xae9672aba3d0c320, 0xa184ac2473b529b1}, - {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e}, - {0x8865899617fb1871, 0x7e2fa67c7a658892}, - {0xaa7eebfb9df9de8d, 0xddbb901b98feeab7}, - {0xd51ea6fa85785631, 0x552a74227f3ea565}, - {0x8533285c936b35de, 0xd53a88958f87275f}, - {0xa67ff273b8460356, 0x8a892abaf368f137}, - {0xd01fef10a657842c, 0x2d2b7569b0432d85}, - {0x8213f56a67f6b29b, 0x9c3b29620e29fc73}, - {0xa298f2c501f45f42, 0x8349f3ba91b47b8f}, - {0xcb3f2f7642717713, 0x241c70a936219a73}, - {0xfe0efb53d30dd4d7, 0xed238cd383aa0110}, - {0x9ec95d1463e8a506, 0xf4363804324a40aa}, - {0xc67bb4597ce2ce48, 0xb143c6053edcd0d5}, - {0xf81aa16fdc1b81da, 0xdd94b7868e94050a}, - {0x9b10a4e5e9913128, 0xca7cf2b4191c8326}, - {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0}, - {0xf24a01a73cf2dccf, 0xbc633b39673c8cec}, - {0x976e41088617ca01, 0xd5be0503e085d813}, - {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18}, - {0xec9c459d51852ba2, 0xddf8e7d60ed1219e}, - {0x93e1ab8252f33b45, 0xcabb90e5c942b503}, - {0xb8da1662e7b00a17, 0x3d6a751f3b936243}, - {0xe7109bfba19c0c9d, 0x0cc512670a783ad4}, - {0x906a617d450187e2, 0x27fb2b80668b24c5}, - {0xb484f9dc9641e9da, 0xb1f9f660802dedf6}, - {0xe1a63853bbd26451, 0x5e7873f8a0396973}, - {0x8d07e33455637eb2, 0xdb0b487b6423e1e8}, - {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62}, - {0xdc5c5301c56b75f7, 0x7641a140cc7810fb}, - {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d}, - {0xac2820d9623bf429, 0x546345fa9fbdcd44}, - {0xd732290fbacaf133, 0xa97c177947ad4095}, - {0x867f59a9d4bed6c0, 0x49ed8eabcccc485d}, - {0xa81f301449ee8c70, 0x5c68f256bfff5a74}, - {0xd226fc195c6a2f8c, 0x73832eec6fff3111}, - {0x83585d8fd9c25db7, 0xc831fd53c5ff7eab}, - {0xa42e74f3d032f525, 0xba3e7ca8b77f5e55}, - {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb}, - {0x80444b5e7aa7cf85, 0x7980d163cf5b81b3}, - {0xa0555e361951c366, 0xd7e105bcc332621f}, - {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7}, - {0xfa856334878fc150, 0xb14f98f6f0feb951}, - {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3}, - {0xc3b8358109e84f07, 0x0a862f80ec4700c8}, - {0xf4a642e14c6262c8, 0xcd27bb612758c0fa}, - {0x98e7e9cccfbd7dbd, 0x8038d51cb897789c}, - {0xbf21e44003acdd2c, 0xe0470a63e6bd56c3}, - {0xeeea5d5004981478, 0x1858ccfce06cac74}, - {0x95527a5202df0ccb, 0x0f37801e0c43ebc8}, - {0xbaa718e68396cffd, 0xd30560258f54e6ba}, - {0xe950df20247c83fd, 0x47c6b82ef32a2069}, - {0x91d28b7416cdd27e, 0x4cdc331d57fa5441}, - {0xb6472e511c81471d, 0xe0133fe4adf8e952}, - {0xe3d8f9e563a198e5, 0x58180fddd97723a6}, - {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648}, - {0xb201833b35d63f73, 0x2cd2cc6551e513da}, - {0xde81e40a034bcf4f, 0xf8077f7ea65e58d1}, - {0x8b112e86420f6191, 0xfb04afaf27faf782}, - {0xadd57a27d29339f6, 0x79c5db9af1f9b563}, - {0xd94ad8b1c7380874, 0x18375281ae7822bc}, - {0x87cec76f1c830548, 0x8f2293910d0b15b5}, - {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb22}, - {0xd433179d9c8cb841, 0x5fa60692a46151eb}, - {0x849feec281d7f328, 0xdbc7c41ba6bcd333}, - {0xa5c7ea73224deff3, 0x12b9b522906c0800}, - {0xcf39e50feae16bef, 0xd768226b34870a00}, - {0x81842f29f2cce375, 0xe6a1158300d46640}, - {0xa1e53af46f801c53, 0x60495ae3c1097fd0}, - {0xca5e89b18b602368, 0x385bb19cb14bdfc4}, - {0xfcf62c1dee382c42, 0x46729e03dd9ed7b5}, - {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d1}, - {0xc5a05277621be293, 0xc7098b7305241885}, + {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, - 0xb8cbee4fc66d1ea7 } + 0xb8cbee4fc66d1ea8 } #else {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, @@ -1768,17 +1025,17 @@ template <> struct cache_accessor { {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, {0xc350000000000000, 0x0000000000000000}, {0x9dc5ada82b70b59d, 0xf020000000000000}, - {0xfee50b7025c36a08, 0x02f236d04753d5b4}, - {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86}, - {0xa6539930bf6bff45, 0x84db8346b786151c}, - {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2}, - {0xd910f7ff28069da4, 0x1b2ba1518094da04}, - {0xaf58416654a6babb, 0x387ac8d1970027b2}, - {0x8da471a9de737e24, 0x5ceaecfed289e5d2}, - {0xe4d5e82392a40515, 0x0fabaf3feaa5334a}, - {0xb8da1662e7b00a17, 0x3d6a751f3b936243}, + {0xfee50b7025c36a08, 0x02f236d04753d5b5}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87}, + {0xa6539930bf6bff45, 0x84db8346b786151d}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3}, + {0xd910f7ff28069da4, 0x1b2ba1518094da05}, + {0xaf58416654a6babb, 0x387ac8d1970027b3}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d3}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936244}, { 0x95527a5202df0ccb, - 0x0f37801e0c43ebc8 } + 0x0f37801e0c43ebc9 } #endif }; @@ -1796,15 +1053,6 @@ template <> struct cache_accessor { 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, 0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9}; - static constexpr const uint32_t pow10_recovery_errors[] = { - 0x50001400, 0x54044100, 0x54014555, 0x55954415, 0x54115555, 0x00000001, - 0x50000000, 0x00104000, 0x54010004, 0x05004001, 0x55555544, 0x41545555, - 0x54040551, 0x15445545, 0x51555514, 0x10000015, 0x00101100, 0x01100015, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04450514, 0x45414110, - 0x55555145, 0x50544050, 0x15040155, 0x11054140, 0x50111514, 0x11451454, - 0x00400541, 0x00000000, 0x55555450, 0x10056551, 0x10054011, 0x55551014, - 0x69514555, 0x05151109, 0x00155555}; - static const int compression_ratio = 27; // Compute base index. @@ -1813,7 +1061,7 @@ template <> struct cache_accessor { int offset = k - kb; // Get base cache. - uint128_wrapper base_cache = pow10_significands[cache_index]; + uint128_fallback base_cache = pow10_significands[cache_index]; if (offset == 0) return base_cache; // Compute the required amount of bit-shift. @@ -1822,9 +1070,8 @@ template <> struct cache_accessor { // Try to recover the real cache. uint64_t pow5 = powers_of_5_64[offset]; - uint128_wrapper recovered_cache = umul128(base_cache.high(), pow5); - uint128_wrapper middle_low = - umul128(base_cache.low() - (kb < 0 ? 1u : 0u), pow5); + uint128_fallback recovered_cache = umul128(base_cache.high(), pow5); + uint128_fallback middle_low = umul128(base_cache.low(), pow5); recovered_cache += middle_low.high(); @@ -1832,60 +1079,60 @@ template <> struct cache_accessor { uint64_t middle_to_low = recovered_cache.low() << (64 - alpha); recovered_cache = - uint128_wrapper{(recovered_cache.low() >> alpha) | high_to_middle, - ((middle_low.low() >> alpha) | middle_to_low)}; - - if (kb < 0) recovered_cache += 1; - - // Get error. - int error_idx = (k - float_info::min_k) / 16; - uint32_t error = (pow10_recovery_errors[error_idx] >> - ((k - float_info::min_k) % 16) * 2) & - 0x3; - - // Add the error back. - FMT_ASSERT(recovered_cache.low() + error >= recovered_cache.low(), ""); - return {recovered_cache.high(), recovered_cache.low() + error}; + 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 } - static carrier_uint compute_mul(carrier_uint u, - const cache_entry_type& cache) FMT_NOEXCEPT { - return umul192_upper64(u, cache); + 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_minus_1) FMT_NOEXCEPT { - return static_cast(cache.high() >> (64 - 1 - beta_minus_1)); + int beta) noexcept { + return static_cast(cache.high() >> (64 - 1 - beta)); } - static bool compute_mul_parity(carrier_uint two_f, - const cache_entry_type& cache, - int beta_minus_1) FMT_NOEXCEPT { - FMT_ASSERT(beta_minus_1 >= 1, ""); - FMT_ASSERT(beta_minus_1 < 64, ""); + 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, ""); - return ((umul192_middle64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0; + 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_minus_1) FMT_NOEXCEPT { + const cache_entry_type& cache, int beta) noexcept { return (cache.high() - - (cache.high() >> (float_info::significand_bits + 2))) >> - (64 - float_info::significand_bits - 1 - beta_minus_1); + (cache.high() >> (num_significand_bits() + 2))) >> + (64 - num_significand_bits() - 1 - beta); } static carrier_uint compute_right_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + const cache_entry_type& cache, int beta) noexcept { return (cache.high() + - (cache.high() >> (float_info::significand_bits + 1))) >> - (64 - float_info::significand_bits - 1 - beta_minus_1); + (cache.high() >> (num_significand_bits() + 1))) >> + (64 - num_significand_bits() - 1 - beta); } static carrier_uint compute_round_up_for_shorter_interval_case( - const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { - return ((cache.high() >> - (64 - float_info::significand_bits - 2 - beta_minus_1)) + + const cache_entry_type& cache, int beta) noexcept { + return ((cache.high() >> (64 - num_significand_bits() - 2 - beta)) + 1) / 2; } @@ -1893,166 +1140,104 @@ template <> struct cache_accessor { // Various integer checks template -bool is_left_endpoint_integer_shorter_interval(int exponent) FMT_NOEXCEPT { - return exponent >= - float_info< - T>::case_shorter_interval_left_endpoint_lower_threshold && - exponent <= - float_info::case_shorter_interval_left_endpoint_upper_threshold; -} -template -bool is_endpoint_integer(typename float_info::carrier_uint two_f, - int exponent, int minus_k) FMT_NOEXCEPT { - if (exponent < float_info::case_fc_pm_half_lower_threshold) return false; - // For k >= 0. - if (exponent <= float_info::case_fc_pm_half_upper_threshold) return true; - // For k < 0. - if (exponent > float_info::divisibility_check_by_5_threshold) return false; - return divisible_by_power_of_5(two_f, minus_k); -} - -template -bool is_center_integer(typename float_info::carrier_uint two_f, int exponent, - int minus_k) FMT_NOEXCEPT { - // Exponent for 5 is negative. - if (exponent > float_info::divisibility_check_by_5_threshold) return false; - if (exponent > float_info::case_fc_upper_threshold) - return divisible_by_power_of_5(two_f, minus_k); - // Both exponents are nonnegative. - if (exponent >= float_info::case_fc_lower_threshold) return true; - // Exponent for 2 is negative. - return divisible_by_power_of_2(two_f, minus_k - exponent + 1); +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) FMT_NOEXCEPT { -#ifdef FMT_BUILTIN_CTZ - int t = FMT_BUILTIN_CTZ(n); -#else - int t = ctz(n); -#endif - if (t > float_info::max_trailing_zeros) - t = float_info::max_trailing_zeros; - - const uint32_t mod_inv1 = 0xcccccccd; - const uint32_t max_quotient1 = 0x33333333; - const uint32_t mod_inv2 = 0xc28f5c29; - const uint32_t max_quotient2 = 0x0a3d70a3; +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; - for (; s < t - 1; s += 2) { - if (n * mod_inv2 > max_quotient2) break; - n *= mod_inv2; + while (true) { + auto q = rotr(n * mod_inv_25, 2); + if (q > max_value() / 100) break; + n = q; + s += 2; } - if (s < t && n * mod_inv1 <= max_quotient1) { - n *= mod_inv1; - ++s; + auto q = rotr(n * mod_inv_5, 1); + if (q <= max_value() / 10) { + n = q; + s |= 1; } - n >>= s; + return s; } // Removes trailing zeros and returns the number of zeros removed (double) -FMT_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT { -#ifdef FMT_BUILTIN_CTZLL - int t = FMT_BUILTIN_CTZLL(n); -#else - int t = ctzll(n); -#endif - if (t > float_info::max_trailing_zeros) - t = float_info::max_trailing_zeros; - // Divide by 10^8 and reduce to 32-bits - // Since ret_value.significand <= (2^64 - 1) / 1000 < 10^17, - // both of the quotient and the r should fit in 32-bits +FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { + FMT_ASSERT(n != 0, ""); - const uint32_t mod_inv1 = 0xcccccccd; - const uint32_t max_quotient1 = 0x33333333; - const uint64_t mod_inv8 = 0xc767074b22e90e21; - const uint64_t max_quotient8 = 0x00002af31dc46118; + // This magic number is ceil(2^90 / 10^8). + constexpr uint64_t magic_number = 12379400392853802749ull; + auto nm = umul128(n, magic_number); - // If the number is divisible by 1'0000'0000, work with the quotient - if (t >= 8) { - auto quotient_candidate = n * mod_inv8; + // 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(nm.high() >> (90 - 64)); - if (quotient_candidate <= max_quotient8) { - auto quotient = static_cast(quotient_candidate >> 8); + const uint32_t mod_inv_5 = 0xcccccccd; + const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5; - int s = 8; - for (; s < t; ++s) { - if (quotient * mod_inv1 > max_quotient1) break; - quotient *= mod_inv1; - } - quotient >>= (s - 8); - n = quotient; - return s; + int s = 8; + while (true) { + auto q = rotr(n32 * mod_inv_25, 2); + if (q > max_value() / 100) break; + n32 = q; + s += 2; } + auto q = rotr(n32 * mod_inv_5, 1); + if (q <= max_value() / 10) { + n32 = q; + s |= 1; + } + + n = n32; + return s; } - // Otherwise, work with the remainder - auto quotient = static_cast(n / 100000000); - auto remainder = static_cast(n - 100000000 * quotient); + // 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; - if (t == 0 || remainder * mod_inv1 > max_quotient1) { - return 0; + int s = 0; + while (true) { + auto q = rotr(n * mod_inv_25, 2); + if (q > max_value() / 100) break; + n = q; + s += 2; } - remainder *= mod_inv1; - - if (t == 1 || remainder * mod_inv1 > max_quotient1) { - n = (remainder >> 1) + quotient * 10000000ull; - return 1; + auto q = rotr(n * mod_inv_5, 1); + if (q <= max_value() / 10) { + n = q; + s |= 1; } - remainder *= mod_inv1; - if (t == 2 || remainder * mod_inv1 > max_quotient1) { - n = (remainder >> 2) + quotient * 1000000ull; - return 2; - } - remainder *= mod_inv1; - - if (t == 3 || remainder * mod_inv1 > max_quotient1) { - n = (remainder >> 3) + quotient * 100000ull; - return 3; - } - remainder *= mod_inv1; - - if (t == 4 || remainder * mod_inv1 > max_quotient1) { - n = (remainder >> 4) + quotient * 10000ull; - return 4; - } - remainder *= mod_inv1; - - if (t == 5 || remainder * mod_inv1 > max_quotient1) { - n = (remainder >> 5) + quotient * 1000ull; - return 5; - } - remainder *= mod_inv1; - - if (t == 6 || remainder * mod_inv1 > max_quotient1) { - n = (remainder >> 6) + quotient * 100ull; - return 6; - } - remainder *= mod_inv1; - - n = (remainder >> 7) + quotient * 10ull; - return 7; + return s; } // The main algorithm for shorter interval case template -FMT_INLINE decimal_fp shorter_interval_case(int exponent) FMT_NOEXCEPT { +FMT_INLINE decimal_fp shorter_interval_case(int exponent) noexcept { decimal_fp ret_value; // Compute k and beta const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); - const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k); + const int beta = exponent + floor_log2_pow10(-minus_k); // Compute xi and zi using cache_entry_type = typename cache_accessor::cache_entry_type; const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); auto xi = cache_accessor::compute_left_endpoint_for_shorter_interval_case( - cache, beta_minus_1); + cache, beta); auto zi = cache_accessor::compute_right_endpoint_for_shorter_interval_case( - cache, beta_minus_1); + cache, beta); // If the left endpoint is not an integer, increase it if (!is_left_endpoint_integer_shorter_interval(exponent)) ++xi; @@ -2069,8 +1254,8 @@ FMT_INLINE decimal_fp shorter_interval_case(int exponent) FMT_NOEXCEPT { // Otherwise, compute the round-up of y ret_value.significand = - cache_accessor::compute_round_up_for_shorter_interval_case( - cache, beta_minus_1); + cache_accessor::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 @@ -2085,7 +1270,7 @@ FMT_INLINE decimal_fp shorter_interval_case(int exponent) FMT_NOEXCEPT { return ret_value; } -template decimal_fp to_decimal(T x) FMT_NOEXCEPT { +template decimal_fp to_decimal(T x) noexcept { // Step 1: integer promotion & Schubfach multiplier calculation. using carrier_uint = typename float_info::carrier_uint; @@ -2094,23 +1279,25 @@ template decimal_fp to_decimal(T x) FMT_NOEXCEPT { // Extract significand bits and exponent bits. const carrier_uint significand_mask = - (static_cast(1) << float_info::significand_bits) - 1; + (static_cast(1) << num_significand_bits()) - 1; carrier_uint significand = (br & significand_mask); - int exponent = static_cast((br & exponent_mask()) >> - float_info::significand_bits); + int exponent = + static_cast((br & exponent_mask()) >> num_significand_bits()); if (exponent != 0) { // Check if normal. - exponent += float_info::exponent_bias - float_info::significand_bits; + exponent -= exponent_bias() + num_significand_bits(); // 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(exponent); - significand |= - (static_cast(1) << float_info::significand_bits); + significand |= (static_cast(1) << num_significand_bits()); } else { // Subnormal case; the interval is always regular. if (significand == 0) return {0, 0}; - exponent = float_info::min_exponent - float_info::significand_bits; + exponent = + std::numeric_limits::min_exponent - num_significand_bits() - 1; } const bool include_left_endpoint = (significand % 2 == 0); @@ -2119,413 +1306,131 @@ template decimal_fp to_decimal(T x) FMT_NOEXCEPT { // Compute k and beta. const int minus_k = floor_log10_pow2(exponent) - float_info::kappa; const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); - const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k); + const int beta = exponent + floor_log2_pow10(-minus_k); - // Compute zi and deltai + // Compute zi and deltai. // 10^kappa <= deltai < 10^(kappa + 1) - const uint32_t deltai = cache_accessor::compute_delta(cache, beta_minus_1); + const uint32_t deltai = cache_accessor::compute_delta(cache, beta); const carrier_uint two_fc = significand << 1; - const carrier_uint two_fr = two_fc | 1; - const carrier_uint zi = - cache_accessor::compute_mul(two_fr << beta_minus_1, cache); - // Step 2: Try larger divisor; remove trailing zeros if necessary + // 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::compute_mul_result z_mul = + cache_accessor::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 + // better than the compiler; we are computing zi / big_divisor here. decimal_fp ret_value; - ret_value.significand = divide_by_10_to_kappa_plus_1(zi); - uint32_t r = static_cast(zi - float_info::big_divisor * - ret_value.significand); + ret_value.significand = divide_by_10_to_kappa_plus_1(z_mul.result); + uint32_t r = static_cast(z_mul.result - float_info::big_divisor * + ret_value.significand); - if (r > deltai) { - goto small_divisor_case_label; - } else if (r < deltai) { - // Exclude the right endpoint if necessary - if (r == 0 && !include_right_endpoint && - is_endpoint_integer(two_fr, exponent, minus_k)) { + 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::big_divisor; goto small_divisor_case_label; } + } else if (r > deltai) { + goto small_divisor_case_label; } else { - // r == deltai; compare fractional parts - // Check conditions in the order different from the paper - // to take advantage of short-circuiting + // r == deltai; compare fractional parts. const carrier_uint two_fl = two_fc - 1; - if ((!include_left_endpoint || - !is_endpoint_integer(two_fl, exponent, minus_k)) && - !cache_accessor::compute_mul_parity(two_fl, cache, beta_minus_1)) { - goto small_divisor_case_label; + + if (!include_left_endpoint || + exponent < float_info::case_fc_pm_half_lower_threshold || + exponent > float_info::divisibility_check_by_5_threshold) { + // If the left endpoint is not included, the condition for + // success is z^(f) < delta^(f) (odd parity). + // Otherwise, the inequalities on exponent ensure that + // x is not an integer, so if z^(f) >= delta^(f) (even parity), we in fact + // have strict inequality. + if (!cache_accessor::compute_mul_parity(two_fl, cache, beta).parity) { + goto small_divisor_case_label; + } + } else { + const typename cache_accessor::compute_mul_parity_result x_mul = + cache_accessor::compute_mul_parity(two_fl, cache, beta); + if (!x_mul.parity && !x_mul.is_integer) { + goto small_divisor_case_label; + } } } ret_value.exponent = minus_k + float_info::kappa + 1; - // We may need to remove trailing zeros + // 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 + // Step 3: Find the significand with the smaller divisor. small_divisor_case_label: ret_value.significand *= 10; ret_value.exponent = minus_k + float_info::kappa; - const uint32_t mask = (1u << float_info::kappa) - 1; - auto dist = r - (deltai / 2) + (float_info::small_divisor / 2); + uint32_t dist = r - (deltai / 2) + (float_info::small_divisor / 2); + const bool approx_y_parity = + ((dist ^ (float_info::small_divisor / 2)) & 1) != 0; - // Is dist divisible by 2^kappa? - if ((dist & mask) == 0) { - const bool approx_y_parity = - ((dist ^ (float_info::small_divisor / 2)) & 1) != 0; - dist >>= float_info::kappa; + // Is dist divisible by 10^kappa? + const bool divisible_by_small_divisor = + check_divisibility_and_divide_by_pow10::kappa>(dist); - // Is dist divisible by 5^kappa? - if (check_divisibility_and_divide_by_pow5::kappa>(dist)) { - ret_value.significand += dist; + // Add dist / 10^kappa to the significand. + ret_value.significand += dist; - // 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 - if (cache_accessor::compute_mul_parity(two_fc, cache, beta_minus_1) != - approx_y_parity) { - --ret_value.significand; - } else { - // If z^(f) >= epsilon^(f), we might have a tie - // when z^(f) == epsilon^(f), or equivalently, when y is an integer - if (is_center_integer(two_fc, exponent, minus_k)) { - ret_value.significand = ret_value.significand % 2 == 0 - ? ret_value.significand - : ret_value.significand - 1; - } - } - } - // Is dist not divisible by 5^kappa? - else { - ret_value.significand += dist; - } - } - // Is dist not divisible by 2^kappa? - else { - // Since we know dist is small, we might be able to optimize the division - // better than the compiler; we are computing dist / small_divisor here - ret_value.significand += - small_division_by_pow10::kappa>(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::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 -// 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(fp value, bool is_predecessor_closer, - int num_digits, buffer& buf, - int& exp10) { - bigint numerator; // 2 * R in (FPP)^2. - bigint denominator; // 2 * S in (FPP)^2. - // lower and upper are differences between value and corresponding boundaries. - bigint lower; // (M^- in (FPP)^2). - bigint upper_store; // upper's value if different from lower. - bigint* upper = nullptr; // (M^+ in (FPP)^2). - // 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. - int shift = is_predecessor_closer ? 2 : 1; - uint64_t significand = value.f << shift; - if (value.e >= 0) { - numerator.assign(significand); - numerator <<= value.e; - lower.assign(1); - lower <<= value.e; - if (shift != 1) { - upper_store.assign(1); - upper_store <<= value.e + 1; - upper = &upper_store; - } - denominator.assign_pow10(exp10); - denominator <<= shift; - } else if (exp10 < 0) { - numerator.assign_pow10(-exp10); - lower.assign(numerator); - if (shift != 1) { - upper_store.assign(numerator); - upper_store <<= 1; - upper = &upper_store; - } - numerator *= significand; - denominator.assign(1); - denominator <<= shift - value.e; - } else { - numerator.assign(significand); - denominator.assign_pow10(exp10); - denominator <<= shift - value.e; - lower.assign(1); - if (shift != 1) { - upper_store.assign(1ULL << 1); - upper = &upper_store; - } - } - // Invariant: value == (numerator / denominator) * pow(10, exp10). - if (num_digits < 0) { - // Generate the shortest representation. - if (!upper) upper = &lower; - bool even = (value.f & 1) == 0; - num_digits = 0; - char* data = buf.data(); - for (;;) { - int digit = numerator.divmod_assign(denominator); - bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. - // numerator + upper >[=] pow10: - bool high = add_compare(numerator, *upper, denominator) + even > 0; - data[num_digits++] = static_cast('0' + digit); - if (low || high) { - if (!low) { - ++data[num_digits - 1]; - } else if (high) { - int result = add_compare(numerator, numerator, denominator); - // Round half to even. - if (result > 0 || (result == 0 && (digit % 2) != 0)) - ++data[num_digits - 1]; - } - buf.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('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('0' + digit); +#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; } - -template -FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, - float_specs specs, - buffer& buf) { - // float is passed as double to reduce the number of instantiations. - static_assert(!std::is_same::value, ""); - FMT_ASSERT(value >= 0, "value is negative"); - - const bool fixed = specs.format == float_format::fixed; - if (value <= 0) { // <= instead of == to silence a warning. - if (precision <= 0 || !fixed) { - buf.push_back('0'); - return 0; - } - buf.try_resize(to_unsigned(precision)); - fill_n(buf.data(), precision, '0'); - return -precision; - } - - if (specs.fallback) return snprintf_float(value, precision, specs, buf); - - if (!is_constant_evaluated() && precision < 0) { - // Use Dragonbox for the shortest format. - if (specs.binary32) { - auto dec = dragonbox::to_decimal(static_cast(value)); - write(buffer_appender(buf), dec.significand); - return dec.exponent; - } - auto dec = dragonbox::to_decimal(static_cast(value)); - write(buffer_appender(buf), dec.significand); - return dec.exponent; - } - - int exp = 0; - bool use_dragon = true; - if (is_fast_float()) { - // 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(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 = fp(); - bool is_predecessor_closer = - specs.binary32 ? f.assign(static_cast(value)) : f.assign(value); - // 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, is_predecessor_closer, 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 -int snprintf_float(T value, int precision, float_specs specs, - buffer& buf) { - // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. - FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); - static_assert(!std::is_same::value, ""); - - // Subtract 1 to account for the difference in precision since we use %e for - // both general and exponent format. - if (specs.format == float_format::general || - specs.format == float_format::exp) - precision = (precision >= 0 ? precision : 6) - 1; - - // Build the format string. - enum { max_format_size = 7 }; // The longest format is "%#.*Le". - char format[max_format_size]; - char* format_ptr = format; - *format_ptr++ = '%'; - if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#'; - if (precision >= 0) { - *format_ptr++ = '.'; - *format_ptr++ = '*'; - } - if (std::is_same()) *format_ptr++ = 'L'; - *format_ptr++ = specs.format != float_format::hex - ? (specs.format == float_format::fixed ? 'f' : 'e') - : (specs.upper ? 'A' : 'a'); - *format_ptr = '\0'; - - // Format using snprintf. - auto offset = buf.size(); - for (;;) { - auto begin = buf.data() + offset; - auto capacity = buf.capacity() - offset; -#ifdef FMT_FUZZ - if (precision > 100000) - throw std::runtime_error( - "fuzz mode - avoid large allocation inside snprintf"); #endif - // Suppress the warning about a nonliteral format string. - // Cannot use auto because of a bug in MinGW (#1532). - int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; - int result = precision >= 0 - ? snprintf_ptr(begin, capacity, format, precision, value) - : snprintf_ptr(begin, capacity, format, value); - if (result < 0) { - // 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_reserve(size + offset + 1); // Add 1 for the terminating '\0'. - continue; - } - auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; - if (specs.format == float_format::fixed) { - if (precision == 0) { - buf.try_resize(size); - return 0; - } - // Find and remove the decimal point. - auto end = begin + size, p = end; - do { - --p; - } while (is_digit(*p)); - int fraction_size = static_cast(end - p - 1); - std::memmove(p, p + 1, to_unsigned(fraction_size)); - buf.try_resize(size - 1); - return -fraction_size; - } - if (specs.format == float_format::hex) { - buf.try_resize(size + offset); - return 0; - } - // Find and parse the exponent. - auto end = begin + size, exp_pos = end; - do { - --exp_pos; - } while (*exp_pos != 'e'); - char sign = exp_pos[1]; - FMT_ASSERT(sign == '+' || sign == '-', ""); - int exp = 0; - auto p = exp_pos + 2; // Skip 'e' and sign. - do { - FMT_ASSERT(is_digit(*p), ""); - exp = exp * 10 + (*p++ - '0'); - } while (p != end); - if (sign == '-') exp = -exp; - int fraction_size = 0; - if (exp_pos != begin + 1) { - // Remove trailing zeros. - auto fraction_end = exp_pos - 1; - while (*fraction_end == '0') --fraction_end; - // Move the fractional part left to get rid of the decimal point. - fraction_size = static_cast(fraction_end - begin - 1); - std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size)); - } - buf.try_resize(to_unsigned(fraction_size) + offset + 1); - return exp - fraction_size; - } -} } // namespace detail template <> struct formatter { - FMT_CONSTEXPR format_parse_context::iterator parse( - format_parse_context& ctx) { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) + -> format_parse_context::iterator { return ctx.begin(); } - format_context::iterator format(const detail::bigint& n, - format_context& ctx) { + template + 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) { @@ -2560,7 +1465,7 @@ FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { } FMT_FUNC void format_system_error(detail::buffer& out, int error_code, - const char* message) FMT_NOEXCEPT { + 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()); @@ -2571,16 +1476,10 @@ FMT_FUNC void format_system_error(detail::buffer& out, int error_code, } FMT_FUNC void report_system_error(int error_code, - const char* message) FMT_NOEXCEPT { + const char* message) noexcept { report_error(format_system_error, error_code, message); } -// DEPRECATED! -// This function is defined here and not inline for ABI compatiblity. -FMT_FUNC void detail::error_handler::on_error(const char* message) { - throw_format_error(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. @@ -2638,6 +1537,197 @@ 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(x); + auto current = true; + for (size_t i = 0; i < normal_size; ++i) { + auto v = static_cast(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(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/externals/dynarmic/externals/fmt/include/fmt/format.h b/externals/dynarmic/externals/fmt/include/fmt/format.h index ee69651ca..0bd2fdb18 100755 --- a/externals/dynarmic/externals/fmt/include/fmt/format.h +++ b/externals/dynarmic/externals/fmt/include/fmt/format.h @@ -1,33 +1,33 @@ /* - Formatting library for C++ + Formatting library for C++ - Copyright (c) 2012 - present, Victor Zverovich + 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: + 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 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. + 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 --- + --- 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. + 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_ @@ -35,11 +35,11 @@ #include // std::signbit #include // uint32_t +#include // std::memcpy #include // std::numeric_limits #include // std::uninitialized_copy #include // std::runtime_error #include // std::system_error -#include // std::swap #ifdef __cpp_lib_bit_cast # include // std::bitcast @@ -71,7 +71,7 @@ # define FMT_NOINLINE #endif -#if FMT_MSC_VER +#if FMT_MSC_VERSION # define FMT_MSC_DEFAULT = default #else # define FMT_MSC_DEFAULT @@ -79,7 +79,7 @@ #ifndef FMT_THROW # if FMT_EXCEPTIONS -# if FMT_MSC_VER || FMT_NVCC +# if FMT_MSC_VERSION || defined(__NVCC__) FMT_BEGIN_NAMESPACE namespace detail { template inline void do_throw(const Exception& x) { @@ -118,17 +118,10 @@ FMT_END_NAMESPACE # endif #endif -// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers. -#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC -# define FMT_DEPRECATED_ALIAS -#else -# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED -#endif - #ifndef FMT_USE_USER_DEFINED_LITERALS // EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. # if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ - FMT_MSC_VER >= 1900) && \ + FMT_MSC_VERSION >= 1900) && \ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) # define FMT_USE_USER_DEFINED_LITERALS 1 # else @@ -146,7 +139,7 @@ FMT_END_NAMESPACE // __builtin_clz is broken in clang with Microsoft CodeGen: // https://github.com/fmtlib/fmt/issues/519. -#if !FMT_MSC_VER +#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 @@ -158,22 +151,25 @@ FMT_END_NAMESPACE // __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 +# 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 +# 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_VER +#if FMT_MSC_VERSION # include // _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_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(FMT_BUILTIN_CTZLL) +#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. @@ -243,15 +239,16 @@ inline auto ctzll(uint64_t x) -> int { FMT_END_NAMESPACE #endif -#ifdef FMT_HEADER_ONLY -# define FMT_HEADER_ONLY_CONSTEXPR20 FMT_CONSTEXPR20 -#else -# define FMT_HEADER_ONLY_CONSTEXPR20 -#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 class formatbuf : public Streambuf { private: using char_type = typename Streambuf::char_type; @@ -284,9 +281,8 @@ template class formatbuf : public Streambuf { }; // Implementation of std::bit_cast for pre-C++20. -template +template FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { - static_assert(sizeof(To) == sizeof(From), "size mismatch"); #ifdef __cpp_lib_bit_cast if (is_constant_evaluated()) return std::bit_cast(from); #endif @@ -310,29 +306,115 @@ inline auto is_big_endian() -> bool { #endif } -// A fallback implementation of uintptr_t for systems that lack it. -struct fallback_uintptr { - unsigned char value[sizeof(void*)]; +class uint128_fallback { + private: + uint64_t lo_, hi_; - fallback_uintptr() = default; - explicit fallback_uintptr(const void* p) { - *this = bit_cast(p); - if (const_check(is_big_endian())) { - for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j) - std::swap(value[i], value[j]); + 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 ::value)> + constexpr explicit operator T() const { + return static_cast(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_}; + return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)}; + } + FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback { + if (shift == 64) return {lo_, 0}; + 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) + unsigned long long carry; + lo_ = __builtin_addcll(lo_, n, 0, &carry); + hi_ += carry; +#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) + 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; + #ifdef UINTPTR_MAX using uintptr_t = ::uintptr_t; -inline auto to_uintptr(const void* p) -> uintptr_t { - return bit_cast(p); -} #else -using uintptr_t = fallback_uintptr; -inline auto to_uintptr(const void* p) -> fallback_uintptr { - return fallback_uintptr(p); -} +using uintptr_t = uint128_t; #endif // Returns the largest possible value for type T. Same as @@ -344,16 +426,31 @@ template constexpr auto num_bits() -> int { return std::numeric_limits::digits; } // std::numeric_limits::digits may return 0 for 128-bit ints. -template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return 128; } template <> constexpr auto num_bits() -> int { return 128; } -template <> constexpr auto num_bits() -> int { - return static_cast(sizeof(void*) * - std::numeric_limits::digits); + +// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t +// and 128-bit pointers to uint128_fallback. +template sizeof(From))> +inline auto bit_cast(const From& from) -> To { + constexpr auto size = static_cast(sizeof(From) / sizeof(unsigned)); + struct data_t { + unsigned value[static_cast(size)]; + } data = bit_cast(from); + auto result = To(); + if (const_check(is_big_endian())) { + for (int i = 0; i < size; ++i) + result = (result << num_bits()) | data.value[i]; + } else { + for (int i = size - 1; i >= 0; --i) + result = (result << num_bits()) | data.value[i]; + } + return result; } FMT_INLINE void assume(bool condition) { (void)condition; -#if FMT_HAS_BUILTIN(__builtin_assume) +#if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION __builtin_assume(condition); #endif } @@ -595,8 +692,8 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) { } inline auto compute_width(basic_string_view s) -> size_t { - return compute_width(basic_string_view( - reinterpret_cast(s.data()), s.size())); + return compute_width( + string_view(reinterpret_cast(s.data()), s.size())); } template @@ -606,9 +703,8 @@ inline auto code_point_index(basic_string_view s, size_t n) -> size_t { } // Calculates the index of the nth code point in a UTF-8 string. -inline auto code_point_index(basic_string_view s, size_t n) - -> size_t { - const char8_type* data = s.data(); +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; @@ -616,11 +712,38 @@ inline auto code_point_index(basic_string_view s, size_t n) return s.size(); } +inline auto code_point_index(basic_string_view s, size_t n) + -> size_t { + return code_point_index( + string_view(reinterpret_cast(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 using is_float128 = std::is_same; + +template +using is_floating_point = + bool_constant::value || is_float128::value>; + template ::value> struct is_fast_float : bool_constant::is_iec559 && sizeof(T) <= sizeof(double)> {}; template struct is_fast_float : std::false_type {}; +template +using is_double_double = bool_constant::digits == 106>; + #ifndef FMT_USE_FULL_CACHE_DRAGONBOX # define FMT_USE_FULL_CACHE_DRAGONBOX 0 #endif @@ -698,9 +821,7 @@ class basic_memory_buffer final : public detail::buffer { const Allocator& alloc = Allocator()) : alloc_(alloc) { this->set(store_, SIZE); - if (detail::is_constant_evaluated()) { - detail::fill_n(store_, SIZE, T{}); - } + if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T()); } FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); } @@ -712,18 +833,14 @@ class basic_memory_buffer final : public detail::buffer { size_t size = other.size(), capacity = other.capacity(); if (data == other.store_) { this->set(store_, capacity); - if (detail::is_constant_evaluated()) { - detail::copy_str(other.store_, other.store_ + size, - detail::make_checked(store_, capacity)); - } else { - std::uninitialized_copy(other.store_, other.store_ + size, - detail::make_checked(store_, capacity)); - } + detail::copy_str(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); } @@ -735,8 +852,7 @@ class basic_memory_buffer final : public detail::buffer { of the other object to it. \endrst */ - FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) - FMT_NOEXCEPT { + FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept { move(other); } @@ -745,8 +861,7 @@ class basic_memory_buffer final : public detail::buffer { Moves the content of the other ``basic_memory_buffer`` object to this one. \endrst */ - auto operator=(basic_memory_buffer&& other) FMT_NOEXCEPT - -> basic_memory_buffer& { + auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& { FMT_ASSERT(this != &other, ""); deallocate(); move(other); @@ -776,9 +891,7 @@ class basic_memory_buffer final : public detail::buffer { template FMT_CONSTEXPR20 void basic_memory_buffer::grow( size_t size) { -#ifdef FMT_FUZZ - if (size > 5000) throw std::runtime_error("fuzz mode - won't grow that much"); -#endif + detail::abort_fuzzing_if(size > 5000); const size_t max_size = std::allocator_traits::max_size(alloc_); size_t old_capacity = this->capacity(); size_t new_capacity = old_capacity + old_capacity / 2; @@ -820,39 +933,17 @@ class FMT_API format_error : public std::runtime_error { format_error& operator=(const format_error&) = default; format_error(format_error&&) = default; format_error& operator=(format_error&&) = default; - ~format_error() FMT_NOEXCEPT override FMT_MSC_DEFAULT; + ~format_error() noexcept override FMT_MSC_DEFAULT; }; -/** - \rst - Constructs a `~fmt::format_arg_store` object that contains references - to arguments and can be implicitly converted to `~fmt::format_args`. - If ``fmt`` is a compile-time string then `make_args_checked` checks - its validity at compile time. - \endrst - */ -template > -FMT_INLINE auto make_args_checked(const S& fmt, - const remove_reference_t&... args) - -> format_arg_store, remove_reference_t...> { - static_assert( - detail::count<( - std::is_base_of>::value && - std::is_reference::value)...>() == 0, - "passing views as lvalues is disallowed"); - detail::check_format_string(fmt); - return {args...}; -} - -// compile-time support namespace detail_exported { -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS template struct fixed_string { constexpr fixed_string(const Char (&str)[N]) { detail::copy_str(static_cast(str), str + N, data); } - Char data[N]{}; + Char data[N] = {}; }; #endif @@ -874,30 +965,31 @@ constexpr auto compile_string_to_view(detail::std_string_view s) FMT_BEGIN_DETAIL_NAMESPACE template struct is_integral : std::is_integral {}; -template <> struct is_integral : std::true_type {}; +template <> struct is_integral : std::true_type {}; template <> struct is_integral : std::true_type {}; template using is_signed = std::integral_constant::is_signed || - std::is_same::value>; + std::is_same::value>; // Returns true if value is negative, false otherwise. // Same as `value < 0` but doesn't produce warnings if T is an unsigned type. template ::value)> -FMT_CONSTEXPR auto is_negative(T value) -> bool { +constexpr auto is_negative(T value) -> bool { return value < 0; } template ::value)> -FMT_CONSTEXPR auto is_negative(T) -> bool { +constexpr auto is_negative(T) -> bool { return false; } -template ::value)> -FMT_CONSTEXPR auto is_supported_floating_point(T) -> uint16_t { - return (std::is_same::value && FMT_USE_FLOAT) || - (std::is_same::value && FMT_USE_DOUBLE) || - (std::is_same::value && FMT_USE_LONG_DOUBLE); +template +FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool { + if (std::is_same()) return FMT_USE_FLOAT; + if (std::is_same()) return FMT_USE_DOUBLE; + if (std::is_same()) return FMT_USE_LONG_DOUBLE; + return true; } // Smallest of uint32_t, uint64_t, uint128_t that is large enough to @@ -948,7 +1040,7 @@ template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { } } #if FMT_USE_INT128 -FMT_CONSTEXPR inline auto count_digits(uint128_t n) -> int { +FMT_CONSTEXPR inline auto count_digits(uint128_opt n) -> int { return count_digits_fallback(n); } #endif @@ -989,7 +1081,7 @@ FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { template FMT_CONSTEXPR auto count_digits(UInt n) -> int { #ifdef FMT_BUILTIN_CLZ - if (num_bits() == 32) + if (!is_constant_evaluated() && num_bits() == 32) return (FMT_BUILTIN_CLZ(static_cast(n) | 1) ^ 31) / BITS + 1; #endif // Lambda avoids unreachable code warnings from NVHPC. @@ -1002,8 +1094,6 @@ FMT_CONSTEXPR auto count_digits(UInt n) -> int { }(n); } -template <> auto count_digits<4>(detail::fallback_uintptr n) -> int; - #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. @@ -1039,15 +1129,11 @@ FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { return count_digits_fallback(n); } -template constexpr auto digits10() FMT_NOEXCEPT -> int { +template constexpr auto digits10() noexcept -> int { return std::numeric_limits::digits10; } -template <> constexpr auto digits10() FMT_NOEXCEPT -> int { - return 38; -} -template <> constexpr auto digits10() FMT_NOEXCEPT -> int { - return 38; -} +template <> constexpr auto digits10() noexcept -> int { return 38; } +template <> constexpr auto digits10() noexcept -> int { return 38; } template struct thousands_sep_result { std::string grouping; @@ -1142,35 +1228,13 @@ FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, Char* end = buffer; do { const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; - unsigned digit = (value & ((1 << BASE_BITS) - 1)); + unsigned digit = static_cast(value & ((1 << BASE_BITS) - 1)); *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) : digits[digit]); } while ((value >>= BASE_BITS) != 0); return end; } -template -auto format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, - bool = false) -> Char* { - auto char_digits = std::numeric_limits::digits / 4; - int start = (num_digits + char_digits - 1) / char_digits - 1; - if (int start_digits = num_digits % char_digits) { - unsigned value = n.value[start--]; - buffer = format_uint(buffer, value, start_digits); - } - for (; start >= 0; --start) { - unsigned value = n.value[start]; - buffer += char_digits; - auto p = buffer; - for (int i = 0; i < char_digits; ++i) { - unsigned digit = (value & ((1 << BASE_BITS) - 1)); - *--p = static_cast("0123456789abcdef"[digit]); - value >>= BASE_BITS; - } - } - return buffer; -} - template inline auto format_uint(It out, UInt value, int num_digits, bool upper = false) -> It { @@ -1200,58 +1264,49 @@ class utf8_to_utf16 { namespace dragonbox { // Type-specific information that Dragonbox uses. -template struct float_info; +template struct float_info; template <> struct float_info { using carrier_uint = uint32_t; - static const int significand_bits = 23; static const int exponent_bits = 8; - static const int min_exponent = -126; - static const int max_exponent = 127; - static const int exponent_bias = -127; - static const int decimal_digits = 9; 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 cache_bits = 64; static const int divisibility_check_by_5_threshold = 39; static const int case_fc_pm_half_lower_threshold = -1; - static const int case_fc_pm_half_upper_threshold = 6; - static const int case_fc_lower_threshold = -2; - static const int case_fc_upper_threshold = 6; - static const int case_shorter_interval_left_endpoint_lower_threshold = 2; - static const int case_shorter_interval_left_endpoint_upper_threshold = 3; static const int shorter_interval_tie_lower_threshold = -35; static const int shorter_interval_tie_upper_threshold = -35; - static const int max_trailing_zeros = 7; }; template <> struct float_info { using carrier_uint = uint64_t; - static const int significand_bits = 52; static const int exponent_bits = 11; - static const int min_exponent = -1022; - static const int max_exponent = 1023; - static const int exponent_bias = -1023; - static const int decimal_digits = 17; 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 cache_bits = 128; static const int divisibility_check_by_5_threshold = 86; static const int case_fc_pm_half_lower_threshold = -2; - static const int case_fc_pm_half_upper_threshold = 9; - static const int case_fc_lower_threshold = -4; - static const int case_fc_upper_threshold = 9; - static const int case_shorter_interval_left_endpoint_lower_threshold = 2; - static const int case_shorter_interval_left_endpoint_upper_threshold = 3; static const int shorter_interval_tie_lower_threshold = -77; static const int shorter_interval_tie_upper_threshold = -77; - static const int max_trailing_zeros = 16; +}; + +// An 80- or 128-bit floating point number. +template +struct float_info::digits == 64 || + std::numeric_limits::digits == 113 || + is_float128::value>> { + using carrier_uint = detail::uint128_t; + static const int exponent_bits = 15; +}; + +// A double-double floating point number. +template +struct float_info::value>> { + using carrier_uint = detail::uint128_t; }; template struct decimal_fp { @@ -1260,16 +1315,35 @@ template struct decimal_fp { int exponent; }; -template -FMT_API auto to_decimal(T x) FMT_NOEXCEPT -> decimal_fp; +template FMT_API auto to_decimal(T x) noexcept -> decimal_fp; } // namespace dragonbox -template +// Returns true iff Float has the implicit bit which is not stored. +template constexpr bool has_implicit_bit() { + // An 80-bit FP number has a 64-bit significand an no implicit bit. + return std::numeric_limits::digits != 64; +} + +// Returns the number of significand bits stored in Float. The implicit bit is +// not counted since it is not stored. +template constexpr int num_significand_bits() { + // std::numeric_limits may not support __float128. + return is_float128() ? 112 + : (std::numeric_limits::digits - + (has_implicit_bit() ? 1 : 0)); +} + +template constexpr auto exponent_mask() -> - typename dragonbox::float_info::carrier_uint { - using uint = typename dragonbox::float_info::carrier_uint; - return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) - << dragonbox::float_info::significand_bits; + typename dragonbox::float_info::carrier_uint { + using uint = typename dragonbox::float_info::carrier_uint; + return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) + << num_significand_bits(); +} +template constexpr auto exponent_bias() -> int { + // std::numeric_limits may not support __float128. + return is_float128() ? 16383 + : std::numeric_limits::max_exponent - 1; } // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. @@ -1294,21 +1368,248 @@ FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It { return it; } -template -FMT_HEADER_ONLY_CONSTEXPR20 auto format_float(T value, int precision, - float_specs specs, - buffer& buf) -> int; +// A floating-point number f * pow(2, e) where F is an unsigned type. +template struct basic_fp { + F f; + int e; -// Formats a floating-point number with snprintf. -template -auto snprintf_float(T value, int precision, float_specs specs, - buffer& buf) -> int; + static constexpr const int num_significand_bits = + static_cast(sizeof(F) * num_bits()); -template constexpr auto promote_float(T value) -> T { + 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 FMT_CONSTEXPR basic_fp(Float n) { assign(n); } + + // Assigns n to this and return true iff predecessor is closer than successor. + template ::value)> + FMT_CONSTEXPR auto assign(Float n) -> bool { + static_assert(std::numeric_limits::digits <= 113, "unsupported FP"); + // Assume Float is in the format [sign][exponent][significand]. + using carrier_uint = typename dragonbox::float_info::carrier_uint; + const auto num_float_significand_bits = + detail::num_significand_bits(); + const auto implicit_bit = carrier_uint(1) << num_float_significand_bits; + const auto significand_mask = implicit_bit - 1; + auto u = bit_cast(n); + f = static_cast(u & significand_mask); + auto biased_e = static_cast((u & exponent_mask()) >> + 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()) + f += static_cast(implicit_bit); + e = biased_e - exponent_bias() - num_float_significand_bits; + if (!has_implicit_bit()) ++e; + return is_predecessor_closer; + } + + template ::value)> + FMT_CONSTEXPR auto assign(Float n) -> bool { + static_assert(std::numeric_limits::is_iec559, "unsupported FP"); + return assign(static_cast(n)); + } +}; + +using fp = basic_fp; + +// Normalizes the value converted from double and multiplied by (1 << SHIFT). +template +FMT_CONSTEXPR basic_fp normalize(basic_fp value) { + // Handle subnormals. + const auto implicit_bit = F(1) << num_significand_bits(); + 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::num_significand_bits - + num_significand_bits() - SHIFT - 1; + value.f <<= offset; + value.e -= offset; return value; } -constexpr auto promote_float(float value) -> double { - return static_cast(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(product >> 64); + return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; +#else + // Multiply 32-bit parts of significands. + uint64_t mask = (1ULL << 32) - 1; + uint64_t a = lhs >> 32, b = lhs & mask; + uint64_t c = rhs >> 32, d = rhs & mask; + uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; + // Compute mid 64-bit of result and round. + uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); + return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); +#endif +} + +FMT_CONSTEXPR inline fp operator*(fp x, fp y) { + return {multiply(x.f, y.f), x.e + y.e + 64}; +} + +template 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 constexpr uint64_t basic_data::pow10_significands[]; +template constexpr int16_t basic_data::pow10_exponents[]; +template constexpr uint64_t basic_data::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( + ((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; + 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 +auto snprintf_float(T value, int precision, float_specs specs, + buffer& 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::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()) *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 +using convert_float_result = + conditional_t::value || sizeof(T) == sizeof(double), + double, T>; + +template +constexpr auto convert_float(T value) -> convert_float_result { + return static_cast>(value); } template @@ -1377,11 +1678,172 @@ auto write_ptr(OutputIt out, UIntPtr value, : 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 struct find_escape_result { + const Char* begin; + const Char* end; + uint32_t cp; +}; + +template +using make_unsigned_char = + typename conditional_t::value, + std::make_unsigned, + type_identity>::type; + +template +auto find_escape(const Char* begin, const Char* end) + -> find_escape_result { + for (; begin != end; ++begin) { + uint32_t cp = static_cast>(*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 { + if (!is_utf8()) return find_escape(begin, end); + auto result = find_escape_result{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::remove_cvref_t; \ + FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ + operator fmt::basic_string_view() const { \ + return fmt::detail_exported::compile_string_to_view(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 +auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { + *out++ = static_cast('\\'); + *out++ = static_cast(prefix); + Char buf[width]; + fill_n(buf, width, static_cast('0')); + format_uint<4>(buf, cp, width); + return copy_str(buf, buf + width, out); +} + +template +auto write_escaped_cp(OutputIt out, const find_escape_result& escape) + -> OutputIt { + auto c = static_cast(escape.cp); + switch (escape.cp) { + case '\n': + *out++ = static_cast('\\'); + c = static_cast('n'); + break; + case '\r': + *out++ = static_cast('\\'); + c = static_cast('r'); + break; + case '\t': + *out++ = static_cast('\\'); + c = static_cast('t'); + break; + case '"': + FMT_FALLTHROUGH; + case '\'': + FMT_FALLTHROUGH; + case '\\': + *out++ = static_cast('\\'); + 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( + escape.begin, to_unsigned(escape.end - escape.begin))) { + out = write_codepoint<2, Char>(out, 'x', + static_cast(escape_char) & 0xFF); + } + return out; + } + *out++ = c; + return out; +} + +template +auto write_escaped_string(OutputIt out, basic_string_view str) + -> OutputIt { + *out++ = static_cast('"'); + auto begin = str.begin(), end = str.end(); + do { + auto escape = find_escape(begin, end); + out = copy_str(begin, escape.begin, out); + begin = escape.end; + if (!begin) break; + out = write_escaped_cp(out, escape); + } while (begin != end); + *out++ = static_cast('"'); + return out; +} + +template +auto write_escaped_char(OutputIt out, Char v) -> OutputIt { + *out++ = static_cast('\''); + if ((needs_escape(static_cast(v)) && v != static_cast('"')) || + v == static_cast('\'')) { + out = write_escaped_cp( + out, find_escape_result{&v, &v + 1, static_cast(v)}); + } else { + *out++ = v; + } + *out++ = static_cast('\''); + return out; +} + template FMT_CONSTEXPR auto write_char(OutputIt out, Char value, const basic_format_specs& specs) -> OutputIt { + bool is_debug = specs.type == presentation_type::debug; return write_padded(out, specs, 1, [=](reserve_iterator it) { + if (is_debug) return write_escaped_char(it, value); *it++ = value; return it; }); @@ -1647,6 +2109,45 @@ FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, 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 void operator=(const T&) {} + }; + + counting_iterator() : count_(0) {} + + size_t count() const { return count_; } + + counting_iterator& operator++() { + ++count_; + return *this; + } + counting_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } + + friend counting_iterator operator+(counting_iterator it, difference_type n) { + it.count_ += static_cast(n); + return it; + } + + value_type operator*() const { return {}; } +}; + template FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, const basic_format_specs& specs) -> OutputIt { @@ -1654,10 +2155,17 @@ FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, auto size = s.size(); if (specs.precision >= 0 && to_unsigned(specs.precision) < size) size = code_point_index(s, to_unsigned(specs.precision)); - auto width = - specs.width != 0 ? compute_width(basic_string_view(data, size)) : 0; + 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(data, size)); + } return write_padded(out, specs, size, width, [=](reserve_iterator it) { + if (is_debug) return write_escaped_string(it, s); return copy_str(data, data + size, it); }); } @@ -1675,15 +2183,37 @@ FMT_CONSTEXPR auto write(OutputIt out, const Char* s, -> OutputIt { return check_cstring_type_spec(specs.type) ? write(out, basic_string_view(s), specs, {}) - : write_ptr(out, to_uintptr(s), &specs); + : write_ptr(out, bit_cast(s), &specs); +} + +template ::value && + !std::is_same::value && + !std::is_same::value)> +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { + auto abs_value = static_cast>(value); + bool negative = is_negative(value); + // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. + if (negative) abs_value = ~abs_value + 1; + int num_digits = count_digits(abs_value); + auto size = (negative ? 1 : 0) + static_cast(num_digits); + auto it = reserve(out, size); + if (auto ptr = to_pointer(it, size)) { + if (negative) *ptr++ = static_cast('-'); + format_decimal(ptr, abs_value, num_digits); + return out; + } + if (negative) *it++ = static_cast('-'); + it = format_decimal(it, abs_value, num_digits).end; + return base_iterator(out, it); } template -FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isinf, +FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, basic_format_specs specs, const float_specs& fspecs) -> OutputIt { auto str = - isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan"); + 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); @@ -1704,12 +2234,12 @@ struct big_decimal_fp { int exponent; }; -constexpr auto get_significand_size(const big_decimal_fp& fp) -> int { - return fp.significand_size; +constexpr auto get_significand_size(const big_decimal_fp& f) -> int { + return f.significand_size; } template -inline auto get_significand_size(const dragonbox::decimal_fp& fp) -> int { - return count_digits(fp.significand); +inline auto get_significand_size(const dragonbox::decimal_fp& f) -> int { + return count_digits(f.significand); } template @@ -1747,7 +2277,7 @@ inline auto write_significand(Char* out, UInt significand, int significand_size, int floating_size = significand_size - integral_size; for (int i = floating_size / 2; i > 0; --i) { out -= 2; - copy2(out, digits2(significand % 100)); + copy2(out, digits2(static_cast(significand % 100))); significand /= 100; } if (floating_size % 2 != 0) { @@ -1803,13 +2333,13 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, template > -FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp, +FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, const basic_format_specs& specs, float_specs fspecs, locale_ref loc) -> OutputIt { - auto significand = fp.significand; - int significand_size = get_significand_size(fp); - constexpr Char zero = static_cast('0'); + auto significand = f.significand; + int significand_size = get_significand_size(f); + const Char zero = static_cast('0'); auto sign = fspecs.sign; size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); using iterator = reserve_iterator; @@ -1817,7 +2347,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp, Char decimal_point = fspecs.locale ? detail::decimal_point(loc) : static_cast('.'); - int output_exp = fp.exponent + significand_size - 1; + 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; @@ -1855,25 +2385,23 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp, : base_iterator(out, write(reserve(out, size))); } - int exp = fp.exponent + significand_size; - if (fp.exponent >= 0) { + int exp = f.exponent + significand_size; + if (f.exponent >= 0) { // 1234e5 -> 123400000[.0+] - size += to_unsigned(fp.exponent); + size += to_unsigned(f.exponent); int num_zeros = fspecs.precision - exp; -#ifdef FMT_FUZZ - if (num_zeros > 5000) - throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); -#endif + 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) + 1; + if (num_zeros > 0) size += to_unsigned(num_zeros); } auto grouping = Grouping(loc, fspecs.locale); - size += to_unsigned(grouping.count_separators(significand_size)); + size += to_unsigned(grouping.count_separators(exp)); return write_padded(out, specs, size, [&](iterator it) { if (sign) *it++ = detail::sign(sign); it = write_significand(it, significand, significand_size, - fp.exponent, grouping); + f.exponent, grouping); if (!fspecs.showpoint) return it; *it++ = decimal_point; return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; @@ -1924,63 +2452,713 @@ template class fallback_digit_grouping { }; template -FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& fp, +FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, const basic_format_specs& specs, float_specs fspecs, locale_ref loc) -> OutputIt { if (is_constant_evaluated()) { return do_write_float>(out, fp, specs, fspecs, + fallback_digit_grouping>(out, f, specs, fspecs, loc); } else { - return do_write_float(out, fp, specs, fspecs, loc); + return do_write_float(out, f, specs, fspecs, loc); } } -template ::value)> -FMT_CONSTEXPR20 bool isinf(T value) { - if (is_constant_evaluated()) { -#if defined(__cpp_if_constexpr) - if constexpr (std::numeric_limits::is_iec559) { - auto bits = detail::bit_cast(static_cast(value)); - constexpr auto significand_bits = - dragonbox::float_info::significand_bits; - return (bits & exponent_mask()) && - !(bits & ((uint64_t(1) << significand_bits) - 1)); - } -#endif - } - return std::isinf(value); +template constexpr bool isnan(T value) { + return !(value >= value); // std::isnan doesn't support __float128. } -template ::value)> +template +struct has_isfinite : std::false_type {}; + +template +struct has_isfinite> + : std::true_type {}; + +template ::value&& + has_isfinite::value)> FMT_CONSTEXPR20 bool isfinite(T value) { - if (is_constant_evaluated()) { -#if defined(__cpp_if_constexpr) - if constexpr (std::numeric_limits::is_iec559) { - auto bits = detail::bit_cast(static_cast(value)); - return (bits & exponent_mask()) != exponent_mask(); - } -#endif - } + constexpr T inf = T(std::numeric_limits::infinity()); + if (is_constant_evaluated()) + return !detail::isnan(value) && value != inf && value != -inf; return std::isfinite(value); } +template ::value)> +FMT_CONSTEXPR bool isfinite(T value) { + T inf = T(std::numeric_limits::infinity()); + // std::isfinite doesn't support __float128. + return !detail::isnan(value) && value != inf && value != -inf; +} -template ::value)> +template ::value)> FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { if (is_constant_evaluated()) { #ifdef __cpp_if_constexpr if constexpr (std::numeric_limits::is_iec559) { auto bits = detail::bit_cast(static_cast(value)); - return (bits & (uint64_t(1) << (num_bits() - 1))) != 0; + return (bits >> (num_bits() - 1)) != 0; } #endif } - return std::signbit(value); + return std::signbit(static_cast(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() - 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(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(integral) << -one.e) + fractional; + auto result = handler.on_digit(static_cast('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('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 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(); + + friend struct formatter; + + FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) { + auto result = static_cast((*this)[index]) - other - borrow; + (*this)[index] = static_cast(result); + borrow = static_cast(result >> (bigit_bits * 2 - 1)); + } + + FMT_CONSTEXPR20 void remove_leading_zeros() { + int num_bigits = static_cast(bigits_.size()) - 1; + while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; + bigits_.resize(to_unsigned(num_bigits + 1)); + } + + // Computes *this -= other assuming aligned bigints and *this >= other. + 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(result); + carry = static_cast(result >> bigit_bits); + } + if (carry != 0) bigits_.push_back(carry); + } + + template ::value || + std::is_same::value)> + FMT_CONSTEXPR20 void multiply(UInt value) { + using half_uint = + conditional_t::value, uint64_t, uint32_t>; + const int shift = num_bits() - bigit_bits; + const UInt lower = static_cast(value); + const UInt upper = value >> num_bits(); + UInt carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + UInt result = lower * bigits_[i] + static_cast(carry); + carry = (upper * bigits_[i] << shift) + (result >> bigit_bits) + + (carry >> bigit_bits); + bigits_[i] = static_cast(result); + } + while (carry != 0) { + bigits_.push_back(static_cast(carry)); + carry >>= bigit_bits; + } + } + + template ::value || + std::is_same::value)> + FMT_CONSTEXPR20 void assign(UInt n) { + size_t num_bigits = 0; + do { + bigits_[num_bigits++] = static_cast(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 FMT_CONSTEXPR20 void operator=(Int n) { + FMT_ASSERT(n > 0, ""); + assign(uint64_or_128_t(n)); + } + + FMT_CONSTEXPR20 int num_bigits() const { + return static_cast(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 FMT_CONSTEXPR20 bigint& operator*=(Int value) { + FMT_ASSERT(value > 0, ""); + multiply(uint32_or_64_or_128_t(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(lhs.bigits_.size()) - 1; + int j = static_cast(rhs.bigits_.size()) - 1; + int end = i - j; + if (end < 0) end = 0; + for (; i >= end; --i, --j) { + bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; + if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; + } + if (i != j) return i > j ? 1 : -1; + return 0; + } + + // Returns compare(lhs1 + lhs2, rhs). + friend 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(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(bigits_.size()); + int num_result_bigits = 2 * num_bigits; + basic_memory_buffer 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(n[i]) * n[j]; + } + (*this)[bigit_index] = static_cast(sum); + sum >>= num_bits(); // Compute the carry. + } + // Do the same for the top half. + for (int bigit_index = num_bigits; bigit_index < num_result_bigits; + ++bigit_index) { + for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) + sum += static_cast(n[i++]) * n[j--]; + (*this)[bigit_index] = static_cast(sum); + sum >>= num_bits(); + } + 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(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 value, + unsigned flags, int num_digits, + buffer& buf, int& exp10) { + bigint numerator; // 2 * R in (FPP)^2. + bigint denominator; // 2 * S in (FPP)^2. + // lower and upper are differences between value and corresponding boundaries. + bigint lower; // (M^- in (FPP)^2). + bigint upper_store; // upper's value if different from lower. + bigint* upper = nullptr; // (M^+ in (FPP)^2). + // 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; + } + } + bool even = (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('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('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('0' + digit); +} + +template +FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, + buffer& buf) -> int { + // float is passed as double to reduce the number of instantiations. + static_assert(!std::is_same::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()) { + const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10) + using info = dragonbox::float_info; + const auto f = basic_fp(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( + 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(value)); + write(buffer_appender(buf), dec.significand); + return dec.exponent; + } + auto dec = dragonbox::to_decimal(static_cast(value)); + write(buffer_appender(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(); + bool is_predecessor_closer = specs.binary32 + ? f.assign(static_cast(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 ::value)> + FMT_ENABLE_IF(is_floating_point::value)> FMT_CONSTEXPR20 auto write(OutputIt out, T value, basic_format_specs specs, locale_ref loc = {}) -> OutputIt { @@ -1995,7 +3173,7 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value, } if (!detail::isfinite(value)) - return write_nonfinite(out, detail::isinf(value), specs, fspecs); + return write_nonfinite(out, detail::isnan(value), specs, fspecs); if (specs.align == align::numeric && fspecs.sign) { auto it = reserve(out, 1); @@ -2008,7 +3186,7 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value, memory_buffer buffer; if (fspecs.format == float_format::hex) { if (fspecs.sign) buffer.push_back(detail::sign(fspecs.sign)); - snprintf_float(promote_float(value), specs.precision, fspecs, buffer); + snprintf_float(convert_float(value), specs.precision, fspecs, buffer); return write_bytes(out, {buffer.data(), buffer.size()}, specs); } @@ -2020,28 +3198,23 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value, 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())) fspecs.binary32 = true; - if (!is_fast_float()) fspecs.fallback = true; - int exp = format_float(promote_float(value), precision, fspecs, buffer); + int exp = format_float(convert_float(value), precision, fspecs, buffer); fspecs.precision = precision; - auto fp = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; - return write_float(out, fp, specs, fspecs, loc); + auto f = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; + return write_float(out, f, specs, fspecs, loc); } template ::value)> FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { - if (is_constant_evaluated()) { + if (is_constant_evaluated()) return write(out, value, basic_format_specs()); - } - if (const_check(!is_supported_floating_point(value))) return out; - using floaty = conditional_t::value, double, T>; - using uint = typename dragonbox::float_info::carrier_uint; - auto bits = bit_cast(value); - auto fspecs = float_specs(); if (detail::signbit(value)) { fspecs.sign = sign::minus; @@ -2049,16 +3222,18 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { } constexpr auto specs = basic_format_specs(); + using floaty = conditional_t::value, double, T>; + using uint = typename dragonbox::float_info::carrier_uint; uint mask = exponent_mask(); - if ((bits & mask) == mask) - return write_nonfinite(out, std::isinf(value), specs, fspecs); + if ((bit_cast(value) & mask) == mask) + return write_nonfinite(out, std::isnan(value), specs, fspecs); auto dec = dragonbox::to_decimal(static_cast(value)); return write_float(out, dec, specs, fspecs, {}); } template ::value && + FMT_ENABLE_IF(is_floating_point::value && !is_fast_float::value)> inline auto write(OutputIt out, T value) -> OutputIt { return write(out, value, basic_format_specs()); @@ -2085,28 +3260,6 @@ constexpr auto write(OutputIt out, const T& value) -> OutputIt { return write(out, to_string_view(value)); } -template ::value && - !std::is_same::value && - !std::is_same::value)> -FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { - auto abs_value = static_cast>(value); - bool negative = is_negative(value); - // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. - if (negative) abs_value = ~abs_value + 1; - int num_digits = count_digits(abs_value); - auto size = (negative ? 1 : 0) + static_cast(num_digits); - auto it = reserve(out, size); - if (auto ptr = to_pointer(it, size)) { - if (negative) *ptr++ = static_cast('-'); - format_decimal(ptr, abs_value, num_digits); - return out; - } - if (negative) *it++ = static_cast('-'); - it = format_decimal(it, abs_value, num_digits).end; - return base_iterator(out, it); -} - // FMT_ENABLE_IF() condition separated to workaround an MSVC bug. template < typename Char, typename OutputIt, typename T, @@ -2116,8 +3269,7 @@ template < type::custom_type, FMT_ENABLE_IF(check)> FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { - return write( - out, static_cast::type>(value)); + return write(out, static_cast>(value)); } template & specs = {}, locale_ref = {}) -> OutputIt { check_pointer_type_spec(specs.type, error_handler()); - return write_ptr(out, to_uintptr(value), &specs); + return write_ptr(out, bit_cast(value), &specs); } // A write overload that handles implicit conversions. @@ -2163,7 +3315,7 @@ template > FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t< std::is_class::value && !is_string::value && - !std::is_same::value && + !is_floating_point::value && !std::is_same::value && !std::is_same().map(value))>::value, OutputIt> { @@ -2356,43 +3508,17 @@ FMT_CONSTEXPR void handle_dynamic_spec(int& value, } } -#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::remove_cvref_t; \ - FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ - operator fmt::basic_string_view() const { \ - return fmt::detail_exported::compile_string_to_view(s); \ - } \ - }; \ - return FMT_COMPILE_STRING(); \ - }() - -/** - \rst - Constructs a compile-time format string from a string literal *s*. - - **Example**:: - - // A compile-time error because 'd' is an invalid specifier for strings. - std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); - \endrst - */ -#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string, ) - #if FMT_USE_USER_DEFINED_LITERALS template struct udl_formatter { basic_string_view str; template auto operator()(T&&... args) const -> std::basic_string { - return vformat(str, fmt::make_args_checked(str, args...)); + return vformat(str, fmt::make_format_args>(args...)); } }; -# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +# if FMT_USE_NONTYPE_TEMPLATE_ARGS template Str> struct statically_named_arg : view { @@ -2441,10 +3567,10 @@ auto vformat(const Locale& loc, basic_string_view format_str, using format_func = void (*)(detail::buffer&, int, const char*); FMT_API void format_error_code(buffer& out, int error_code, - string_view message) FMT_NOEXCEPT; + string_view message) noexcept; FMT_API void report_error(format_func func, int error_code, - const char* message) FMT_NOEXCEPT; + const char* message) noexcept; FMT_END_DETAIL_NAMESPACE FMT_API auto vsystem_error(int error_code, string_view format_str, @@ -2490,12 +3616,11 @@ auto system_error(int error_code, format_string fmt, T&&... args) \endrst */ FMT_API void format_system_error(detail::buffer& out, int error_code, - const char* message) FMT_NOEXCEPT; + 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) FMT_NOEXCEPT; +FMT_API void report_system_error(int error_code, const char* message) noexcept; /** Fast integer formatter. */ class format_int { @@ -2577,28 +3702,6 @@ formatter(ctx.out(), val, specs_, ctx.locale()); } -#define FMT_FORMAT_AS(Type, Base) \ - template \ - struct formatter : formatter { \ - template \ - auto format(Type const& val, FormatContext& ctx) const \ - -> decltype(ctx.out()) { \ - return formatter::format(static_cast(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, basic_string_view); -FMT_FORMAT_AS(std::nullptr_t, const void*); -FMT_FORMAT_AS(detail::byte, unsigned char); -FMT_FORMAT_AS(detail::std_string_view, basic_string_view); - template struct formatter : formatter { template @@ -2688,6 +3791,28 @@ template auto ptr(const std::shared_ptr& 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 +constexpr auto underlying(Enum e) noexcept -> underlying_t { + return static_cast>(e); +} + +namespace enums { +template ::value)> +constexpr auto format_as(Enum e) noexcept -> underlying_t { + return static_cast>(e); +} +} // namespace enums + class bytes { private: string_view data_; @@ -2778,9 +3903,6 @@ struct join_view : detail::view { : begin(b), end(e), sep(s) {} }; -template -using arg_join FMT_DEPRECATED_ALIAS = join_view; - template struct formatter, Char> { private: @@ -2818,8 +3940,8 @@ struct formatter, Char> { } template - auto format(const join_view& value, FormatContext& ctx) - -> decltype(ctx.out()) { + auto format(const join_view& value, + FormatContext& ctx) const -> decltype(ctx.out()) { auto it = value.begin; auto out = ctx.out(); if (it != value.end) { @@ -2936,9 +4058,10 @@ void vformat_to( basic_format_parse_context parse_context; buffer_context context; - format_handler(buffer_appender out, basic_string_view str, - basic_format_args> args, locale_ref loc) - : parse_context(str), context(out, args, loc) {} + format_handler(buffer_appender p_out, basic_string_view str, + basic_format_args> 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(begin, to_unsigned(end - begin)); @@ -2995,20 +4118,6 @@ extern template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; extern template FMT_API auto decimal_point_impl(locale_ref) -> char; extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; -extern template auto format_float(double value, int precision, - float_specs specs, buffer& buf) - -> int; -extern template auto format_float(long double value, int precision, - float_specs specs, - buffer& buf) -> int; -void snprintf_float(float, int, float_specs, buffer&) = delete; -extern template auto snprintf_float(double value, int precision, - float_specs specs, - buffer& buf) -> int; -extern template auto snprintf_float(long double value, - int precision, - float_specs specs, - buffer& buf) -> int; #endif // FMT_HEADER_ONLY FMT_END_DETAIL_NAMESPACE @@ -3025,25 +4134,16 @@ inline namespace literals { fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); \endrst */ -# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -template -constexpr auto operator""_a() - -> detail::udl_arg, - sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> { - return {}; +# if FMT_USE_NONTYPE_TEMPLATE_ARGS +template constexpr auto operator""_a() { + using char_t = remove_cvref_t; + return detail::udl_arg(); } # else constexpr auto operator"" _a(const char* s, size_t) -> detail::udl_arg { return {s}; } # endif - -// DEPRECATED! -// User-defined literal equivalent of fmt::format. -FMT_DEPRECATED constexpr auto operator"" _format(const char* s, size_t n) - -> detail::udl_formatter { - return {{s, n}}; -} } // namespace literals #endif // FMT_USE_USER_DEFINED_LITERALS @@ -3060,14 +4160,6 @@ inline auto format(const Locale& loc, format_string fmt, T&&... args) return vformat(loc, string_view(fmt), fmt::make_format_args(args...)); } -template -FMT_DEPRECATED auto format_to(basic_memory_buffer& buf, - format_string fmt, T&&... args) - -> appender { - detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...)); - return appender(buf); -} - template ::value&& detail::is_locale::value)> @@ -3090,10 +4182,6 @@ FMT_INLINE auto format_to(OutputIt out, const Locale& loc, FMT_MODULE_EXPORT_END FMT_END_NAMESPACE -#ifdef FMT_DEPRECATED_INCLUDE_XCHAR -# include "xchar.h" -#endif - #ifdef FMT_HEADER_ONLY # define FMT_FUNC inline # include "format-inl.h" diff --git a/externals/dynarmic/externals/fmt/include/fmt/os.h b/externals/dynarmic/externals/fmt/include/fmt/os.h index b64f8bbfa..d82be1125 100755 --- a/externals/dynarmic/externals/fmt/include/fmt/os.h +++ b/externals/dynarmic/externals/fmt/include/fmt/os.h @@ -9,10 +9,8 @@ #define FMT_OS_H_ #include -#include // locale_t #include #include -#include // strtod_l #include // std::system_error #if defined __APPLE__ || defined(__FreeBSD__) @@ -141,7 +139,7 @@ template struct formatter { }; #ifdef _WIN32 -FMT_API const std::error_category& system_category() FMT_NOEXCEPT; +FMT_API const std::error_category& system_category() noexcept; FMT_BEGIN_DETAIL_NAMESPACE // A converter from UTF-16 to UTF-8. @@ -165,7 +163,7 @@ class utf16_to_utf8 { }; FMT_API void format_windows_error(buffer& out, int error_code, - const char* message) FMT_NOEXCEPT; + const char* message) noexcept; FMT_END_DETAIL_NAMESPACE FMT_API std::system_error vwindows_error(int error_code, string_view format_str, @@ -207,10 +205,9 @@ std::system_error windows_error(int error_code, string_view message, // 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) FMT_NOEXCEPT; +FMT_API void report_windows_error(int error_code, const char* message) noexcept; #else -inline const std::error_category& system_category() FMT_NOEXCEPT { +inline const std::error_category& system_category() noexcept { return std::system_category(); } #endif // _WIN32 @@ -237,13 +234,13 @@ class buffered_file { void operator=(const buffered_file&) = delete; // Constructs a buffered_file object which doesn't represent any file. - buffered_file() FMT_NOEXCEPT : file_(nullptr) {} + buffered_file() noexcept : file_(nullptr) {} // Destroys the object closing the file it represents if any. - FMT_API ~buffered_file() FMT_NOEXCEPT; + FMT_API ~buffered_file() noexcept; public: - buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) { + buffered_file(buffered_file&& other) noexcept : file_(other.file_) { other.file_ = nullptr; } @@ -261,11 +258,9 @@ class buffered_file { FMT_API void close(); // Returns the pointer to a FILE object representing this file. - FILE* get() const FMT_NOEXCEPT { return file_; } + FILE* get() const noexcept { return file_; } - // We place parentheses around fileno to workaround a bug in some versions - // of MinGW that define fileno as a macro. - FMT_API int(fileno)() const; + FMT_API int descriptor() const; void vprint(string_view format_str, format_args args) { fmt::vprint(file_, format_str, args); @@ -279,12 +274,12 @@ class buffered_file { #if FMT_USE_FCNTL // A file. Closed file is represented by a file object with descriptor -1. -// Methods that are not declared with FMT_NOEXCEPT may throw +// 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 file { +class FMT_API file { private: int fd_; // File descriptor. @@ -303,16 +298,16 @@ class file { }; // Constructs a file object which doesn't represent any file. - file() FMT_NOEXCEPT : fd_(-1) {} + file() noexcept : fd_(-1) {} // Opens a file and constructs a file object representing this file. - FMT_API file(cstring_view path, int oflag); + file(cstring_view path, int oflag); public: file(const file&) = delete; void operator=(const file&) = delete; - file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } + file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } // Move assignment is not noexcept because close may throw. file& operator=(file&& other) { @@ -323,43 +318,43 @@ class file { } // Destroys the object closing the file it represents if any. - FMT_API ~file() FMT_NOEXCEPT; + ~file() noexcept; // Returns the file descriptor. - int descriptor() const FMT_NOEXCEPT { return fd_; } + int descriptor() const noexcept { return fd_; } // Closes the file. - FMT_API void close(); + void close(); // Returns the file size. The size has signed type for consistency with // stat::st_size. - FMT_API long long size() const; + long long size() const; // Attempts to read count bytes from the file into the specified buffer. - FMT_API size_t read(void* buffer, size_t count); + size_t read(void* buffer, size_t count); // Attempts to write count bytes from the specified buffer to the file. - FMT_API size_t write(const void* buffer, size_t count); + 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. - FMT_API static file dup(int fd); + static file dup(int fd); // Makes fd be the copy of this file descriptor, closing fd first if // necessary. - FMT_API void dup2(int fd); + void dup2(int fd); // Makes fd be the copy of this file descriptor, closing fd first if // necessary. - FMT_API void dup2(int fd, std::error_code& ec) FMT_NOEXCEPT; + 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. - FMT_API static void pipe(file& read_end, file& write_end); + 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. - FMT_API buffered_file fdopen(const char* mode); + buffered_file fdopen(const char* mode); }; // Returns the memory page size. @@ -462,7 +457,7 @@ class FMT_API ostream final : private detail::buffer { * ````: Flags passed to `open `_ - (``file::WRONLY | file::CREATE`` by default) + (``file::WRONLY | file::CREATE | file::TRUNC`` by default) * ``buffer_size=``: Output buffer size **Example**:: @@ -477,50 +472,6 @@ inline ostream output_file(cstring_view path, T... params) { } #endif // FMT_USE_FCNTL -#ifdef FMT_LOCALE -// A "C" numeric locale. -class locale { - private: -# ifdef _WIN32 - using locale_t = _locale_t; - - static void freelocale(locale_t loc) { _free_locale(loc); } - - static double strtod_l(const char* nptr, char** endptr, _locale_t loc) { - return _strtod_l(nptr, endptr, loc); - } -# endif - - locale_t locale_; - - public: - using type = locale_t; - locale(const locale&) = delete; - void operator=(const locale&) = delete; - - locale() { -# ifndef _WIN32 - locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr)); -# else - locale_ = _create_locale(LC_NUMERIC, "C"); -# endif - if (!locale_) FMT_THROW(system_error(errno, "cannot create locale")); - } - ~locale() { freelocale(locale_); } - - type get() const { return locale_; } - - // Converts string to floating-point number and advances str past the end - // of the parsed input. - FMT_DEPRECATED double strtod(const char*& str) const { - char* end = nullptr; - double result = strtod_l(str, &end, locale_); - str = end; - return result; - } -}; -using Locale FMT_DEPRECATED_ALIAS = locale; -#endif // FMT_LOCALE FMT_MODULE_EXPORT_END FMT_END_NAMESPACE diff --git a/externals/dynarmic/externals/fmt/include/fmt/ostream.h b/externals/dynarmic/externals/fmt/include/fmt/ostream.h index 3d716ece8..394d947c5 100755 --- a/externals/dynarmic/externals/fmt/include/fmt/ostream.h +++ b/externals/dynarmic/externals/fmt/include/fmt/ostream.h @@ -8,6 +8,7 @@ #ifndef FMT_OSTREAM_H_ #define FMT_OSTREAM_H_ +#include #include #include "format.h" @@ -45,15 +46,59 @@ struct is_streamable< enable_if_t< std::is_arithmetic::value || std::is_array::value || std::is_pointer::value || std::is_same::value || - std::is_same>::value || + std::is_convertible>::value || std::is_same>::value || (std::is_convertible::value && !std::is_enum::value)>> : std::false_type {}; +template FILE* get_file(std::basic_filebuf&) { + return nullptr; +} + +struct dummy_filebuf { + FILE* _Myfile; +}; +template struct ms_filebuf { + using type = dummy_filebuf; +}; +template struct ms_filebuf { + using type = T; +}; +using filebuf_type = ms_filebuf::type; + +FILE* get_file(filebuf_type& buf); + +// Generate a unique explicit instantion in every translation unit using a tag +// type in an anonymous namespace. +namespace { +struct filebuf_access_tag {}; +} // namespace +template +class filebuf_access { + friend FILE* get_file(filebuf_type& buf) { return buf.*file; } +}; +template class filebuf_access; + +inline bool write(std::filebuf& buf, fmt::string_view data) { + FILE* f = get_file(buf); + if (!f) return false; + print(f, data); + return true; +} +inline bool write(std::wfilebuf&, fmt::basic_string_view) { + return false; +} + // Write the content of buf to os. // It is a separate function rather than a part of vprint to simplify testing. template void write_buffer(std::basic_ostream& os, buffer& buf) { + if (const_check(FMT_MSC_VERSION)) { + auto filebuf = dynamic_cast*>(os.rdbuf()); + if (filebuf && write(*filebuf, {buf.data(), buf.size()})) return; + } const Char* buf_data = buf.data(); using unsigned_streamsize = std::make_unsigned::type; unsigned_streamsize size = buf.size(); @@ -76,38 +121,65 @@ void format_value(buffer& buf, const T& value, #endif output << value; output.exceptions(std::ios_base::failbit | std::ios_base::badbit); - buf.try_resize(buf.size()); } -// Formats an object of type T that has an overloaded ostream operator<<. -template -struct fallback_formatter::value>> - : private formatter, Char> { - using formatter, Char>::parse; +template struct streamed_view { const T& value; }; - template - auto format(const T& value, basic_format_context& ctx) +} // namespace detail + +// Formats an object of type T that has an overloaded ostream operator<<. +template +struct basic_ostream_formatter : formatter, Char> { + template + auto format(const T& value, basic_format_context& ctx) const -> OutputIt { auto buffer = basic_memory_buffer(); format_value(buffer, value, ctx.locale()); return formatter, Char>::format( {buffer.data(), buffer.size()}, ctx); } +}; - // DEPRECATED! +using ostream_formatter = basic_ostream_formatter; + +template +struct formatter> : ostream_formatter { template - auto format(const T& value, basic_printf_context& ctx) - -> OutputIt { - auto buffer = basic_memory_buffer(); - format_value(buffer, value, ctx.locale()); - return std::copy(buffer.begin(), buffer.end(), ctx.out()); + auto format(detail::streamed_view view, + basic_format_context& ctx) const -> OutputIt { + return ostream_formatter::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 +auto streamed(const T& value) -> detail::streamed_view { + return {value}; +} + +namespace detail { + +// Formats an object of type T that has an overloaded ostream operator<<. +template +struct fallback_formatter::value>> + : basic_ostream_formatter { + using basic_ostream_formatter::format; +}; + } // namespace detail -FMT_MODULE_EXPORT -template -void vprint(std::basic_ostream& os, basic_string_view format_str, +FMT_MODULE_EXPORT template +void vprint(std::basic_ostream& os, + basic_string_view> format_str, basic_format_args>> args) { auto buffer = basic_memory_buffer(); detail::vformat_to(buffer, format_str, args); @@ -123,13 +195,19 @@ void vprint(std::basic_ostream& os, basic_string_view format_str, fmt::print(cerr, "Don't {}!", "panic"); \endrst */ -FMT_MODULE_EXPORT -template ::value, char_t>> -void print(std::basic_ostream& os, const S& format_str, Args&&... args) { - vprint(os, to_string_view(format_str), - fmt::make_args_checked(format_str, args...)); +FMT_MODULE_EXPORT template +void print(std::ostream& os, format_string fmt, T&&... args) { + vprint(os, fmt, fmt::make_format_args(args...)); } + +FMT_MODULE_EXPORT +template +void print(std::wostream& os, + basic_format_string...> fmt, + Args&&... args) { + vprint(os, fmt, fmt::make_format_args>(args...)); +} + FMT_END_NAMESPACE #endif // FMT_OSTREAM_H_ diff --git a/externals/dynarmic/externals/fmt/include/fmt/printf.h b/externals/dynarmic/externals/fmt/include/fmt/printf.h index 19d550f6c..70a592dc2 100755 --- a/externals/dynarmic/externals/fmt/include/fmt/printf.h +++ b/externals/dynarmic/externals/fmt/include/fmt/printf.h @@ -10,7 +10,6 @@ #include // std::max #include // std::numeric_limits -#include #include "format.h" @@ -561,7 +560,7 @@ inline auto vsprintf( basic_format_args>> args) -> std::basic_string { basic_memory_buffer buffer; - vprintf(buffer, to_string_view(fmt), args); + vprintf(buffer, detail::to_string_view(fmt), args); return to_string(buffer); } @@ -578,7 +577,8 @@ template ::value, char_t>> inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string { using context = basic_printf_context_t; - return vsprintf(to_string_view(fmt), fmt::make_format_args(args...)); + return vsprintf(detail::to_string_view(fmt), + fmt::make_format_args(args...)); } template > @@ -587,7 +587,7 @@ inline auto vfprintf( basic_format_args>> args) -> int { basic_memory_buffer buffer; - vprintf(buffer, to_string_view(fmt), args); + vprintf(buffer, detail::to_string_view(fmt), args); size_t size = buffer.size(); return std::fwrite(buffer.data(), sizeof(Char), size, f) < size ? -1 @@ -606,7 +606,7 @@ inline auto vfprintf( template > inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { using context = basic_printf_context_t; - return vfprintf(f, to_string_view(fmt), + return vfprintf(f, detail::to_string_view(fmt), fmt::make_format_args(args...)); } @@ -615,7 +615,7 @@ inline auto vprintf( const S& fmt, basic_format_args>> args) -> int { - return vfprintf(stdout, to_string_view(fmt), args); + return vfprintf(stdout, detail::to_string_view(fmt), args); } /** @@ -630,27 +630,10 @@ inline auto vprintf( template ::value)> inline auto printf(const S& fmt, const T&... args) -> int { return vprintf( - to_string_view(fmt), + detail::to_string_view(fmt), fmt::make_format_args>>(args...)); } -template > -FMT_DEPRECATED auto vfprintf( - std::basic_ostream& os, const S& fmt, - basic_format_args>> args) - -> int { - basic_memory_buffer buffer; - vprintf(buffer, to_string_view(fmt), args); - os.write(buffer.data(), static_cast(buffer.size())); - return static_cast(buffer.size()); -} -template > -FMT_DEPRECATED auto fprintf(std::basic_ostream& os, const S& fmt, - const T&... args) -> int { - return vfprintf(os, to_string_view(fmt), - fmt::make_format_args>(args...)); -} - FMT_MODULE_EXPORT_END FMT_END_NAMESPACE diff --git a/externals/dynarmic/externals/fmt/include/fmt/ranges.h b/externals/dynarmic/externals/fmt/include/fmt/ranges.h index eb9fb8a92..10429fc8e 100755 --- a/externals/dynarmic/externals/fmt/include/fmt/ranges.h +++ b/externals/dynarmic/externals/fmt/include/fmt/ranges.h @@ -55,7 +55,7 @@ template class is_std_string_like { template static void check(...); public: - static FMT_CONSTEXPR_DECL const bool value = + static constexpr const bool value = is_string::value || std::is_convertible>::value || !std::is_void(nullptr))>::value; @@ -70,9 +70,9 @@ template class is_map { public: #ifdef FMT_FORMAT_MAP_AS_LIST - static FMT_CONSTEXPR_DECL const bool value = false; + static constexpr const bool value = false; #else - static FMT_CONSTEXPR_DECL const bool value = + static constexpr const bool value = !std::is_void(nullptr))>::value; #endif }; @@ -83,9 +83,9 @@ template class is_set { public: #ifdef FMT_FORMAT_SET_AS_LIST - static FMT_CONSTEXPR_DECL const bool value = false; + static constexpr const bool value = false; #else - static FMT_CONSTEXPR_DECL const bool value = + static constexpr const bool value = !std::is_void(nullptr))>::value && !is_map::value; #endif }; @@ -94,7 +94,7 @@ template struct conditional_helper {}; template struct is_range_ : std::false_type {}; -#if !FMT_MSC_VER || FMT_MSC_VER > 1800 +#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800 # define FMT_DECLTYPE_RETURN(val) \ ->decltype(val) { return val; } \ @@ -174,12 +174,12 @@ template class is_tuple_like_ { template static void check(...); public: - static FMT_CONSTEXPR_DECL const bool value = + static constexpr const bool value = !std::is_void(nullptr))>::value; }; // Check for integer_sequence -#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 +#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900 template using integer_sequence = std::integer_sequence; template using index_sequence = std::index_sequence; @@ -202,8 +202,33 @@ template using make_index_sequence = make_integer_sequence; #endif +template +using tuple_index_sequence = make_index_sequence::value>; + +template ::value> +class is_tuple_formattable_ { + public: + static constexpr const bool value = false; +}; +template class is_tuple_formattable_ { + template + static std::true_type check2(index_sequence, + integer_sequence); + static std::false_type check2(...); + template + static decltype(check2( + index_sequence{}, + integer_sequence< + bool, (is_formattable::type, + C>::value)...>{})) check(index_sequence); + + public: + static constexpr const bool value = + decltype(check(tuple_index_sequence{}))::value; +}; + template -void for_each(index_sequence, Tuple&& tup, F&& f) FMT_NOEXCEPT { +void for_each(index_sequence, Tuple&& tup, F&& f) noexcept { using std::get; // using free function get(T) now. const int _[] = {0, ((void)f(get(tup)), 0)...}; @@ -221,9 +246,36 @@ template void for_each(Tuple&& tup, F&& f) { for_each(indexes, std::forward(tup), std::forward(f)); } +#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920 +// Older MSVC doesn't get the reference type correctly for arrays. +template struct range_reference_type_impl { + using type = decltype(*detail::range_begin(std::declval())); +}; + +template struct range_reference_type_impl { + using type = T&; +}; + +template +using range_reference_type = typename range_reference_type_impl::type; +#else template -using value_type = - remove_cvref_t()))>; +using range_reference_type = + decltype(*detail::range_begin(std::declval())); +#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 +using uncvref_type = remove_cvref_t>; + +template +using uncvref_first_type = remove_cvref_t< + decltype(std::declval>().first)>; + +template +using uncvref_second_type = remove_cvref_t< + decltype(std::declval>().second)>; template OutputIt write_delimiter(OutputIt out) { *out++ = ','; @@ -231,286 +283,9 @@ template OutputIt write_delimiter(OutputIt out) { return out; } -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(x); - auto current = true; - for (size_t i = 0; i < normal_size; ++i) { - auto v = static_cast(normal[i]); - auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v; - xsigned -= len; - if (xsigned < 0) break; - current = !current; - } - return current; -} - -// Returns true iff the code point cp is printable. -// This code is generated by support/printable.py. -inline 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(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; -} - -inline auto needs_escape(uint32_t cp) -> bool { - return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' || - !is_printable(cp); -} - -template struct find_escape_result { - const Char* begin; - const Char* end; - uint32_t cp; -}; - -template -auto find_escape(const Char* begin, const Char* end) - -> find_escape_result { - for (; begin != end; ++begin) { - auto cp = static_cast::type>(*begin); - if (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 { - if (!is_utf8()) return find_escape(begin, end); - auto result = find_escape_result{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; -} - template auto write_range_entry(OutputIt out, basic_string_view str) -> OutputIt { - *out++ = '"'; - auto begin = str.begin(), end = str.end(); - do { - auto escape = find_escape(begin, end); - out = copy_str(begin, escape.begin, out); - begin = escape.end; - if (!begin) break; - auto c = static_cast(escape.cp); - switch (escape.cp) { - case '\n': - *out++ = '\\'; - c = 'n'; - break; - case '\r': - *out++ = '\\'; - c = 'r'; - break; - case '\t': - *out++ = '\\'; - c = 't'; - break; - case '"': - FMT_FALLTHROUGH; - case '\\': - *out++ = '\\'; - break; - default: - if (is_utf8()) { - if (escape.cp < 0x100) { - out = format_to(out, "\\x{:02x}", escape.cp); - continue; - } - if (escape.cp < 0x10000) { - out = format_to(out, "\\u{:04x}", escape.cp); - continue; - } - if (escape.cp < 0x110000) { - out = format_to(out, "\\U{:08x}", escape.cp); - continue; - } - } - for (Char escape_char : basic_string_view( - escape.begin, to_unsigned(escape.end - escape.begin))) { - out = format_to( - out, "\\x{:02x}", - static_cast::type>(escape_char)); - } - continue; - } - *out++ = c; - } while (begin != end); - *out++ = '"'; - return out; + return write_escaped_string(out, str); } template OutputIt { template ::value)> OutputIt write_range_entry(OutputIt out, const Arg v) { - *out++ = '\''; - *out++ = v; - *out++ = '\''; - return out; + return write_escaped_char(out, v); } template < @@ -540,12 +312,19 @@ OutputIt write_range_entry(OutputIt out, const Arg& v) { } // namespace detail template struct is_tuple_like { - static FMT_CONSTEXPR_DECL const bool value = + static constexpr const bool value = detail::is_tuple_like_::value && !detail::is_range_::value; }; +template struct is_tuple_formattable { + static constexpr const bool value = + detail::is_tuple_formattable_::value; +}; + template -struct formatter::value>> { +struct formatter::value && + fmt::is_tuple_formattable::value>> { private: // C++11 generic lambda for format(). template struct format_each { @@ -565,7 +344,8 @@ struct formatter::value>> { } template - auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { + auto format(const TupleT& values, FormatContext& ctx) const + -> decltype(ctx.out()) { auto out = ctx.out(); *out++ = '('; detail::for_each(values, format_each{0, out}); @@ -575,50 +355,101 @@ struct formatter::value>> { }; template struct is_range { - static FMT_CONSTEXPR_DECL const bool value = + static constexpr const bool value = detail::is_range_::value && !detail::is_std_string_like::value && !detail::is_map::value && !std::is_convertible>::value && !std::is_constructible, T>::value; }; -template +namespace detail { +template struct range_mapper { + using mapper = arg_mapper; + + template , Context>::value)> + static auto map(T&& value) -> T&& { + return static_cast(value); + } + template , Context>::value)> + static auto map(T&& value) + -> decltype(mapper().map(static_cast(value))) { + return mapper().map(static_cast(value)); + } +}; + +template +using range_formatter_type = conditional_t< + is_formattable::value, + formatter>{}.map( + std::declval()))>, + Char>, + fallback_formatter>; + +template +using maybe_const_range = + conditional_t::value, const R, R>; +} // namespace detail + +template struct formatter< - T, Char, + R, Char, enable_if_t< - fmt::is_range::value -// Workaround a bug in MSVC 2019 and earlier. -#if !FMT_MSC_VER - && (is_formattable, Char>::value || - detail::has_fallback_formatter, Char>::value) + conjunction +// Workaround a bug in MSVC 2017 and earlier. +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920 + , + disjunction< + is_formattable>, + Char>, + detail::has_fallback_formatter< + detail::uncvref_type>, Char> + > #endif + >::value >> { + + using range_type = detail::maybe_const_range; + using formatter_type = + detail::range_formatter_type>; + formatter_type underlying_; + bool custom_specs_ = false; + template FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); + auto it = ctx.begin(); + auto end = ctx.end(); + if (it == end || *it == '}') return it; + + if (*it != ':') + FMT_THROW(format_error("no top-level range formatters supported")); + + custom_specs_ = true; + ++it; + ctx.advance_to(it); + return underlying_.parse(ctx); } - template < - typename FormatContext, typename U, - FMT_ENABLE_IF( - std::is_same::value, - const T, T>>::value)> - auto format(U& range, FormatContext& ctx) -> decltype(ctx.out()) { -#ifdef FMT_DEPRECATED_BRACED_RANGES - Char prefix = '{'; - Char postfix = '}'; -#else - Char prefix = detail::is_set::value ? '{' : '['; - Char postfix = detail::is_set::value ? '}' : ']'; -#endif + template + auto format(range_type& range, FormatContext& ctx) const + -> decltype(ctx.out()) { + Char prefix = detail::is_set::value ? '{' : '['; + Char postfix = detail::is_set::value ? '}' : ']'; + detail::range_mapper> mapper; auto out = ctx.out(); *out++ = prefix; int i = 0; - auto it = std::begin(range); - auto end = std::end(range); + auto it = detail::range_begin(range); + auto end = detail::range_end(range); for (; it != end; ++it) { if (i > 0) out = detail::write_delimiter(out); - out = detail::write_range_entry(out, *it); + if (custom_specs_) { + ctx.advance_to(out); + out = underlying_.format(mapper.map(*it), ctx); + } else { + out = detail::write_range_entry(out, *it); + } ++i; } *out++ = postfix; @@ -629,14 +460,21 @@ struct formatter< template struct formatter< T, Char, - enable_if_t< - detail::is_map::value -// Workaround a bug in MSVC 2019 and earlier. -#if !FMT_MSC_VER - && (is_formattable, Char>::value || - detail::has_fallback_formatter, Char>::value) + enable_if_t +// Workaround a bug in MSVC 2017 and earlier. +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920 + , + disjunction< + is_formattable, Char>, + detail::has_fallback_formatter, Char> + >, + disjunction< + is_formattable, Char>, + detail::has_fallback_formatter, Char> + > #endif - >> { + >::value + >> { template FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { return ctx.begin(); @@ -647,7 +485,7 @@ struct formatter< FMT_ENABLE_IF( std::is_same::value, const T, T>>::value)> - auto format(U& map, FormatContext& ctx) -> decltype(ctx.out()) { + auto format(U& map, FormatContext& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); *out++ = '{'; int i = 0; diff --git a/externals/dynarmic/externals/fmt/include/fmt/std.h b/externals/dynarmic/externals/fmt/include/fmt/std.h new file mode 100755 index 000000000..227f48411 --- /dev/null +++ b/externals/dynarmic/externals/fmt/include/fmt/std.h @@ -0,0 +1,176 @@ +// 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 +#include +#include + +#include "ostream.h" + +#if FMT_HAS_INCLUDE() +# include +#endif +// Checking FMT_CPLUSPLUS for warning suppression in MSVC. +#if FMT_CPLUSPLUS >= 201703L +# if FMT_HAS_INCLUDE() +# include +# endif +# if FMT_HAS_INCLUDE() +# include +# endif +#endif + +#ifdef __cpp_lib_filesystem +FMT_BEGIN_NAMESPACE + +namespace detail { + +template +void write_escaped_path(basic_memory_buffer& quoted, + const std::filesystem::path& p) { + write_escaped_string(std::back_inserter(quoted), p.string()); +} +# ifdef _WIN32 +template <> +inline void write_escaped_path(basic_memory_buffer& quoted, + const std::filesystem::path& p) { + auto s = p.u8string(); + write_escaped_string( + std::back_inserter(quoted), + string_view(reinterpret_cast(s.c_str()), s.size())); +} +# endif +template <> +inline void write_escaped_path( + basic_memory_buffer& quoted, + const std::filesystem::path& p) { + write_escaped_string( + std::back_inserter(quoted), p.native()); +} + +} // namespace detail + +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920 +// For MSVC 2017 and earlier using the partial specialization +// would cause an ambiguity error, therefore we provide it only +// conditionally. +template +struct formatter + : formatter> { + template + auto format(const std::filesystem::path& p, FormatContext& ctx) const -> + typename FormatContext::iterator { + basic_memory_buffer quoted; + detail::write_escaped_path(quoted, p); + return formatter>::format( + basic_string_view(quoted.data(), quoted.size()), ctx); + } +}; +#endif +FMT_END_NAMESPACE +#endif + +FMT_BEGIN_NAMESPACE +template +struct formatter : basic_ostream_formatter {}; +FMT_END_NAMESPACE + +#ifdef __cpp_lib_variant +FMT_BEGIN_NAMESPACE +template struct formatter { + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template + auto format(const std::monostate&, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + out = detail::write(out, "monostate"); + return out; + } +}; + +namespace detail { + +template +using variant_index_sequence = + std::make_index_sequence::value>; + +// variant_size and variant_alternative check. +template +struct is_variant_like_ : std::false_type {}; +template +struct is_variant_like_::value)>> + : std::true_type {}; + +// formattable element check +template class is_variant_formattable_ { + template + static std::conjunction< + is_formattable, C>...> + check(std::index_sequence); + + public: + static constexpr const bool value = + decltype(check(variant_index_sequence{}))::value; +}; + +template +auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt { + if constexpr (is_string::value) + return write_escaped_string(out, detail::to_string_view(v)); + else if constexpr (std::is_same_v) + return write_escaped_char(out, v); + else + return write(out, v); +} + +} // namespace detail + +template struct is_variant_like { + static constexpr const bool value = detail::is_variant_like_::value; +}; + +template struct is_variant_formattable { + static constexpr const bool value = + detail::is_variant_formattable_::value; +}; + +template +struct formatter< + Variant, Char, + std::enable_if_t, is_variant_formattable>>> { + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template + auto format(const Variant& value, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + + out = detail::write(out, "variant("); + std::visit( + [&](const auto& v) { + out = detail::write_variant_alternative(out, v); + }, + value); + *out++ = ')'; + return out; + } +}; +FMT_END_NAMESPACE +#endif + +#endif // FMT_STD_H_ diff --git a/externals/dynarmic/externals/fmt/include/fmt/xchar.h b/externals/dynarmic/externals/fmt/include/fmt/xchar.h index 55825077f..2865b76ef 100755 --- a/externals/dynarmic/externals/fmt/include/fmt/xchar.h +++ b/externals/dynarmic/externals/fmt/include/fmt/xchar.h @@ -47,12 +47,7 @@ constexpr format_arg_store make_wformat_args( } inline namespace literals { -constexpr auto operator"" _format(const wchar_t* s, size_t n) - -> detail::udl_formatter { - return {{s, n}}; -} - -#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS constexpr detail::udl_arg operator"" _a(const wchar_t* s, size_t) { return {s}; } @@ -87,13 +82,23 @@ auto vformat(basic_string_view format_str, return to_string(buffer); } +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 409 +template +using wformat_string = basic_format_string...>; +#endif + +template +auto format(wformat_string fmt, T&&... args) -> std::wstring { + return vformat(fmt, fmt::make_wformat_args(args...)); +} + // Pass char_t as a default template parameter instead of using // std::basic_string> to reduce the symbol size. template , FMT_ENABLE_IF(!std::is_same::value)> auto format(const S& format_str, Args&&... args) -> std::basic_string { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return vformat(to_string_view(format_str), vargs); + return vformat(detail::to_string_view(format_str), + fmt::make_format_args>(args...)); } template , @@ -103,7 +108,7 @@ inline auto vformat( const Locale& loc, const S& format_str, basic_format_args>> args) -> std::basic_string { - return detail::vformat(loc, to_string_view(format_str), args); + return detail::vformat(loc, detail::to_string_view(format_str), args); } template ::value)> inline auto format(const Locale& loc, const S& format_str, Args&&... args) -> std::basic_string { - return detail::vformat(loc, to_string_view(format_str), - fmt::make_args_checked(format_str, args...)); + return detail::vformat(loc, detail::to_string_view(format_str), + fmt::make_format_args>(args...)); } template , @@ -123,7 +128,7 @@ auto vformat_to(OutputIt out, const S& format_str, basic_format_args>> args) -> OutputIt { auto&& buf = detail::get_buffer(out); - detail::vformat_to(buf, to_string_view(format_str), args); + detail::vformat_to(buf, detail::to_string_view(format_str), args); return detail::get_iterator(buf); } @@ -132,18 +137,8 @@ template ::value&& detail::is_exotic_char::value)> inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { - const auto& vargs = fmt::make_args_checked(fmt, args...); - return vformat_to(out, to_string_view(fmt), vargs); -} - -template ::value)> -FMT_DEPRECATED auto format_to(basic_memory_buffer& buf, - const S& format_str, Args&&... args) -> - typename buffer_context::iterator { - const auto& vargs = fmt::make_args_checked(format_str, args...); - detail::vformat_to(buf, to_string_view(format_str), vargs, {}); - return detail::buffer_appender(buf); + return vformat_to(out, detail::to_string_view(fmt), + fmt::make_format_args>(args...)); } template >> args) -> OutputIt { auto&& buf = detail::get_buffer(out); - vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc)); + vformat_to(buf, detail::to_string_view(format_str), args, + detail::locale_ref(loc)); return detail::get_iterator(buf); } @@ -167,8 +163,8 @@ template < inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, Args&&... args) -> typename std::enable_if::type { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return vformat_to(out, loc, to_string_view(format_str), vargs); + return vformat_to(out, loc, to_string_view(format_str), + fmt::make_format_args>(args...)); } template ::value)> inline auto format_to_n(OutputIt out, size_t n, const S& fmt, const Args&... args) -> format_to_n_result { - const auto& vargs = fmt::make_args_checked(fmt, args...); - return vformat_to_n(out, n, to_string_view(fmt), vargs); + return vformat_to_n(out, n, detail::to_string_view(fmt), + fmt::make_format_args>(args...)); } template , FMT_ENABLE_IF(detail::is_exotic_char::value)> inline auto formatted_size(const S& fmt, Args&&... args) -> size_t { detail::counting_buffer buf; - const auto& vargs = fmt::make_args_checked(fmt, args...); - detail::vformat_to(buf, to_string_view(fmt), vargs); + detail::vformat_to(buf, detail::to_string_view(fmt), + fmt::make_format_args>(args...)); return buf.count(); } diff --git a/externals/dynarmic/externals/fmt/src/format.cc b/externals/dynarmic/externals/fmt/src/format.cc index ecb8cc79a..99b7e9dd4 100755 --- a/externals/dynarmic/externals/fmt/src/format.cc +++ b/externals/dynarmic/externals/fmt/src/format.cc @@ -10,115 +10,38 @@ FMT_BEGIN_NAMESPACE namespace detail { -// DEPRECATED! -template struct basic_data { - FMT_API static constexpr const char digits[100][2] = { - {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, - {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, - {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, - {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, - {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, - {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, - {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'}, - {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, - {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, - {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, - {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, - {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'}, - {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, - {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, - {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, - {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, - {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; - FMT_API static constexpr const char hex_digits[] = "0123456789abcdef"; - FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '}; - FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1, - 0}; - FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1, - 0}; - FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', - 0x1000000u | ' '}; -}; - -#ifdef FMT_SHARED -// Required for -flto, -fivisibility=hidden and -shared to work -extern template struct basic_data; -#endif - -#if __cplusplus < 201703L -// DEPRECATED! These are here only for ABI compatiblity. -template constexpr const char basic_data::digits[][2]; -template constexpr const char basic_data::hex_digits[]; -template constexpr const char basic_data::signs[]; -template constexpr const char basic_data::left_padding_shifts[]; -template -constexpr const char basic_data::right_padding_shifts[]; -template constexpr const unsigned basic_data::prefixes[]; -#endif - -template -int format_float(char* buf, std::size_t size, const char* format, int precision, - T value) { -#ifdef FMT_FUZZ - if (precision > 100000) - throw std::runtime_error( - "fuzz mode - avoid large allocation inside snprintf"); -#endif - // Suppress the warning about nonliteral format string. - int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; - return precision < 0 ? snprintf_ptr(buf, size, format, value) - : snprintf_ptr(buf, size, format, precision, value); -} - -template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(float x) - FMT_NOEXCEPT; -template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(double x) - FMT_NOEXCEPT; -} // namespace detail - -// Workaround a bug in MSVC2013 that prevents instantiation of format_float. -int (*instantiate_format_float)(double, int, detail::float_specs, - detail::buffer&) = detail::format_float; +template FMT_API auto dragonbox::to_decimal(float x) noexcept + -> dragonbox::decimal_fp; +template FMT_API auto dragonbox::to_decimal(double x) noexcept + -> dragonbox::decimal_fp; #ifndef FMT_STATIC_THOUSANDS_SEPARATOR -template FMT_API detail::locale_ref::locale_ref(const std::locale& loc); -template FMT_API std::locale detail::locale_ref::get() const; +template FMT_API locale_ref::locale_ref(const std::locale& loc); +template FMT_API auto locale_ref::get() const -> std::locale; #endif // Explicit instantiations for char. -template FMT_API auto detail::thousands_sep_impl(locale_ref) +template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; -template FMT_API char detail::decimal_point_impl(locale_ref); +template FMT_API auto decimal_point_impl(locale_ref) -> char; -template FMT_API void detail::buffer::append(const char*, const char*); +template FMT_API void buffer::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 detail::vformat_to( - detail::buffer&, string_view, - basic_format_args, detail::locale_ref); - -template FMT_API int detail::snprintf_float(double, int, detail::float_specs, - detail::buffer&); -template FMT_API int detail::snprintf_float(long double, int, - detail::float_specs, - detail::buffer&); -template FMT_API int detail::format_float(double, int, detail::float_specs, - detail::buffer&); -template FMT_API int detail::format_float(long double, int, detail::float_specs, - detail::buffer&); +template FMT_API void vformat_to(buffer&, string_view, + basic_format_args, + locale_ref); // Explicit instantiations for wchar_t. -template FMT_API auto detail::thousands_sep_impl(locale_ref) +template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; -template FMT_API wchar_t detail::decimal_point_impl(locale_ref); +template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; -template FMT_API void detail::buffer::append(const wchar_t*, - const wchar_t*); - -template struct detail::basic_data; +template FMT_API void buffer::append(const wchar_t*, const wchar_t*); +} // namespace detail FMT_END_NAMESPACE diff --git a/externals/dynarmic/externals/fmt/src/os.cc b/externals/dynarmic/externals/fmt/src/os.cc index 04b4dc506..f388ead01 100755 --- a/externals/dynarmic/externals/fmt/src/os.cc +++ b/externals/dynarmic/externals/fmt/src/os.cc @@ -35,9 +35,15 @@ # 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 @@ -45,10 +51,6 @@ # include #endif -#ifdef fileno -# undef fileno -#endif - namespace { #ifdef _WIN32 // Return type of read and write functions. @@ -107,7 +109,7 @@ class system_message { unsigned long result_; wchar_t* message_; - static bool is_whitespace(wchar_t c) FMT_NOEXCEPT { + static bool is_whitespace(wchar_t c) noexcept { return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0'; } @@ -126,15 +128,15 @@ class system_message { } } ~system_message() { LocalFree(message_); } - explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; } - operator basic_string_view() const FMT_NOEXCEPT { + explicit operator bool() const noexcept { return result_ != 0; } + operator basic_string_view() const noexcept { return basic_string_view(message_, result_); } }; class utf8_system_category final : public std::error_category { public: - const char* name() const FMT_NOEXCEPT override { return "system"; } + const char* name() const noexcept override { return "system"; } std::string message(int error_code) const override { system_message msg(error_code); if (msg) { @@ -149,7 +151,7 @@ class utf8_system_category final : public std::error_category { } // namespace detail -FMT_API const std::error_category& system_category() FMT_NOEXCEPT { +FMT_API const std::error_category& system_category() noexcept { static const detail::utf8_system_category category; return category; } @@ -161,13 +163,13 @@ std::system_error vwindows_error(int err_code, string_view format_str, } void detail::format_windows_error(detail::buffer& out, int error_code, - const char* message) FMT_NOEXCEPT { + 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) { - format_to(buffer_appender(out), "{}: {}", message, utf8_message); + fmt::format_to(buffer_appender(out), "{}: {}", message, utf8_message); return; } } @@ -176,12 +178,12 @@ void detail::format_windows_error(detail::buffer& out, int error_code, format_error_code(out, error_code, message); } -void report_windows_error(int error_code, const char* message) FMT_NOEXCEPT { +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() FMT_NOEXCEPT { +buffered_file::~buffered_file() noexcept { if (file_ && FMT_SYSTEM(fclose(file_)) != 0) report_system_error(errno, "cannot close file"); } @@ -200,11 +202,8 @@ void buffered_file::close() { if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); } -// A macro used to prevent expansion of fileno on broken versions of MinGW. -#define FMT_ARGS - -int buffered_file::fileno() const { - int fd = FMT_POSIX_CALL(fileno FMT_ARGS(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; } @@ -214,7 +213,8 @@ file::file(cstring_view path, int oflag) { # ifdef _WIN32 using mode_t = int; # endif - mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + 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)); @@ -225,7 +225,7 @@ file::file(cstring_view path, int oflag) { FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); } -file::~file() FMT_NOEXCEPT { +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) @@ -299,7 +299,7 @@ void file::dup2(int fd) { } } -void file::dup2(int fd, std::error_code& ec) FMT_NOEXCEPT { +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()); diff --git a/externals/dynarmic/externals/fmt/support/bazel/.bazelversion b/externals/dynarmic/externals/fmt/support/bazel/.bazelversion index fae6e3d04..ac14c3dfa 100755 --- a/externals/dynarmic/externals/fmt/support/bazel/.bazelversion +++ b/externals/dynarmic/externals/fmt/support/bazel/.bazelversion @@ -1 +1 @@ -4.2.1 +5.1.1 diff --git a/externals/dynarmic/externals/fmt/support/bazel/BUILD.bazel b/externals/dynarmic/externals/fmt/support/bazel/BUILD.bazel index 3380bbcab..085765890 100755 --- a/externals/dynarmic/externals/fmt/support/bazel/BUILD.bazel +++ b/externals/dynarmic/externals/fmt/support/bazel/BUILD.bazel @@ -11,18 +11,17 @@ cc_library( "include/fmt/color.h", "include/fmt/compile.h", "include/fmt/core.h", - "include/fmt/format.h", "include/fmt/format-inl.h", - "include/fmt/locale.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", - "src", + "include", ], strip_include_prefix = "include", visibility = ["//visibility:public"], diff --git a/externals/dynarmic/externals/fmt/support/cmake/cxx14.cmake b/externals/dynarmic/externals/fmt/support/cmake/cxx14.cmake index 16ff57541..deb1e26fb 100755 --- a/externals/dynarmic/externals/fmt/support/cmake/cxx14.cmake +++ b/externals/dynarmic/externals/fmt/support/cmake/cxx14.cmake @@ -1,7 +1,11 @@ # C++14 feature support detection -include(CheckCXXSourceCompiles) 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) @@ -9,35 +13,38 @@ endif() message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}") if (CMAKE_CXX_STANDARD EQUAL 20) - check_cxx_compiler_flag(-std=c++20 has_std_20_flag) - check_cxx_compiler_flag(-std=c++2a has_std_2a_flag) + 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) - check_cxx_compiler_flag(-std=c++17 has_std_17_flag) - check_cxx_compiler_flag(-std=c++1z has_std_1z_flag) + 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) - check_cxx_compiler_flag(-std=c++14 has_std_14_flag) - check_cxx_compiler_flag(-std=c++1y has_std_1y_flag) + 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) - check_cxx_compiler_flag(-std=c++11 has_std_11_flag) - check_cxx_compiler_flag(-std=c++0x has_std_0x_flag) + 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) @@ -45,26 +52,3 @@ elseif (CMAKE_CXX_STANDARD EQUAL 11) set(CXX_STANDARD_FLAG -std=c++0x) endif () endif () - -set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG}) - -# Check if user-defined literals are available -check_cxx_source_compiles(" - void operator\"\" _udl(long double); - int main() {}" - SUPPORTS_USER_DEFINED_LITERALS) -if (NOT SUPPORTS_USER_DEFINED_LITERALS) - set (SUPPORTS_USER_DEFINED_LITERALS OFF) -endif () - -# Check if is available -set(CMAKE_REQUIRED_FLAGS -std=c++1z) -check_cxx_source_compiles(" - #include - int main() {}" - FMT_HAS_VARIANT) -if (NOT FMT_HAS_VARIANT) - set (FMT_HAS_VARIANT OFF) -endif () - -set(CMAKE_REQUIRED_FLAGS ) diff --git a/externals/dynarmic/externals/fmt/support/cmake/fmt-config.cmake.in b/externals/dynarmic/externals/fmt/support/cmake/fmt-config.cmake.in index 71e302860..bc1684f24 100755 --- a/externals/dynarmic/externals/fmt/support/cmake/fmt-config.cmake.in +++ b/externals/dynarmic/externals/fmt/support/cmake/fmt-config.cmake.in @@ -1,4 +1,7 @@ @PACKAGE_INIT@ -include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake) +if (NOT TARGET fmt::fmt) + include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake) +endif () + check_required_components(fmt) diff --git a/externals/dynarmic/externals/fmt/support/printable.py b/externals/dynarmic/externals/fmt/support/printable.py index 7d23d3bb7..8fa86b300 100755 --- a/externals/dynarmic/externals/fmt/support/printable.py +++ b/externals/dynarmic/externals/fmt/support/printable.py @@ -171,7 +171,7 @@ def main(): normal1 = compress_normal(normal1) print("""\ -inline auto is_printable(uint32_t cp) -> bool {\ +FMT_FUNC auto is_printable(uint32_t cp) -> bool {\ """) print_singletons(singletons0u, singletons0l, 'singletons0', 'singletons0_lower') print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower') diff --git a/externals/dynarmic/externals/fmt/test/CMakeLists.txt b/externals/dynarmic/externals/fmt/test/CMakeLists.txt index 7ed8d5f78..5ac196291 100755 --- a/externals/dynarmic/externals/fmt/test/CMakeLists.txt +++ b/externals/dynarmic/externals/fmt/test/CMakeLists.txt @@ -4,9 +4,7 @@ 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 $) -target_link_libraries(test-main gtest) - -include(CheckCXXCompilerFlag) +target_link_libraries(test-main gtest fmt) function(add_fmt_executable name) add_executable(${name} ${ARGN}) @@ -79,6 +77,15 @@ 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) @@ -128,9 +135,6 @@ if (NOT MSVC_STATIC_RUNTIME) if (FMT_PEDANTIC) target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) endif () - if (HAVE_STRTOD_L) - target_compile_definitions(posix-mock-test PRIVATE FMT_LOCALE) - endif () add_test(NAME posix-mock-test COMMAND posix-mock-test) add_fmt_test(os-test) endif () @@ -148,9 +152,7 @@ if (FMT_PEDANTIC) target_include_directories( noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include) target_compile_options(noexception-test PRIVATE -fno-exceptions) - if (FMT_PEDANTIC) - target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) - endif () + target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) endif () # Test that the library compiles without locale. @@ -174,8 +176,8 @@ if (FMT_PEDANTIC AND NOT WIN32) "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}" "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" - "-DFMT_DIR=${CMAKE_SOURCE_DIR}" - "-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}") + "-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} diff --git a/externals/dynarmic/externals/fmt/test/chrono-test.cc b/externals/dynarmic/externals/fmt/test/chrono-test.cc index 42360e5f1..959fb65a3 100755 --- a/externals/dynarmic/externals/fmt/test/chrono-test.cc +++ b/externals/dynarmic/externals/fmt/test/chrono-test.cc @@ -557,6 +557,9 @@ TEST(chrono_test, special_durations) { "03:33"); EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration{2}), "03:33:20"); + EXPECT_EQ("44.000000000000", + fmt::format("{:%S}", std::chrono::duration( + 1.54213895E+26))); } TEST(chrono_test, unsigned_duration) { @@ -620,6 +623,10 @@ TEST(chrono_test, cpp20_duration_subsecond_support) { // 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>(1)), + "00.333333"); + EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration>(1)), + "00.142857"); } #endif // FMT_STATIC_THOUSANDS_SEPARATOR diff --git a/externals/dynarmic/externals/fmt/test/color-test.cc b/externals/dynarmic/externals/fmt/test/color-test.cc index af8f14942..c2ba13a97 100755 --- a/externals/dynarmic/externals/fmt/test/color-test.cc +++ b/externals/dynarmic/externals/fmt/test/color-test.cc @@ -50,6 +50,12 @@ TEST(color_test, format) { "\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) { diff --git a/externals/dynarmic/externals/fmt/test/compile-error-test/CMakeLists.txt b/externals/dynarmic/externals/fmt/test/compile-error-test/CMakeLists.txt index db7a94296..9c65dfdce 100755 --- a/externals/dynarmic/externals/fmt/test/compile-error-test/CMakeLists.txt +++ b/externals/dynarmic/externals/fmt/test/compile-error-test/CMakeLists.txt @@ -6,6 +6,8 @@ project(compile-error-test CXX) set(fmt_headers " #include #include + #include + #include ") set(error_test_names "") @@ -154,6 +156,28 @@ expect_compile(format-function-error " 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) @@ -181,16 +205,30 @@ if (CMAKE_CXX_STANDARD GREATER_EQUAL 20) #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_PARAMETERS + #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_PARAMETERS + #if defined(FMT_HAS_CONSTEVAL) && FMT_USE_NONTYPE_TEMPLATE_ARGS using namespace fmt::literals; fmt::print(\"{foo}\", \"bar\"_a=42); #else diff --git a/externals/dynarmic/externals/fmt/test/compile-fp-test.cc b/externals/dynarmic/externals/fmt/test/compile-fp-test.cc index afedc26d0..db0cd906c 100755 --- a/externals/dynarmic/externals/fmt/test/compile-fp-test.cc +++ b/externals/dynarmic/externals/fmt/test/compile-fp-test.cc @@ -11,7 +11,7 @@ #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 && __cplusplus >= 202002L + __cpp_constexpr_dynamic_alloc >= 201907 && FMT_CPLUSPLUS >= 202002L template struct test_string { template constexpr bool operator==(const T& rhs) const noexcept { return fmt::basic_string_view(rhs).compare(buffer) == 0; diff --git a/externals/dynarmic/externals/fmt/test/compile-test.cc b/externals/dynarmic/externals/fmt/test/compile-test.cc index 1765961b8..2a9e16196 100755 --- a/externals/dynarmic/externals/fmt/test/compile-test.cc +++ b/externals/dynarmic/externals/fmt/test/compile-test.cc @@ -187,7 +187,7 @@ TEST(compile_test, named) { EXPECT_THROW(fmt::format(FMT_COMPILE("{invalid}"), fmt::arg("valid", 42)), fmt::format_error); -# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +# if FMT_USE_NONTYPE_TEMPLATE_ARGS using namespace fmt::literals; auto statically_named_field_compiled = fmt::detail::compile(FMT_COMPILE("{arg}")); @@ -201,6 +201,11 @@ TEST(compile_test, named) { # 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); @@ -280,7 +285,7 @@ TEST(compile_test, print) { } #endif -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS TEST(compile_test, compile_format_string_literal) { using namespace fmt::literals; EXPECT_EQ("", fmt::format(""_cf)); @@ -289,8 +294,15 @@ TEST(compile_test, compile_format_string_literal) { } #endif -#if __cplusplus >= 202002L || \ - (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002) +// 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 struct test_string { template constexpr bool operator==(const T& rhs) const noexcept { return fmt::basic_string_view(rhs).compare(buffer) == 0; diff --git a/externals/dynarmic/externals/fmt/test/core-test.cc b/externals/dynarmic/externals/fmt/test/core-test.cc index b2f2097ea..ce0816c6b 100755 --- a/externals/dynarmic/externals/fmt/test/core-test.cc +++ b/externals/dynarmic/externals/fmt/test/core-test.cc @@ -128,7 +128,7 @@ TEST(core_test, buffer_appender) { #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470 TEST(buffer_test, noncopyable) { EXPECT_FALSE(std::is_copy_constructible>::value); -# if !FMT_MSC_VER +# if !FMT_MSC_VERSION // std::is_copy_assignable is broken in MSVC2013. EXPECT_FALSE(std::is_copy_assignable>::value); # endif @@ -136,7 +136,7 @@ TEST(buffer_test, noncopyable) { TEST(buffer_test, nonmoveable) { EXPECT_FALSE(std::is_move_constructible>::value); -# if !FMT_MSC_VER +# if !FMT_MSC_VERSION // std::is_move_assignable is broken in MSVC2013. EXPECT_FALSE(std::is_move_assignable>::value); # endif @@ -385,11 +385,11 @@ VISIT_TYPE(unsigned long, unsigned long long); template class numeric_arg_test : public testing::Test {}; -using types = +using test_types = testing::Types; -TYPED_TEST_SUITE(numeric_arg_test, types); +TYPED_TEST_SUITE(numeric_arg_test, test_types); template ::value, int> = 0> T test_value() { @@ -679,6 +679,7 @@ TEST(format_test, constexpr_parse_format_string) { #endif // FMT_USE_CONSTEXPR struct enabled_formatter {}; +struct enabled_ptr_formatter {}; struct disabled_formatter {}; struct disabled_formatter_convertible { operator int() const { return 42; } @@ -693,6 +694,16 @@ template <> struct formatter { return ctx.out(); } }; + +template <> struct 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) { @@ -737,7 +748,34 @@ struct convertible_to_pointer { operator const int*() const { return nullptr; } }; -enum class test_scoped_enum {}; +struct convertible_to_pointer_formattable { + operator const int*() const { return nullptr; } +}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter { + 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 @@ -758,6 +796,7 @@ TEST(core_test, is_formattable) { static_assert(!fmt::is_formattable>::value, ""); static_assert(fmt::is_formattable::value, ""); + static_assert(!fmt::is_formattable::value, ""); static_assert(!fmt::is_formattable::value, ""); static_assert(fmt::is_formattable::value, ""); @@ -765,19 +804,22 @@ TEST(core_test, is_formattable) { static_assert(fmt::is_formattable::value, ""); static_assert(fmt::is_formattable::value, ""); -#if !FMT_MSC_VER || FMT_MSC_VER >= 1910 +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 static_assert(!fmt::is_formattable::value, ""); #endif static_assert(!fmt::is_formattable::value, ""); + const auto f = convertible_to_pointer_formattable(); + EXPECT_EQ(fmt::format("{}", f), "test"); static_assert(!fmt::is_formattable::value, ""); struct s; - static_assert(!fmt::is_formattable::value, ""); static_assert(!fmt::is_formattable::value, ""); - static_assert(!fmt::is_formattable::value, ""); + static_assert(!fmt::is_formattable::value, ""); + static_assert(fmt::is_formattable::value, ""); + static_assert(!fmt::is_formattable::value, ""); } TEST(core_test, format) { EXPECT_EQ(fmt::format("{}", 42), "42"); } @@ -788,6 +830,10 @@ TEST(core_test, format_to) { 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; } }; @@ -850,13 +896,16 @@ TEST(core_test, format_implicitly_convertible_to_string_view) { } // std::is_constructible is broken in MSVC until version 2015. -#if !FMT_MSC_VER || FMT_MSC_VER >= 1900 +#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) { - EXPECT_EQ("foo", fmt::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::value, ""); } # ifdef FMT_USE_STRING_VIEW @@ -865,8 +914,11 @@ struct explicitly_convertible_to_std_string_view { }; TEST(core_test, format_explicitly_convertible_to_std_string_view) { - EXPECT_EQ("foo", - fmt::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::value, + ""); } # endif #endif diff --git a/externals/dynarmic/externals/fmt/test/detect-stdfs.cc b/externals/dynarmic/externals/fmt/test/detect-stdfs.cc new file mode 100755 index 000000000..2dc665383 --- /dev/null +++ b/externals/dynarmic/externals/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 // _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/externals/dynarmic/externals/fmt/test/format-impl-test.cc b/externals/dynarmic/externals/fmt/test/format-impl-test.cc index a012306f5..b210e979d 100755 --- a/externals/dynarmic/externals/fmt/test/format-impl-test.cc +++ b/externals/dynarmic/externals/fmt/test/format-impl-test.cc @@ -24,9 +24,9 @@ static_assert(!std::is_copy_constructible::value, ""); static_assert(!std::is_copy_assignable::value, ""); TEST(bigint_test, construct) { - EXPECT_EQ("", fmt::format("{}", bigint())); - EXPECT_EQ("42", fmt::format("{}", bigint(0x42))); - EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0))); + 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) { @@ -72,63 +72,56 @@ TEST(bigint_test, add_compare) { TEST(bigint_test, shift_left) { bigint n(0x42); n <<= 0; - EXPECT_EQ("42", fmt::format("{}", n)); + EXPECT_EQ(fmt::to_string(n), "42"); n <<= 1; - EXPECT_EQ("84", fmt::format("{}", n)); + EXPECT_EQ(fmt::to_string(n), "84"); n <<= 25; - EXPECT_EQ("108000000", fmt::format("{}", n)); + EXPECT_EQ(fmt::to_string(n), "108000000"); } TEST(bigint_test, multiply) { bigint n(0x42); EXPECT_THROW(n *= 0, assertion_failure); n *= 1; - EXPECT_EQ("42", fmt::format("{}", n)); + EXPECT_EQ(fmt::to_string(n), "42"); + n *= 2; - EXPECT_EQ("84", fmt::format("{}", n)); + EXPECT_EQ(fmt::to_string(n), "84"); n *= 0x12345678; - EXPECT_EQ("962fc95e0", fmt::format("{}", n)); + EXPECT_EQ(fmt::to_string(n), "962fc95e0"); + bigint bigmax(max_value()); bigmax *= max_value(); - EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax)); - bigmax.assign(max_value()); - bigmax *= max_value(); - EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax)); -} + EXPECT_EQ(fmt::to_string(bigmax), "fffffffe00000001"); -TEST(bigint_test, accumulator) { - fmt::detail::accumulator acc; - EXPECT_EQ(acc.lower, 0); - EXPECT_EQ(acc.upper, 0); - acc.upper = 12; - acc.lower = 34; - EXPECT_EQ(static_cast(acc), 34); - acc += 56; - EXPECT_EQ(acc.lower, 90); - acc += max_value(); - EXPECT_EQ(acc.upper, 13); - EXPECT_EQ(acc.lower, 89); - acc >>= 32; - EXPECT_EQ(acc.upper, 0); - EXPECT_EQ(acc.lower, 13 * 0x100000000); + const auto max64 = max_value(); + 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("0", fmt::format("{}", n0)); + EXPECT_EQ(fmt::to_string(n0), "0"); bigint n1(0x100); n1.square(); - EXPECT_EQ("10000", fmt::format("{}", n1)); + EXPECT_EQ(fmt::to_string(n1), "10000"); bigint n2(0xfffffffff); n2.square(); - EXPECT_EQ("ffffffffe000000001", fmt::format("{}", n2)); + EXPECT_EQ(fmt::to_string(n2), "ffffffffe000000001"); bigint n3(max_value()); n3.square(); - EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", n3)); + EXPECT_EQ(fmt::to_string(n3), "fffffffffffffffe0000000000000001"); bigint n4; n4.assign_pow10(10); - EXPECT_EQ("2540be400", fmt::format("{}", n4)); + EXPECT_EQ(fmt::to_string(n4), "2540be400"); } TEST(bigint_test, divmod_assign_zero_divisor) { @@ -150,8 +143,8 @@ TEST(bigint_test, divmod_assign_unaligned) { n2.assign_pow10(100); int result = n1.divmod_assign(n2); EXPECT_EQ(result, 9406); - EXPECT_EQ("10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96", - fmt::format("{}", n1)); + EXPECT_EQ(fmt::to_string(n1), + "10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96"); } TEST(bigint_test, divmod_assign) { @@ -159,19 +152,19 @@ TEST(bigint_test, divmod_assign) { bigint n1(100); int result = n1.divmod_assign(bigint(10)); EXPECT_EQ(result, 10); - EXPECT_EQ("0", fmt::format("{}", n1)); + 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("13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96", - fmt::format("{}", n1)); + 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("2a", fmt::format("{}", n2)); + EXPECT_EQ(fmt::to_string(n2), "2a"); } template void run_double_tests() { @@ -190,8 +183,8 @@ TEST(fp_test, double_tests) { TEST(fp_test, normalize) { const auto v = fp(0xbeef, 42); auto normalized = normalize(v); - EXPECT_EQ(0xbeef000000000000, normalized.f); - EXPECT_EQ(-6, normalized.e); + EXPECT_EQ(normalized.f, 0xbeef000000000000); + EXPECT_EQ(normalized.e, -6); } TEST(fp_test, multiply) { @@ -207,18 +200,18 @@ TEST(fp_test, get_cached_power) { using limits = std::numeric_limits; for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) { int dec_exp = 0; - auto fp = fmt::detail::get_cached_power(exp, dec_exp); - bigint exact, cache(fp.f); + 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 (fp.e <= 0) - exact <<= -fp.e; + if (power.e <= 0) + exact <<= -power.e; else - cache <<= fp.e; + cache <<= power.e; exact.align(cache); cache.align(exact); - auto exact_str = fmt::format("{}", exact); - auto cache_str = fmt::format("{}", 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, 15), cache_str.substr(0, 15)); int diff = cache_str[15] - exact_str[15]; @@ -228,12 +221,12 @@ TEST(fp_test, get_cached_power) { EXPECT_EQ(diff, 0); } else { cache.assign_pow10(-dec_exp); - cache *= fp.f + 1; // Inexact check. - exact.assign(1); - exact <<= -fp.e; + cache *= power.f + 1; // Inexact check. + exact = 1; + exact <<= -power.e; exact.align(cache); - auto exact_str = fmt::format("{}", exact); - auto cache_str = fmt::format("{}", 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)); } @@ -243,38 +236,41 @@ TEST(fp_test, get_cached_power) { TEST(fp_test, dragonbox_max_k) { using fmt::detail::dragonbox::floor_log10_pow2; using float_info = fmt::detail::dragonbox::float_info; - EXPECT_EQ(fmt::detail::const_check(float_info::max_k), - float_info::kappa - floor_log10_pow2(float_info::min_exponent - - float_info::significand_bits)); + EXPECT_EQ( + fmt::detail::const_check(float_info::max_k), + float_info::kappa - + floor_log10_pow2(std::numeric_limits::min_exponent - + fmt::detail::num_significand_bits() - 1)); using double_info = fmt::detail::dragonbox::float_info; EXPECT_EQ( fmt::detail::const_check(double_info::max_k), - double_info::kappa - floor_log10_pow2(double_info::min_exponent - - double_info::significand_bits)); + double_info::kappa - + floor_log10_pow2(std::numeric_limits::min_exponent - + fmt::detail::num_significand_bits() - 1)); } TEST(fp_test, get_round_direction) { using fmt::detail::get_round_direction; using fmt::detail::round_direction; - EXPECT_EQ(round_direction::down, get_round_direction(100, 50, 0)); - EXPECT_EQ(round_direction::up, get_round_direction(100, 51, 0)); - EXPECT_EQ(round_direction::down, get_round_direction(100, 40, 10)); - EXPECT_EQ(round_direction::up, get_round_direction(100, 60, 10)); + 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(round_direction::unknown, get_round_direction(100, i, 10)); + EXPECT_EQ(get_round_direction(100, i, 10), round_direction::unknown); uint64_t max = max_value(); 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(round_direction::up, get_round_direction(max, max - 1, 2)); + EXPECT_EQ(get_round_direction(max, max - 1, 2), round_direction::up); // Check that 2 * (remainder + error) doesn't overflow. - EXPECT_EQ(round_direction::unknown, - get_round_direction(max, max / 2 + 1, max / 2)); + EXPECT_EQ(get_round_direction(max, max / 2 + 1, max / 2), + round_direction::unknown); // Check that remainder - error doesn't overflow. - EXPECT_EQ(round_direction::unknown, get_round_direction(100, 40, 41)); + EXPECT_EQ(get_round_direction(100, 40, 41), round_direction::unknown); // Check that 2 * (remainder - error) doesn't overflow. - EXPECT_EQ(round_direction::up, get_round_direction(max, max - 1, 1)); + EXPECT_EQ(get_round_direction(max, max - 1, 1), round_direction::up); } TEST(fp_test, fixed_handler) { @@ -297,20 +293,20 @@ TEST(fp_test, fixed_handler) { } TEST(fp_test, grisu_format_compiles_with_on_ieee_double) { - fmt::memory_buffer buf; + 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 = ": "; { - fmt::memory_buffer buffer; + auto buffer = fmt::memory_buffer(); format_to(fmt::appender(buffer), "garbage"); fmt::detail::format_error_code(buffer, 42, "test"); - EXPECT_EQ("test: " + msg, to_string(buffer)); + EXPECT_EQ(to_string(buffer), "test: " + msg); } { - fmt::memory_buffer buffer; + 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); @@ -331,7 +327,7 @@ TEST(format_impl_test, format_error_code) { // Test with a message that doesn't fit into the buffer. prefix += 'x'; fmt::detail::format_error_code(buffer, codes[i], prefix); - EXPECT_EQ(msg, to_string(buffer)); + EXPECT_EQ(to_string(buffer), msg); } } @@ -347,8 +343,8 @@ template 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() / 10; n <= end; ++i) { n *= 10; - EXPECT_EQ(i, fmt::detail::count_digits(n - 1)); - EXPECT_EQ(i + 1, fmt::detail::count_digits(n)); + EXPECT_EQ(fmt::detail::count_digits(n - 1), i); + EXPECT_EQ(fmt::detail::count_digits(n), i + 1); } } @@ -357,21 +353,51 @@ TEST(format_impl_test, count_digits) { test_count_digits(); } -TEST(format_impl_test, write_fallback_uintptr) { - std::string s; - fmt::detail::write_ptr( - std::back_inserter(s), - fmt::detail::fallback_uintptr(reinterpret_cast(0xface)), nullptr); - EXPECT_EQ(s, "0xface"); +#if FMT_USE_FLOAT128 +TEST(format_impl_test, write_float128) { + auto s = std::string(); + fmt::detail::write(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; +} + +namespace std { +template <> struct is_floating_point : std::true_type {}; +template <> struct numeric_limits { + // is_iec559 is true for double-double in libstdc++. + static constexpr bool is_iec559 = true; + static constexpr int digits = 106; +}; +} // namespace std + +TEST(format_impl_test, write_double_double) { + auto s = std::string(); + fmt::detail::write(std::back_inserter(s), double_double(42), {}); +#ifndef _MSC_VER // MSVC has an issue with specializing is_floating_point. + EXPECT_EQ(s, "42"); +#endif } #ifdef _WIN32 # include -#endif -#ifdef _WIN32 TEST(format_impl_test, write_console_signature) { - decltype(WriteConsoleW)* p = fmt::detail::WriteConsoleW; + decltype(::WriteConsoleW)* p = fmt::detail::WriteConsoleW; (void)p; } #endif diff --git a/externals/dynarmic/externals/fmt/test/format-test.cc b/externals/dynarmic/externals/fmt/test/format-test.cc index a8592ef07..45a92624f 100755 --- a/externals/dynarmic/externals/fmt/test/format-test.cc +++ b/externals/dynarmic/externals/fmt/test/format-test.cc @@ -33,12 +33,98 @@ 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(n), 42); +} + +TEST(uint128_test, shift) { + auto n = uint128_fallback(42); + n = n << 64; + EXPECT_EQ(static_cast(n), 0); + n = n >> 64; + EXPECT_EQ(static_cast(n), 42); + n = n << 62; + EXPECT_EQ(static_cast(n >> 64), 0xa); + EXPECT_EQ(static_cast(n), 0x8000000000000000); + n = n >> 62; + EXPECT_EQ(static_cast(n), 42); +} + +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()); + 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(n >> 64), 440901); +} + +template 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()))); + // Use double because std::numeric_limits is broken for __float128. + using limits = std::numeric_limits; + 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(); +#ifdef __SIZEOF_FLOAT128__ + check_isfinite(); +#endif +} + +template 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()))); + // Use double because std::numeric_limits is broken for __float128. + using limits = std::numeric_limits; + 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(); +#ifdef __SIZEOF_FLOAT128__ + check_isnan(); +#endif +} + struct uint32_pair { uint32_t u[2]; }; @@ -223,8 +309,9 @@ TEST(memory_buffer_test, move_ctor_dynamic_buffer) { buffer.push_back('a'); basic_memory_buffer buffer2(std::move(buffer)); // Move should rip the guts of the first buffer. - EXPECT_EQ(inline_buffer_ptr, &buffer[0]); - EXPECT_EQ("testa", std::string(&buffer2[0], buffer2.size())); + 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); } @@ -325,7 +412,7 @@ template class max_size_allocator : public Allocator { public: using typename Allocator::value_type; - size_t max_size() const FMT_NOEXCEPT { return MaxSize; } + 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"); @@ -570,6 +657,9 @@ TEST(format_test, plus_sign) { 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)); @@ -918,6 +1008,14 @@ TEST(format_test, precision) { EXPECT_THAT(outputs, testing::Contains(fmt::format("{:.838A}", -2.14001164E+38))); + if (std::numeric_limits::digits == 64) { + auto ld = (std::numeric_limits::min)(); + EXPECT_EQ(fmt::format("{:.0}", ld), "3e-4932"); + EXPECT_EQ( + fmt::format("{:0g}", std::numeric_limits::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)); @@ -939,6 +1037,7 @@ TEST(format_test, precision) { 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) { @@ -1060,7 +1159,7 @@ TEST(format_test, format_short) { template void check_unknown_types(const T& value, const char* types, const char*) { char format_str[buffer_size]; - const char* special = ".0123456789L}"; + const char* special = ".0123456789L?}"; for (int i = CHAR_MIN; i <= CHAR_MAX; ++i) { char c = static_cast(i); if (std::strchr(types, c) || std::strchr(special, c) || !c) continue; @@ -1229,32 +1328,30 @@ TEST(format_test, format_float) { } TEST(format_test, format_double) { - EXPECT_EQ("0", fmt::format("{}", 0.0)); + EXPECT_EQ(fmt::format("{}", 0.0), "0"); check_unknown_types(1.2, "eEfFgGaAnL%", "double"); - EXPECT_EQ("0", fmt::format("{:}", 0.0)); - EXPECT_EQ("0.000000", fmt::format("{:f}", 0.0)); - EXPECT_EQ("0", fmt::format("{:g}", 0.0)); - EXPECT_EQ("392.65", fmt::format("{:}", 392.65)); - EXPECT_EQ("392.65", fmt::format("{:g}", 392.65)); - EXPECT_EQ("392.65", fmt::format("{:G}", 392.65)); - EXPECT_EQ("4.9014e+06", fmt::format("{:g}", 4.9014e6)); - EXPECT_EQ("392.650000", fmt::format("{:f}", 392.65)); - EXPECT_EQ("392.650000", fmt::format("{:F}", 392.65)); - EXPECT_EQ("42", fmt::format("{:L}", 42.0)); - EXPECT_EQ(" 0x1.0cccccccccccdp+2", fmt::format("{:24a}", 4.2)); - EXPECT_EQ("0x1.0cccccccccccdp+2 ", fmt::format("{:<24a}", 4.2)); + 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, "%e", 392.65); - EXPECT_EQ(buffer, fmt::format("{0:e}", 392.65)); - safe_sprintf(buffer, "%E", 392.65); - EXPECT_EQ(buffer, fmt::format("{0:E}", 392.65)); - EXPECT_EQ("+0000392.6", fmt::format("{0:+010.4g}", 392.65)); safe_sprintf(buffer, "%a", -42.0); - EXPECT_EQ(buffer, fmt::format("{:a}", -42.0)); + EXPECT_EQ(fmt::format("{:a}", -42.0), buffer); safe_sprintf(buffer, "%A", -42.0); - EXPECT_EQ(buffer, fmt::format("{:A}", -42.0)); - EXPECT_EQ("9223372036854775808.000000", - fmt::format("{:f}", 9223372036854775807.0)); + EXPECT_EQ(fmt::format("{:A}", -42.0), buffer); + EXPECT_EQ(fmt::format("{:f}", 9223372036854775807.0), + "9223372036854775808.000000"); } TEST(format_test, precision_rounding) { @@ -1363,6 +1460,9 @@ TEST(format_test, format_char) { << 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) { @@ -1393,7 +1493,11 @@ TEST(format_test, format_pointer) { EXPECT_EQ("0x0", fmt::format("{0}", static_cast(nullptr))); EXPECT_EQ("0x1234", fmt::format("{0}", reinterpret_cast(0x1234))); EXPECT_EQ("0x1234", fmt::format("{0:p}", reinterpret_cast(0x1234))); - EXPECT_EQ("0x" + std::string(sizeof(void*) * CHAR_BIT / 4, 'f'), + // 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(~uintptr_t()))); EXPECT_EQ("0x1234", fmt::format("{}", fmt::ptr(reinterpret_cast(0x1234)))); @@ -1409,14 +1513,43 @@ TEST(format_test, format_pointer) { 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( + std::back_inserter(s), + fmt::detail::bit_cast( + reinterpret_cast(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("test", fmt::format("{0}", std::string("test"))); + 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())); } @@ -1623,6 +1756,7 @@ TEST(format_test, group_digits_view) { } enum test_enum { foo, bar }; +auto format_as(test_enum e) -> int { return e; } TEST(format_test, join) { using fmt::join; @@ -1652,9 +1786,15 @@ TEST(format_test, join) { } #ifdef __cpp_lib_byte +TEST(format_test, format_byte) { + using arg_mapper = fmt::detail::arg_mapper; + 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(1), std::byte(2), std::byte(3)}; - EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join(v, ", "))); + EXPECT_EQ(fmt::format("{}", fmt::join(v, ", ")), "1, 2, 3"); } #endif @@ -1694,20 +1834,21 @@ fmt::string_view to_string_view(string_like) { return "foo"; } constexpr char with_null[3] = {'{', '}', '\0'}; constexpr char no_null[2] = {'{', '}'}; -static FMT_CONSTEXPR_DECL const char static_with_null[3] = {'{', '}', '\0'}; -static FMT_CONSTEXPR_DECL const char static_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_PARAMETERS +#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; @@ -1719,11 +1860,11 @@ TEST(format_test, compile_time_string) { (void)with_null; (void)no_null; -#if __cplusplus >= 201703L +#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) && __cplusplus >= 201703L +#if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L EXPECT_EQ("42", fmt::format(FMT_STRING(std::string_view("{}")), 42)); #endif } @@ -1739,44 +1880,16 @@ TEST(format_test, custom_format_compile_time_string) { } #if FMT_USE_USER_DEFINED_LITERALS -// Passing user-defined literals directly to EXPECT_EQ causes problems -// with macro argument stringification (#) on some versions of GCC. -// Workaround: Assing the UDL result to a variable before the macro. - -using namespace fmt::literals; - -# if FMT_GCC_VERSION -# define FMT_CHECK_DEPRECATED_UDL_FORMAT 1 -# elif FMT_CLANG_VERSION && defined(__has_warning) -# if __has_warning("-Wdeprecated-declarations") -# define FMT_CHECK_DEPRECATED_UDL_FORMAT 1 -# endif -# endif -# ifndef FMT_CHECK_DEPRECATED_UDL_FORMAT -# define FMT_CHECK_DEPRECATED_UDL_FORMAT 0 -# endif - -# if FMT_CHECK_DEPRECATED_UDL_FORMAT -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" - -TEST(format_test, format_udl) { - EXPECT_EQ("{}c{}"_format("ab", 1), fmt::format("{}c{}", "ab", 1)); - EXPECT_EQ("foo"_format(), "foo"); - EXPECT_EQ("{0:10}"_format(42), " 42"); - EXPECT_EQ("{}"_format(date(2015, 10, 21)), "2015-10-21"); -} - -# pragma GCC diagnostic pop -# endif - 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 @@ -1790,6 +1903,7 @@ TEST(format_test, formatter_not_specialized) { #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)); @@ -1871,9 +1985,11 @@ TEST(format_test, to_string) { EXPECT_EQ(fmt::to_string(reinterpret_cast(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"); - enum foo : unsigned char { zero }; - EXPECT_EQ(fmt::to_string(zero), "0"); +#if FMT_USE_FLOAT128 + EXPECT_EQ(fmt::to_string(__float128(0.5)), "0.5"); +#endif } TEST(format_test, output_iterators) { @@ -2038,7 +2154,7 @@ 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_VER || FMT_MSC_VER >= 1916 +# 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); @@ -2070,7 +2186,8 @@ TEST(format_test, format_string_errors) { # else fmt::print("warning: constexpr is broken in this version of MSVC\n"); # endif -# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +# 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))); @@ -2116,7 +2233,7 @@ TEST(format_test, char_traits_is_not_ambiguous) { using namespace std; auto c = char_traits::char_type(); (void)c; -#if __cplusplus >= 201103L +#if FMT_CPLUSPLUS >= 201103L auto s = std::string(); auto lval = begin(s); (void)lval; diff --git a/externals/dynarmic/externals/fmt/test/fuzzing/one-arg.cc b/externals/dynarmic/externals/fmt/test/fuzzing/one-arg.cc index 90cec7164..af9787f81 100755 --- a/externals/dynarmic/externals/fmt/test/fuzzing/one-arg.cc +++ b/externals/dynarmic/externals/fmt/test/fuzzing/one-arg.cc @@ -30,8 +30,8 @@ void invoke_fmt(const uint8_t* data, size_t size) { #if FMT_FUZZ_FORMAT_TO_STRING std::string message = fmt::format(format_str.get(), *value); #else - fmt::memory_buffer message; - fmt::format_to(message, format_str.get(), *value); + auto buf = fmt::memory_buffer(); + fmt::format_to(std::back_inserter(buf), format_str.get(), *value); #endif } catch (std::exception&) { } diff --git a/externals/dynarmic/externals/fmt/test/fuzzing/two-args.cc b/externals/dynarmic/externals/fmt/test/fuzzing/two-args.cc index 979320c24..931c64656 100755 --- a/externals/dynarmic/externals/fmt/test/fuzzing/two-args.cc +++ b/externals/dynarmic/externals/fmt/test/fuzzing/two-args.cc @@ -27,8 +27,8 @@ void invoke_fmt(const uint8_t* data, size_t size) { #if FMT_FUZZ_FORMAT_TO_STRING std::string message = fmt::format(format_str, item1, item2); #else - fmt::memory_buffer message; - fmt::format_to(message, format_str, item1, item2); + auto buf = fmt::memory_buffer(); + fmt::format_to(std::back_inserter(buf), format_str, item1, item2); #endif } diff --git a/externals/dynarmic/externals/fmt/test/gtest-extra-test.cc b/externals/dynarmic/externals/fmt/test/gtest-extra-test.cc index 0d86206c9..69e42bc68 100755 --- a/externals/dynarmic/externals/fmt/test/gtest-extra-test.cc +++ b/externals/dynarmic/externals/fmt/test/gtest-extra-test.cc @@ -347,7 +347,7 @@ TEST(output_redirect_test, flush_error_in_ctor) { TEST(output_redirect_test, dup_error_in_ctor) { buffered_file f = open_buffered_file(); - int fd = (f.fileno)(); + int fd = (f.descriptor)(); file copy = file::dup(fd); FMT_POSIX(close(fd)); std::unique_ptr redir{nullptr}; diff --git a/externals/dynarmic/externals/fmt/test/gtest-extra.cc b/externals/dynarmic/externals/fmt/test/gtest-extra.cc index 1d48a1736..542e4b5e6 100755 --- a/externals/dynarmic/externals/fmt/test/gtest-extra.cc +++ b/externals/dynarmic/externals/fmt/test/gtest-extra.cc @@ -23,7 +23,7 @@ output_redirect::output_redirect(FILE* f) : file_(f) { write_end.dup2(fd); } -output_redirect::~output_redirect() FMT_NOEXCEPT { +output_redirect::~output_redirect() noexcept { try { restore(); } catch (const std::exception& e) { diff --git a/externals/dynarmic/externals/fmt/test/gtest-extra.h b/externals/dynarmic/externals/fmt/test/gtest-extra.h index df2b1d8c1..ef2a04e7c 100755 --- a/externals/dynarmic/externals/fmt/test/gtest-extra.h +++ b/externals/dynarmic/externals/fmt/test/gtest-extra.h @@ -83,7 +83,7 @@ class output_redirect { public: explicit output_redirect(FILE* file); - ~output_redirect() FMT_NOEXCEPT; + ~output_redirect() noexcept; output_redirect(const output_redirect&) = delete; void operator=(const output_redirect&) = delete; diff --git a/externals/dynarmic/externals/fmt/test/gtest/CMakeLists.txt b/externals/dynarmic/externals/fmt/test/gtest/CMakeLists.txt index 0cc4e1aa3..ed0e59d5f 100755 --- a/externals/dynarmic/externals/fmt/test/gtest/CMakeLists.txt +++ b/externals/dynarmic/externals/fmt/test/gtest/CMakeLists.txt @@ -18,7 +18,7 @@ else () endif () # Workaround GTest bug https://github.com/google/googletest/issues/705. -check_cxx_compiler_flag( +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) diff --git a/externals/dynarmic/externals/fmt/test/module-test.cc b/externals/dynarmic/externals/fmt/test/module-test.cc index 39c839834..62e5fe8ba 100755 --- a/externals/dynarmic/externals/fmt/test/module-test.cc +++ b/externals/dynarmic/externals/fmt/test/module-test.cc @@ -36,7 +36,6 @@ #else # define FMT_USE_FCNTL 0 #endif -#define FMT_NOEXCEPT noexcept #if defined(_WIN32) && !defined(__MINGW32__) # define FMT_POSIX(call) _##call #else @@ -196,13 +195,6 @@ TEST(module_test, wformat_args) { EXPECT_TRUE(args.get(0)); } -TEST(module_test, checked_format_args) { - fmt::basic_format_args args = fmt::make_args_checked("{}", 42); - EXPECT_TRUE(args.get(0)); - fmt::basic_format_args wargs = fmt::make_args_checked(L"{}", 42); - EXPECT_TRUE(wargs.get(0)); -} - TEST(module_test, dynamic_format_args) { fmt::dynamic_format_arg_store dyn_store; dyn_store.push_back(fmt::arg("a42", 42)); diff --git a/externals/dynarmic/externals/fmt/test/os-test.cc b/externals/dynarmic/externals/fmt/test/os-test.cc index 5b5ef76ed..a1745c8c8 100755 --- a/externals/dynarmic/externals/fmt/test/os-test.cc +++ b/externals/dynarmic/externals/fmt/test/os-test.cc @@ -14,10 +14,6 @@ #include "gtest-extra.h" #include "util.h" -#ifdef fileno -# undef fileno -#endif - using fmt::buffered_file; using testing::HasSubstr; using wstring_view = fmt::basic_string_view; @@ -205,7 +201,7 @@ TEST(buffered_file_test, move_assignment) { TEST(buffered_file_test, move_assignment_closes_file) { buffered_file bf = open_buffered_file(); buffered_file bf2 = open_buffered_file(); - int old_fd = bf2.fileno(); + int old_fd = bf2.descriptor(); bf2 = std::move(bf); EXPECT_TRUE(isclosed(old_fd)); } @@ -225,7 +221,7 @@ TEST(buffered_file_test, move_from_temporary_in_assignment) { TEST(buffered_file_test, move_from_temporary_in_assignment_closes_file) { buffered_file f = open_buffered_file(); - int old_fd = f.fileno(); + int old_fd = f.descriptor(); f = open_buffered_file(); EXPECT_TRUE(isclosed(old_fd)); } @@ -234,7 +230,7 @@ TEST(buffered_file_test, close_file_in_dtor) { int fd = 0; { buffered_file f = open_buffered_file(); - fd = f.fileno(); + fd = f.descriptor(); } EXPECT_TRUE(isclosed(fd)); } @@ -249,7 +245,7 @@ TEST(buffered_file_test, close_error_in_dtor) { // 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->fileno())); + FMT_POSIX(close(f->descriptor())); SUPPRESS_ASSERT(f.reset(nullptr)); }, system_error_message(EBADF, "cannot close file") + "\n"); @@ -257,7 +253,7 @@ TEST(buffered_file_test, close_error_in_dtor) { TEST(buffered_file_test, close) { buffered_file f = open_buffered_file(); - int fd = f.fileno(); + int fd = f.descriptor(); f.close(); EXPECT_TRUE(f.get() == nullptr); EXPECT_TRUE(isclosed(fd)); @@ -265,15 +261,15 @@ TEST(buffered_file_test, close) { TEST(buffered_file_test, close_error) { buffered_file f = open_buffered_file(); - FMT_POSIX(close(f.fileno())); + FMT_POSIX(close(f.descriptor())); EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file"); EXPECT_TRUE(f.get() == nullptr); } -TEST(buffered_file_test, fileno) { +TEST(buffered_file_test, descriptor) { auto f = open_buffered_file(); - EXPECT_TRUE(f.fileno() != -1); - file copy = file::dup(f.fileno()); + EXPECT_TRUE(f.descriptor() != -1); + file copy = file::dup(f.descriptor()); EXPECT_READ(copy, file_content); } diff --git a/externals/dynarmic/externals/fmt/test/ostream-test.cc b/externals/dynarmic/externals/fmt/test/ostream-test.cc index f81039e5b..a3a51d532 100755 --- a/externals/dynarmic/externals/fmt/test/ostream-test.cc +++ b/externals/dynarmic/externals/fmt/test/ostream-test.cc @@ -5,6 +5,8 @@ // // For the license information refer to format.h. +#include + #include "fmt/format.h" using fmt::runtime; @@ -30,12 +32,12 @@ template <> struct formatter : formatter { #include "gtest-extra.h" #include "util.h" -std::ostream& operator<<(std::ostream& os, const date& d) { +auto operator<<(std::ostream& os, const date& d) -> std::ostream& { os << d.year() << '-' << d.month() << '-' << d.day(); return os; } -std::wostream& operator<<(std::wostream& os, const date& d) { +auto operator<<(std::wostream& os, const date& d) -> std::wostream& { os << d.year() << L'-' << d.month() << L'-' << d.day(); return os; } @@ -47,11 +49,24 @@ template type_with_comma_op operator<<(T&, const date&); enum streamable_enum {}; -std::ostream& operator<<(std::ostream& os, 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 : ostream_formatter {}; +template <> struct formatter : ostream_formatter {}; +template <> struct formatter : ostream_formatter {}; +template <> struct formatter : ostream_formatter {}; +} // namespace fmt TEST(ostream_test, enum) { EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_enum())); @@ -86,9 +101,6 @@ TEST(ostream_test, format_specs) { EXPECT_EQ("te", fmt::format("{0:.{1}}", test_string("test"), 2)); } -struct empty_test {}; -std::ostream& operator<<(std::ostream& os, empty_test) { return os << ""; } - TEST(ostream_test, empty_custom_output) { EXPECT_EQ("", fmt::format("{}", empty_test())); } @@ -121,7 +133,7 @@ TEST(ostream_test, write_to_ostream_max_size) { struct mock_streambuf : std::streambuf { MOCK_METHOD2(xsputn, std::streamsize(const void* s, std::streamsize n)); - std::streamsize xsputn(const char* s, std::streamsize n) override { + auto xsputn(const char* s, std::streamsize n) -> std::streamsize override { const void* v = s; return xsputn(v, n); } @@ -158,15 +170,16 @@ TEST(ostream_test, join_fallback_formatter) { #if FMT_USE_CONSTEXPR TEST(ostream_test, constexpr_string) { - EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42"))); - EXPECT_EQ("a string", format(FMT_STRING("{0}"), test_string("a 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 Output& operator<<(Output& out, abc) { +template auto operator<<(Output& out, abc) -> Output& { return out << "abc"; } } // namespace fmt_test @@ -174,7 +187,7 @@ template Output& operator<<(Output& out, abc) { template struct test_template {}; template -std::ostream& operator<<(std::ostream& os, test_template) { +auto operator<<(std::ostream& os, test_template) -> std::ostream& { return os << 1; } @@ -184,6 +197,8 @@ template struct formatter> : formatter { return formatter::format(2, ctx); } }; + +template <> struct formatter : ostream_formatter {}; } // namespace fmt TEST(ostream_test, template) { @@ -214,41 +229,6 @@ TEST(ostream_test, disable_builtin_ostream_operators) { EXPECT_EQ("foo", fmt::format("{}", convertible("foo"))); } -struct explicitly_convertible_to_string_like { - template ::value>::type> - explicit operator String() const { - return String("foo", 3u); - } -}; - -std::ostream& operator<<(std::ostream& os, - explicitly_convertible_to_string_like) { - return os << "bar"; -} - -TEST(ostream_test, format_explicitly_convertible_to_string_like) { - EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like())); -} - -#ifdef FMT_USE_STRING_VIEW -struct explicitly_convertible_to_std_string_view { - explicit operator fmt::detail::std_string_view() const { - return {"foo", 3u}; - } -}; - -std::ostream& operator<<(std::ostream& os, - explicitly_convertible_to_std_string_view) { - return os << "bar"; -} - -TEST(ostream_test, format_explicitly_convertible_to_std_string_view) { - EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like())); -} -#endif // FMT_USE_STRING_VIEW - struct streamable_and_convertible_to_bool { operator bool() const { return true; } }; @@ -262,6 +242,21 @@ TEST(ostream_test, format_convertible_to_bool) { 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) { @@ -270,6 +265,10 @@ std::ostream& operator<<(std::ostream& os, copyfmt_test) { return os << "foo"; } +namespace fmt { +template <> struct formatter : ostream_formatter {}; +} // namespace fmt + TEST(ostream_test, copyfmt) { EXPECT_EQ("foo", fmt::format("{}", copyfmt_test())); } @@ -286,11 +285,15 @@ TEST(ostream_test, range) { struct abstract { virtual ~abstract() = default; virtual void f() = 0; - friend std::ostream& operator<<(std::ostream& os, const abstract&) { + friend auto operator<<(std::ostream& os, const abstract&) -> std::ostream& { return os; } }; +namespace fmt { +template <> struct formatter : ostream_formatter {}; +} // namespace fmt + void format_abstract_compiles(const abstract& a) { fmt::format(FMT_COMPILE("{}"), a); } @@ -299,3 +302,21 @@ TEST(ostream_test, is_formattable) { EXPECT_TRUE(fmt::is_formattable()); EXPECT_TRUE(fmt::is_formattable>()); } + +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()); + 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/externals/dynarmic/externals/fmt/test/posix-mock-test.cc b/externals/dynarmic/externals/fmt/test/posix-mock-test.cc index 255e216c9..819a94dfb 100755 --- a/externals/dynarmic/externals/fmt/test/posix-mock-test.cc +++ b/externals/dynarmic/externals/fmt/test/posix-mock-test.cc @@ -6,7 +6,7 @@ // For the license information refer to format.h. // Disable bogus MSVC warnings. -#ifndef _CRT_SECURE_NO_WARNINGS +#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) # define _CRT_SECURE_NO_WARNINGS #endif @@ -438,7 +438,7 @@ TEST(buffered_file_test, fileno_no_retry) { file::pipe(read_end, write_end); buffered_file f = read_end.fdopen("r"); fileno_count = 1; - EXPECT_SYSTEM_ERROR((f.fileno)(), EINTR, "cannot get file descriptor"); + EXPECT_SYSTEM_ERROR((f.descriptor)(), EINTR, "cannot get file descriptor"); EXPECT_EQ(2, fileno_count); fileno_count = 0; } @@ -457,84 +457,3 @@ TEST(scoped_mock, scope) { } EXPECT_EQ(nullptr, test_mock::instance); } - -#ifdef FMT_LOCALE - -using locale_type = fmt::locale::type; - -struct locale_mock { - static locale_mock* instance; - MOCK_METHOD3(newlocale, locale_type(int category_mask, const char* locale, - locale_type base)); - MOCK_METHOD1(freelocale, void(locale_type locale)); -} * locale_mock::instance; - -# ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4273) -# ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Winconsistent-dllimport" -# endif - -_locale_t _create_locale(int category, const char* locale) { - return locale_mock::instance->newlocale(category, locale, 0); -} - -void _free_locale(_locale_t locale) { - locale_mock::instance->freelocale(locale); -} -# ifdef __clang__ -# pragma clang diagnostic pop -# endif -# pragma warning(pop) -# endif - -# if defined(__THROW) && \ - ((FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408) || defined(__e2k__)) -# define FMT_LOCALE_THROW __THROW -# else -# define FMT_LOCALE_THROW -# endif - -# if defined(__APPLE__) || \ - (defined(__FreeBSD__) && __FreeBSD_version < 1200002) -typedef int FreeLocaleResult; -# else -typedef void FreeLocaleResult; -# endif - -FreeLocaleResult freelocale(locale_type locale) FMT_LOCALE_THROW { - locale_mock::instance->freelocale(locale); - return FreeLocaleResult(); -} - -# undef FMT_LOCALE_THROW - -# ifndef _WIN32 -locale_t test::newlocale(int category_mask, const char* locale, locale_t base) { - return locale_mock::instance->newlocale(category_mask, locale, base); -} - -TEST(locale_test, locale_mock) { - scoped_mock mock; - auto locale = reinterpret_cast(11); - EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale)); - FMT_SYSTEM(newlocale(222, "foo", locale)); -} -# endif - -TEST(locale_test, locale) { -# ifndef LC_NUMERIC_MASK - enum { LC_NUMERIC_MASK = LC_NUMERIC }; -# endif - scoped_mock mock; - auto impl = reinterpret_cast(42); - EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr)) - .WillOnce(Return(impl)); - EXPECT_CALL(mock, freelocale(impl)); - fmt::locale loc; - EXPECT_EQ(impl, loc.get()); -} - -#endif // FMT_LOCALE diff --git a/externals/dynarmic/externals/fmt/test/printf-test.cc b/externals/dynarmic/externals/fmt/test/printf-test.cc index 0bb9ccdaf..383ffb83b 100755 --- a/externals/dynarmic/externals/fmt/test/printf-test.cc +++ b/externals/dynarmic/externals/fmt/test/printf-test.cc @@ -11,7 +11,6 @@ #include #include -#include "fmt/ostream.h" #include "fmt/xchar.h" #include "gtest-extra.h" #include "util.h" @@ -505,6 +504,7 @@ TEST(printf_test, pointer) { } enum test_enum { answer = 42 }; +auto format_as(test_enum e) -> int { return e; } TEST(printf_test, enum) { EXPECT_PRINTF("42", "%d", answer); @@ -533,10 +533,6 @@ TEST(printf_test, wide_string) { EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); } -TEST(printf_test, printf_custom) { - EXPECT_EQ("abc", test_sprintf("%s", test_string("abc"))); -} - TEST(printf_test, vprintf) { fmt::format_arg_store as{42}; fmt::basic_format_args args(as); diff --git a/externals/dynarmic/externals/fmt/test/ranges-test.cc b/externals/dynarmic/externals/fmt/test/ranges-test.cc index 63cb8c8bc..0804996ff 100755 --- a/externals/dynarmic/externals/fmt/test/ranges-test.cc +++ b/externals/dynarmic/externals/fmt/test/ranges-test.cc @@ -21,7 +21,7 @@ # define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY #endif -#if !FMT_MSC_VER || FMT_MSC_VER > 1910 +#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1910 # define FMT_RANGES_TEST_ENABLE_JOIN # define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT #endif @@ -46,11 +46,13 @@ TEST(ranges_test, format_array_of_literals) { TEST(ranges_test, format_vector) { auto v = std::vector{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]"); } TEST(ranges_test, format_vector2) { auto v = std::vector>{{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]]"); } TEST(ranges_test, format_map) { @@ -63,16 +65,42 @@ TEST(ranges_test, format_set) { "{\"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(42, 1.5f); EXPECT_EQ(fmt::format("{}", p), "(42, 1.5)"); } +struct unformattable {}; + TEST(ranges_test, format_tuple) { auto t = std::tuple(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>::value)); + EXPECT_FALSE((fmt::is_formattable::value)); + EXPECT_FALSE((fmt::is_formattable>::value)); + EXPECT_FALSE((fmt::is_formattable>::value)); + EXPECT_FALSE((fmt::is_formattable>::value)); + EXPECT_FALSE( + (fmt::is_formattable>::value)); + EXPECT_TRUE((fmt::is_formattable>::value)); } #ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT @@ -131,8 +159,8 @@ TEST(ranges_test, path_like) { struct string_like { const char* begin(); const char* end(); - explicit operator fmt::string_view() const { return "foo"; } - explicit operator std::string_view() const { return "foo"; } + operator fmt::string_view() const { return "foo"; } + operator std::string_view() const { return "foo"; } }; TEST(ranges_test, format_string_like) { @@ -196,14 +224,14 @@ TEST(ranges_test, range) { } enum test_enum { foo }; +auto format_as(test_enum e) -> int { return e; } TEST(ranges_test, enum_range) { auto v = std::vector{test_enum::foo}; EXPECT_EQ(fmt::format("{}", v), "[0]"); } -#if !FMT_MSC_VER -struct unformattable {}; +#if !FMT_MSC_VERSION TEST(ranges_test, unformattable_range) { EXPECT_FALSE((fmt::has_formatter, @@ -296,6 +324,7 @@ static_assert(std::input_iterator); 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"); } @@ -348,6 +377,7 @@ TEST(ranges_test, escape_string) { EXPECT_EQ(fmt::format("{}", vec{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]"); EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xc0"}), "[\"\\xf4\\x8f\\xbf\\xc0\"]"); + EXPECT_EQ(fmt::format("{}", vec{"понедельник"}), "[\"понедельник\"]"); } } @@ -361,3 +391,18 @@ TEST(ranges_test, escape_convertible_to_string_view) { "[\"foo\"]"); } #endif // FMT_USE_STRING_VIEW + +template 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> v = {{1, 2, 3}, {4, 5}}; + EXPECT_EQ(fmt::format("{}", v), "[[1, 2, 3], [4, 5]]"); + + fmt_ref_view r{&v}; + EXPECT_EQ(fmt::format("{}", r), "[[1, 2, 3], [4, 5]]"); +} diff --git a/externals/dynarmic/externals/fmt/test/std-test.cc b/externals/dynarmic/externals/fmt/test/std-test.cc new file mode 100755 index 000000000..fc8f72a0f --- /dev/null +++ b/externals/dynarmic/externals/fmt/test/std-test.cc @@ -0,0 +1,84 @@ +// 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 +#include + +#include "gtest/gtest.h" + +TEST(std_test, path) { +// Test ambiguity problem described in #2954. We need to exclude compilers +// where the ambiguity problem cannot be solved for now. +#if defined(__cpp_lib_filesystem) && \ + (!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920) + 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. We need to exclude compilers +// where the ambiguity problem cannot be solved for now. +#if defined(__cpp_lib_filesystem) && \ + (!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920) + auto p = std::filesystem::path("foo/bar.txt"); + auto c = std::vector{"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; + 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::value)); + EXPECT_FALSE((fmt::is_formattable>::value)); + EXPECT_FALSE((fmt::is_formattable>::value)); + EXPECT_FALSE((fmt::is_formattable>::value)); + EXPECT_FALSE( + (fmt::is_formattable>::value)); + EXPECT_TRUE((fmt::is_formattable>::value)); + + using V1 = std::variant; + 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/externals/dynarmic/externals/fmt/test/test-main.cc b/externals/dynarmic/externals/fmt/test/test-main.cc index 268ed0868..39d2789d3 100755 --- a/externals/dynarmic/externals/fmt/test/test-main.cc +++ b/externals/dynarmic/externals/fmt/test/test-main.cc @@ -15,9 +15,6 @@ #ifdef _MSC_VER # include -#else -# define _CrtSetReportFile(a, b) -# define _CrtSetReportMode(a, b) #endif int main(int argc, char** argv) { @@ -28,11 +25,13 @@ int main(int argc, char** argv) { 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"; diff --git a/externals/dynarmic/externals/fmt/test/xchar-test.cc b/externals/dynarmic/externals/fmt/test/xchar-test.cc index 346cd2126..ea8bc85ab 100755 --- a/externals/dynarmic/externals/fmt/test/xchar-test.cc +++ b/externals/dynarmic/externals/fmt/test/xchar-test.cc @@ -66,14 +66,16 @@ TYPED_TEST(is_string_test, is_string) { } // std::is_constructible is broken in MSVC until version 2015. -#if !FMT_MSC_VER || FMT_MSC_VER >= 1900 +#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) { - EXPECT_EQ(L"foo", - fmt::format(L"{}", 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::value, ""); } #endif @@ -96,12 +98,12 @@ TEST(xchar_test, is_formattable) { } TEST(xchar_test, compile_time_string) { -#if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L +#if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42)); #endif } -#if __cplusplus > 201103L +#if FMT_CPLUSPLUS > 201103L struct custom_char { int value; custom_char() = default; @@ -183,11 +185,6 @@ TEST(format_test, wide_format_to_n) { } #if FMT_USE_USER_DEFINED_LITERALS -TEST(xchar_test, format_udl) { - using namespace fmt::literals; - EXPECT_EQ(L"{}c{}"_format(L"ab", 1), fmt::format(L"{}c{}", L"ab", 1)); -} - TEST(xchar_test, named_arg_udl) { using namespace fmt::literals; auto udl_a = @@ -218,7 +215,14 @@ std::wostream& operator<<(std::wostream& os, streamable_enum) { return os << L"streamable_enum"; } +namespace fmt { +template <> +struct formatter : basic_ostream_formatter { +}; +} // 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())); @@ -232,28 +236,6 @@ TEST(xchar_test, sign_not_truncated) { EXPECT_THROW(fmt::format(format_str, 42), fmt::format_error); } -namespace fake_qt { -class QString { - public: - QString(const wchar_t* s) : s_(s) {} - const wchar_t* utf16() const FMT_NOEXCEPT { return s_.data(); } - int size() const FMT_NOEXCEPT { return static_cast(s_.size()); } - - private: - std::wstring s_; -}; - -fmt::basic_string_view to_string_view(const QString& s) FMT_NOEXCEPT { - return {s.utf16(), static_cast(s.size())}; -} -} // namespace fake_qt - -TEST(format_test, format_foreign_strings) { - using fake_qt::QString; - EXPECT_EQ(fmt::format(QString(L"{}"), 42), L"42"); - EXPECT_EQ(fmt::format(QString(L"{}"), QString(L"42")), L"42"); -} - TEST(xchar_test, chrono) { auto tm = std::tm(); tm.tm_year = 116; @@ -322,9 +304,22 @@ TEST(xchar_test, color) { } 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(L"Don't panic!", wos.str()); + EXPECT_EQ(wos.str(), L"Don't panic!"); +#endif +} + +TEST(xchar_test, format_map) { + auto m = std::map{{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; + 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)); } @@ -360,10 +355,11 @@ template struct small_grouping : std::numpunct { TEST(locale_test, localized_double) { auto loc = std::locale(std::locale(), new numpunct()); - EXPECT_EQ("1?23", fmt::format(loc, "{:L}", 1.23)); - EXPECT_EQ("1?230000", fmt::format(loc, "{:Lf}", 1.23)); - EXPECT_EQ("1~234?5", fmt::format(loc, "{:L}", 1234.5)); - EXPECT_EQ("12~000", fmt::format(loc, "{:L}", 12000.0)); + 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) { diff --git a/externals/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp b/externals/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp index 05da3601a..44932c777 100755 --- a/externals/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp @@ -384,7 +384,7 @@ void A32EmitX64::EmitA32GetCpsr(A32EmitContext& ctx, IR::Inst* inst) { code.pdep(result, result, tmp); } else { code.mov(result, dword[r15 + offsetof(A32JitState, upper_location_descriptor)]); - code.imul(result, result, 0x12); + code.imul(result, result, 0x120); code.and_(result, 0x00000220); code.mov(tmp, dword[r15 + offsetof(A32JitState, cpsr_ge)]); @@ -541,6 +541,48 @@ void A32EmitX64::EmitA32SetCpsrNZCVQ(A32EmitContext& ctx, IR::Inst* inst) { } } +void A32EmitX64::EmitA32SetCpsrNZ(A32EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + const Xbyak::Reg32 nz = ctx.reg_alloc.UseGpr(args[0]).cvt32(); + const Xbyak::Reg32 tmp = ctx.reg_alloc.ScratchGpr().cvt32(); + + code.movzx(tmp, code.byte[r15 + offsetof(A32JitState, cpsr_nzcv) + 1]); + code.and_(tmp, 1); + code.or_(tmp, nz); + code.mov(code.byte[r15 + offsetof(A32JitState, cpsr_nzcv) + 1], tmp.cvt8()); +} + +void A32EmitX64::EmitA32SetCpsrNZC(A32EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + if (args[0].IsImmediate()) { + if (args[1].IsImmediate()) { + const bool c = args[1].GetImmediateU1(); + + code.mov(code.byte[r15 + offsetof(A32JitState, cpsr_nzcv) + 1], c); + } else { + const Xbyak::Reg8 c = ctx.reg_alloc.UseGpr(args[1]).cvt8(); + + code.mov(code.byte[r15 + offsetof(A32JitState, cpsr_nzcv) + 1], c); + } + } else { + const Xbyak::Reg32 nz = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32(); + + if (args[1].IsImmediate()) { + const bool c = args[1].GetImmediateU1(); + + code.or_(nz, c); + code.mov(code.byte[r15 + offsetof(A32JitState, cpsr_nzcv) + 1], nz.cvt8()); + } else { + const Xbyak::Reg32 c = ctx.reg_alloc.UseGpr(args[1]).cvt32(); + + code.or_(nz, c); + code.mov(code.byte[r15 + offsetof(A32JitState, cpsr_nzcv) + 1], nz.cvt8()); + } + } +} + static void EmitGetFlag(BlockOfCode& code, A32EmitContext& ctx, IR::Inst* inst, size_t flag_bit) { const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32(); code.mov(result, dword[r15 + offsetof(A32JitState, cpsr_nzcv)]); @@ -551,48 +593,10 @@ static void EmitGetFlag(BlockOfCode& code, A32EmitContext& ctx, IR::Inst* inst, ctx.reg_alloc.DefineValue(inst, result); } -static void EmitSetFlag(BlockOfCode& code, A32EmitContext& ctx, IR::Inst* inst, size_t flag_bit) { - const u32 flag_mask = 1u << flag_bit; - auto args = ctx.reg_alloc.GetArgumentInfo(inst); - if (args[0].IsImmediate()) { - if (args[0].GetImmediateU1()) { - code.or_(dword[r15 + offsetof(A32JitState, cpsr_nzcv)], flag_mask); - } else { - code.and_(dword[r15 + offsetof(A32JitState, cpsr_nzcv)], ~flag_mask); - } - } else { - const Xbyak::Reg32 to_store = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32(); - - if (flag_bit != 0) { - code.shl(to_store, static_cast(flag_bit)); - code.and_(dword[r15 + offsetof(A32JitState, cpsr_nzcv)], ~flag_mask); - code.or_(dword[r15 + offsetof(A32JitState, cpsr_nzcv)], to_store); - } else { - code.mov(code.byte[r15 + offsetof(A32JitState, cpsr_nzcv)], to_store.cvt8()); - } - } -} - -void A32EmitX64::EmitA32SetNFlag(A32EmitContext& ctx, IR::Inst* inst) { - EmitSetFlag(code, ctx, inst, NZCV::x64_n_flag_bit); -} - -void A32EmitX64::EmitA32SetZFlag(A32EmitContext& ctx, IR::Inst* inst) { - EmitSetFlag(code, ctx, inst, NZCV::x64_z_flag_bit); -} - void A32EmitX64::EmitA32GetCFlag(A32EmitContext& ctx, IR::Inst* inst) { EmitGetFlag(code, ctx, inst, NZCV::x64_c_flag_bit); } -void A32EmitX64::EmitA32SetCFlag(A32EmitContext& ctx, IR::Inst* inst) { - EmitSetFlag(code, ctx, inst, NZCV::x64_c_flag_bit); -} - -void A32EmitX64::EmitA32SetVFlag(A32EmitContext& ctx, IR::Inst* inst) { - EmitSetFlag(code, ctx, inst, NZCV::x64_v_flag_bit); -} - void A32EmitX64::EmitA32OrQFlag(A32EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); if (args[0].IsImmediate()) { diff --git a/externals/dynarmic/src/dynarmic/backend/x64/a32_interface.cpp b/externals/dynarmic/src/dynarmic/backend/x64/a32_interface.cpp index 9fdcaf5f7..5e7fa8e82 100755 --- a/externals/dynarmic/src/dynarmic/backend/x64/a32_interface.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/a32_interface.cpp @@ -174,7 +174,7 @@ private: IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions}); Optimization::PolyfillPass(ir_block, polyfill_options); if (conf.HasOptimization(OptimizationFlag::GetSetElimination) && !conf.check_halt_on_memory_access) { - Optimization::A32GetSetElimination(ir_block); + Optimization::A32GetSetElimination(ir_block, {.convert_nz_to_nzc = true}); Optimization::DeadCodeElimination(ir_block); } if (conf.HasOptimization(OptimizationFlag::ConstProp)) { diff --git a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp index 5ffeefc1d..dd8f2681b 100755 --- a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp @@ -134,6 +134,34 @@ void EmitX64::EmitGetLowerFromOp(EmitContext&, IR::Inst*) { ASSERT_MSG(false, "should never happen"); } +void EmitX64::EmitGetNZFromOp(EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + const int bitsize = [&] { + switch (args[0].GetType()) { + case IR::Type::U8: + return 8; + case IR::Type::U16: + return 16; + case IR::Type::U32: + return 32; + case IR::Type::U64: + return 64; + default: + UNREACHABLE(); + } + }(); + + const Xbyak::Reg64 nz = ctx.reg_alloc.ScratchGpr(HostLoc::RAX); + const Xbyak::Reg value = ctx.reg_alloc.UseGpr(args[0]).changeBit(bitsize); + code.cmp(value, 0); + code.lahf(); + code.db(0x0f); + code.db(0xb6); + code.db(0xc4); + ctx.reg_alloc.DefineValue(inst, nz); +} + void EmitX64::EmitGetNZCVFromOp(EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); @@ -160,6 +188,22 @@ void EmitX64::EmitGetNZCVFromOp(EmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.DefineValue(inst, nzcv); } +void EmitX64::EmitGetCFlagFromNZCV(EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + if (args[0].IsImmediate()) { + const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32(); + const u32 value = (args[0].GetImmediateU32() >> 8) & 1; + code.mov(result, value); + ctx.reg_alloc.DefineValue(inst, result); + } else { + const Xbyak::Reg32 result = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32(); + code.shr(result, 8); + code.and_(result, 1); + ctx.reg_alloc.DefineValue(inst, result); + } +} + void EmitX64::EmitNZCVFromPackedFlags(EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); diff --git a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector.cpp b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector.cpp index bf485cc9c..69fb3cf9a 100755 --- a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector.cpp @@ -3829,29 +3829,29 @@ void EmitX64::EmitVectorSignedSaturatedDoublingMultiply16(EmitContext& ctx, IR:: ctx.EraseInstruction(lower_inst); } + const Xbyak::Xmm upper_result = ctx.reg_alloc.ScratchXmm(); + + if (code.HasHostFeature(HostFeature::AVX)) { + code.vpsrlw(lower_tmp, lower_tmp, 15); + code.vpaddw(upper_tmp, upper_tmp, upper_tmp); + code.vpor(upper_result, upper_tmp, lower_tmp); + code.vpcmpeqw(upper_tmp, upper_result, code.XmmBConst<16>(xword, 0x8000)); + code.vpxor(upper_result, upper_result, upper_tmp); + } else { + code.paddw(upper_tmp, upper_tmp); + code.psrlw(lower_tmp, 15); + code.movdqa(upper_result, upper_tmp); + code.por(upper_result, lower_tmp); + code.movdqa(upper_tmp, code.XmmBConst<16>(xword, 0x8000)); + code.pcmpeqw(upper_tmp, upper_result); + code.pxor(upper_result, upper_tmp); + } + + const Xbyak::Reg32 bit = ctx.reg_alloc.ScratchGpr().cvt32(); + code.pmovmskb(bit, upper_tmp); + code.or_(code.dword[code.r15 + code.GetJitStateInfo().offsetof_fpsr_qc], bit); + if (upper_inst) { - const Xbyak::Xmm upper_result = ctx.reg_alloc.ScratchXmm(); - - if (code.HasHostFeature(HostFeature::AVX)) { - code.vpsrlw(lower_tmp, lower_tmp, 15); - code.vpaddw(upper_tmp, upper_tmp, upper_tmp); - code.vpor(upper_result, upper_tmp, lower_tmp); - code.vpcmpeqw(upper_tmp, upper_result, code.XmmBConst<16>(xword, 0x8000)); - code.vpxor(upper_result, upper_result, upper_tmp); - } else { - code.paddw(upper_tmp, upper_tmp); - code.psrlw(lower_tmp, 15); - code.movdqa(upper_result, upper_tmp); - code.por(upper_result, lower_tmp); - code.movdqa(upper_tmp, code.XmmBConst<16>(xword, 0x8000)); - code.pcmpeqw(upper_tmp, upper_result); - code.pxor(upper_result, upper_tmp); - } - - const Xbyak::Reg32 bit = ctx.reg_alloc.ScratchGpr().cvt32(); - code.pmovmskb(bit, upper_tmp); - code.or_(code.dword[code.r15 + code.GetJitStateInfo().offsetof_fpsr_qc], bit); - ctx.reg_alloc.DefineValue(upper_inst, upper_result); ctx.EraseInstruction(upper_inst); } @@ -3880,23 +3880,23 @@ void EmitX64::EmitVectorSignedSaturatedDoublingMultiply32(EmitContext& ctx, IR:: code.vpaddq(odds, odds, odds); code.vpaddq(even, even, even); + const Xbyak::Xmm upper_result = ctx.reg_alloc.ScratchXmm(); + + code.vpsrlq(upper_result, odds, 32); + code.vblendps(upper_result, upper_result, even, 0b1010); + + const Xbyak::Xmm mask = ctx.reg_alloc.ScratchXmm(); + const Xbyak::Reg32 bit = ctx.reg_alloc.ScratchGpr().cvt32(); + + code.vpcmpeqd(mask, upper_result, code.XmmBConst<32>(xword, 0x80000000)); + code.vpxor(upper_result, upper_result, mask); + code.pmovmskb(bit, mask); + code.or_(code.dword[code.r15 + code.GetJitStateInfo().offsetof_fpsr_qc], bit); + + ctx.reg_alloc.Release(mask); + ctx.reg_alloc.Release(bit); + if (upper_inst) { - const Xbyak::Xmm upper_result = ctx.reg_alloc.ScratchXmm(); - - code.vpsrlq(upper_result, odds, 32); - code.vblendps(upper_result, upper_result, even, 0b1010); - - const Xbyak::Xmm mask = ctx.reg_alloc.ScratchXmm(); - const Xbyak::Reg32 bit = ctx.reg_alloc.ScratchGpr().cvt32(); - - code.vpcmpeqd(mask, upper_result, code.XmmBConst<32>(xword, 0x80000000)); - code.vpxor(upper_result, upper_result, mask); - code.pmovmskb(bit, mask); - code.or_(code.dword[code.r15 + code.GetJitStateInfo().offsetof_fpsr_qc], bit); - - ctx.reg_alloc.Release(mask); - ctx.reg_alloc.Release(bit); - ctx.reg_alloc.DefineValue(upper_inst, upper_result); ctx.EraseInstruction(upper_inst); } @@ -3955,15 +3955,15 @@ void EmitX64::EmitVectorSignedSaturatedDoublingMultiply32(EmitContext& ctx, IR:: code.por(lower_result, x); code.psubd(upper_result, sign_correction); + const Xbyak::Reg32 bit = ctx.reg_alloc.ScratchGpr().cvt32(); + + code.movdqa(tmp, code.XmmBConst<32>(xword, 0x80000000)); + code.pcmpeqd(tmp, upper_result); + code.pxor(upper_result, tmp); + code.pmovmskb(bit, tmp); + code.or_(code.dword[code.r15 + code.GetJitStateInfo().offsetof_fpsr_qc], bit); + if (upper_inst) { - const Xbyak::Reg32 bit = ctx.reg_alloc.ScratchGpr().cvt32(); - - code.movdqa(tmp, code.XmmBConst<32>(xword, 0x80000000)); - code.pcmpeqd(tmp, upper_result); - code.pxor(upper_result, tmp); - code.pmovmskb(bit, tmp); - code.or_(code.dword[code.r15 + code.GetJitStateInfo().offsetof_fpsr_qc], bit); - ctx.reg_alloc.DefineValue(upper_inst, upper_result); ctx.EraseInstruction(upper_inst); } diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.cpp index 7f686b327..343e521ab 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.cpp @@ -166,22 +166,6 @@ IR::U1 IREmitter::GetCFlag() { return Inst(Opcode::A32GetCFlag); } -void IREmitter::SetNFlag(const IR::U1& value) { - Inst(Opcode::A32SetNFlag, value); -} - -void IREmitter::SetZFlag(const IR::U1& value) { - Inst(Opcode::A32SetZFlag, value); -} - -void IREmitter::SetCFlag(const IR::U1& value) { - Inst(Opcode::A32SetCFlag, value); -} - -void IREmitter::SetVFlag(const IR::U1& value) { - Inst(Opcode::A32SetVFlag, value); -} - void IREmitter::OrQFlag(const IR::U1& value) { Inst(Opcode::A32OrQFlag, value); } @@ -198,6 +182,18 @@ void IREmitter::SetGEFlagsCompressed(const IR::U32& value) { Inst(Opcode::A32SetGEFlagsCompressed, value); } +IR::NZCV IREmitter::NZFrom(const IR::Value& value) { + return Inst(Opcode::GetNZFromOp, value); +} + +void IREmitter::SetCpsrNZ(const IR::NZCV& nz) { + Inst(Opcode::A32SetCpsrNZ, nz); +} + +void IREmitter::SetCpsrNZC(const IR::NZCV& nz, const IR::U1& c) { + Inst(Opcode::A32SetCpsrNZC, nz, c); +} + void IREmitter::DataSynchronizationBarrier() { Inst(Opcode::A32DataSynchronizationBarrier); } diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.h b/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.h index 96d162b00..9fde4f877 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.h +++ b/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.h @@ -62,15 +62,15 @@ public: void SetCheckBit(const IR::U1& value); IR::U1 GetOverflowFrom(const IR::Value& value); IR::U1 GetCFlag(); - void SetNFlag(const IR::U1& value); - void SetZFlag(const IR::U1& value); - void SetCFlag(const IR::U1& value); - void SetVFlag(const IR::U1& value); void OrQFlag(const IR::U1& value); IR::U32 GetGEFlags(); void SetGEFlags(const IR::U32& value); void SetGEFlagsCompressed(const IR::U32& value); + IR::NZCV NZFrom(const IR::Value& value); + void SetCpsrNZ(const IR::NZCV& nz); + void SetCpsrNZC(const IR::NZCV& nz, const IR::U1& c); + void DataSynchronizationBarrier(); void DataMemoryBarrier(); void InstructionSynchronizationBarrier(); diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/a32_location_descriptor.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/a32_location_descriptor.cpp index 62a94113f..ac3a374e0 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A32/a32_location_descriptor.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/a32_location_descriptor.cpp @@ -5,20 +5,17 @@ #include "dynarmic/frontend/A32/a32_location_descriptor.h" -#include - #include namespace Dynarmic::A32 { -std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor) { - o << fmt::format("{{{:08x},{},{},{:08x}{}}}", - descriptor.PC(), - descriptor.TFlag() ? "T" : "!T", - descriptor.EFlag() ? "E" : "!E", - descriptor.FPSCR().Value(), - descriptor.SingleStepping() ? ",step" : ""); - return o; +std::string ToString(const LocationDescriptor& descriptor) { + return fmt::format("{{{:08x},{},{},{:08x}{}}}", + descriptor.PC(), + descriptor.TFlag() ? "T" : "!T", + descriptor.EFlag() ? "E" : "!E", + descriptor.FPSCR().Value(), + descriptor.SingleStepping() ? ",step" : ""); } } // namespace Dynarmic::A32 diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/a32_location_descriptor.h b/externals/dynarmic/src/dynarmic/frontend/A32/a32_location_descriptor.h index 3bdb72c9a..c53e75d4b 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A32/a32_location_descriptor.h +++ b/externals/dynarmic/src/dynarmic/frontend/A32/a32_location_descriptor.h @@ -6,9 +6,10 @@ #pragma once #include -#include +#include #include +#include #include #include "dynarmic/frontend/A32/FPSCR.h" @@ -131,10 +132,9 @@ private: /** * Provides a string representation of a LocationDescriptor. * - * @param o Output stream * @param descriptor The descriptor to get a string representation of */ -std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor); +std::string ToString(const LocationDescriptor& descriptor); } // namespace Dynarmic::A32 @@ -152,3 +152,11 @@ struct hash { } }; } // namespace std + +template<> +struct fmt::formatter : fmt::formatter { + template + auto format(Dynarmic::A32::LocationDescriptor descriptor, FormatContext& ctx) const { + return formatter::format(Dynarmic::A32::ToString(descriptor), ctx); + } +}; diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/a32_types.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/a32_types.cpp index c922f57b3..a47ce0b78 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A32/a32_types.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/a32_types.cpp @@ -57,24 +57,4 @@ std::string RegListToString(RegList reg_list) { return ret; } -std::ostream& operator<<(std::ostream& o, Reg reg) { - o << RegToString(reg); - return o; -} - -std::ostream& operator<<(std::ostream& o, ExtReg reg) { - o << ExtRegToString(reg); - return o; -} - -std::ostream& operator<<(std::ostream& o, CoprocReg reg) { - o << CoprocRegToString(reg); - return o; -} - -std::ostream& operator<<(std::ostream& o, RegList reg_list) { - o << RegListToString(reg_list); - return o; -} - } // namespace Dynarmic::A32 diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/a32_types.h b/externals/dynarmic/src/dynarmic/frontend/A32/a32_types.h index 5d1166f3a..6f56bea51 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A32/a32_types.h +++ b/externals/dynarmic/src/dynarmic/frontend/A32/a32_types.h @@ -5,10 +5,10 @@ #pragma once -#include #include #include +#include #include #include @@ -72,11 +72,6 @@ const char* ExtRegToString(ExtReg reg); const char* CoprocRegToString(CoprocReg reg); std::string RegListToString(RegList reg_list); -std::ostream& operator<<(std::ostream& o, Reg reg); -std::ostream& operator<<(std::ostream& o, ExtReg reg); -std::ostream& operator<<(std::ostream& o, CoprocReg reg); -std::ostream& operator<<(std::ostream& o, RegList reg_list); - constexpr bool IsSingleExtReg(ExtReg reg) { return reg >= ExtReg::S0 && reg <= ExtReg::S31; } @@ -148,3 +143,35 @@ inline ExtReg ToVector(bool Q, size_t base, bool bit) { } } // namespace Dynarmic::A32 + +template<> +struct fmt::formatter : fmt::formatter { + template + auto format(Dynarmic::A32::Reg reg, FormatContext& ctx) const { + return formatter::format(Dynarmic::A32::RegToString(reg), ctx); + } +}; + +template<> +struct fmt::formatter : fmt::formatter { + template + auto format(Dynarmic::A32::ExtReg reg, FormatContext& ctx) const { + return formatter::format(Dynarmic::A32::ExtRegToString(reg), ctx); + } +}; + +template<> +struct fmt::formatter : fmt::formatter { + template + auto format(Dynarmic::A32::CoprocReg reg, FormatContext& ctx) const { + return formatter::format(Dynarmic::A32::CoprocRegToString(reg), ctx); + } +}; + +template<> +struct fmt::formatter : fmt::formatter { + template + auto format(Dynarmic::A32::RegList reg_list, FormatContext& ctx) const { + return formatter::format(Dynarmic::A32::RegListToString(reg_list), ctx); + } +}; diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/data_processing.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/data_processing.cpp index 69d0a9465..9d7ea6a17 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/data_processing.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/data_processing.cpp @@ -181,9 +181,7 @@ bool TranslatorVisitor::arm_AND_imm(Cond cond, bool S, Reg n, Reg d, int rotate, ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); } return true; @@ -210,9 +208,7 @@ bool TranslatorVisitor::arm_AND_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5 } ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; @@ -235,9 +231,7 @@ bool TranslatorVisitor::arm_AND_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, Shif ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; @@ -264,9 +258,7 @@ bool TranslatorVisitor::arm_BIC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); } return true; @@ -294,9 +286,7 @@ bool TranslatorVisitor::arm_BIC_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5 ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; @@ -319,9 +309,7 @@ bool TranslatorVisitor::arm_BIC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, Shif ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; @@ -438,9 +426,7 @@ bool TranslatorVisitor::arm_EOR_imm(Cond cond, bool S, Reg n, Reg d, int rotate, ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); } return true; @@ -468,9 +454,7 @@ bool TranslatorVisitor::arm_EOR_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5 ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; @@ -493,9 +477,7 @@ bool TranslatorVisitor::arm_EOR_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, Shif ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; @@ -522,9 +504,7 @@ bool TranslatorVisitor::arm_MOV_imm(Cond cond, bool S, Reg d, int rotate, Imm<8> ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); } return true; @@ -552,9 +532,7 @@ bool TranslatorVisitor::arm_MOV_reg(Cond cond, bool S, Reg d, Imm<5> imm5, Shift ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; @@ -575,9 +553,7 @@ bool TranslatorVisitor::arm_MOV_rsr(Cond cond, bool S, Reg d, Reg s, ShiftType s const auto result = shifted.result; ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; @@ -604,9 +580,7 @@ bool TranslatorVisitor::arm_MVN_imm(Cond cond, bool S, Reg d, int rotate, Imm<8> ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); } return true; @@ -634,9 +608,7 @@ bool TranslatorVisitor::arm_MVN_reg(Cond cond, bool S, Reg d, Imm<5> imm5, Shift ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; @@ -659,9 +631,7 @@ bool TranslatorVisitor::arm_MVN_rsr(Cond cond, bool S, Reg d, Reg s, ShiftType s ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; @@ -688,9 +658,7 @@ bool TranslatorVisitor::arm_ORR_imm(Cond cond, bool S, Reg n, Reg d, int rotate, ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); } return true; @@ -718,9 +686,7 @@ bool TranslatorVisitor::arm_ORR_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5 ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; @@ -743,9 +709,7 @@ bool TranslatorVisitor::arm_ORR_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, Shif ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; @@ -1066,9 +1030,7 @@ bool TranslatorVisitor::arm_TEQ_imm(Cond cond, Reg n, int rotate, Imm<8> imm8) { const auto imm_carry = ArmExpandImm_C(rotate, imm8, ir.GetCFlag()); const auto result = ir.Eor(ir.GetRegister(n), ir.Imm32(imm_carry.imm32)); - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); return true; } @@ -1082,9 +1044,7 @@ bool TranslatorVisitor::arm_TEQ_reg(Cond cond, Reg n, Imm<5> imm5, ShiftType shi const auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, carry_in); const auto result = ir.Eor(ir.GetRegister(n), shifted.result); - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); return true; } @@ -1103,9 +1063,7 @@ bool TranslatorVisitor::arm_TEQ_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Re const auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in); const auto result = ir.Eor(ir.GetRegister(n), shifted.result); - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); return true; } @@ -1118,9 +1076,7 @@ bool TranslatorVisitor::arm_TST_imm(Cond cond, Reg n, int rotate, Imm<8> imm8) { const auto imm_carry = ArmExpandImm_C(rotate, imm8, ir.GetCFlag()); const auto result = ir.And(ir.GetRegister(n), ir.Imm32(imm_carry.imm32)); - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); return true; } @@ -1134,9 +1090,7 @@ bool TranslatorVisitor::arm_TST_reg(Cond cond, Reg n, Imm<5> imm5, ShiftType shi const auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, carry_in); const auto result = ir.And(ir.GetRegister(n), shifted.result); - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); return true; } @@ -1155,9 +1109,7 @@ bool TranslatorVisitor::arm_TST_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Re const auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in); const auto result = ir.And(ir.GetRegister(n), shifted.result); - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); return true; } diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/multiply.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/multiply.cpp index adcf6ddb3..2bda56a96 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/multiply.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/multiply.cpp @@ -20,8 +20,7 @@ bool TranslatorVisitor::arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n) { const auto result = ir.Add(ir.Mul(ir.GetRegister(n), ir.GetRegister(m)), ir.GetRegister(a)); ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); + ir.SetCpsrNZ(ir.NZFrom(result)); } return true; @@ -59,8 +58,7 @@ bool TranslatorVisitor::arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n) { const auto result = ir.Mul(ir.GetRegister(n), ir.GetRegister(m)); ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); + ir.SetCpsrNZ(ir.NZFrom(result)); } return true; @@ -91,8 +89,7 @@ bool TranslatorVisitor::arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Re ir.SetRegister(dLo, lo); ir.SetRegister(dHi, hi); if (S) { - ir.SetNFlag(ir.MostSignificantBit(hi)); - ir.SetZFlag(ir.IsZero(result)); + ir.SetCpsrNZ(ir.NZFrom(result)); } return true; @@ -121,8 +118,7 @@ bool TranslatorVisitor::arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Re ir.SetRegister(dLo, lo); ir.SetRegister(dHi, hi); if (S) { - ir.SetNFlag(ir.MostSignificantBit(hi)); - ir.SetZFlag(ir.IsZero(result)); + ir.SetCpsrNZ(ir.NZFrom(result)); } return true; @@ -177,8 +173,7 @@ bool TranslatorVisitor::arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Re ir.SetRegister(dLo, lo); ir.SetRegister(dHi, hi); if (S) { - ir.SetNFlag(ir.MostSignificantBit(hi)); - ir.SetZFlag(ir.IsZero(result)); + ir.SetCpsrNZ(ir.NZFrom(result)); } return true; @@ -207,8 +202,7 @@ bool TranslatorVisitor::arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Re ir.SetRegister(dLo, lo); ir.SetRegister(dHi, hi); if (S) { - ir.SetNFlag(ir.MostSignificantBit(hi)); - ir.SetZFlag(ir.IsZero(result)); + ir.SetCpsrNZ(ir.NZFrom(result)); } return true; diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb16.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb16.cpp index 0c531e9f2..67a94a0e8 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb16.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb16.cpp @@ -22,9 +22,7 @@ bool TranslatorVisitor::thumb16_LSL_imm(Imm<5> imm5, Reg m, Reg d) { ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { - ir.SetNFlag(ir.MostSignificantBit(result.result)); - ir.SetZFlag(ir.IsZero(result.result)); - ir.SetCFlag(result.carry); + ir.SetCpsrNZC(ir.NZFrom(result.result), result.carry); } return true; } @@ -37,9 +35,7 @@ bool TranslatorVisitor::thumb16_LSR_imm(Imm<5> imm5, Reg m, Reg d) { ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { - ir.SetNFlag(ir.MostSignificantBit(result.result)); - ir.SetZFlag(ir.IsZero(result.result)); - ir.SetCFlag(result.carry); + ir.SetCpsrNZC(ir.NZFrom(result.result), result.carry); } return true; } @@ -52,9 +48,7 @@ bool TranslatorVisitor::thumb16_ASR_imm(Imm<5> imm5, Reg m, Reg d) { ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { - ir.SetNFlag(ir.MostSignificantBit(result.result)); - ir.SetZFlag(ir.IsZero(result.result)); - ir.SetCFlag(result.carry); + ir.SetCpsrNZC(ir.NZFrom(result.result), result.carry); } return true; } @@ -117,8 +111,7 @@ bool TranslatorVisitor::thumb16_MOV_imm(Reg d, Imm<8> imm8) { ir.SetRegister(d, result); if (!ir.current_location.IT().IsInITBlock()) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); + ir.SetCpsrNZ(ir.NZFrom(result)); } return true; } @@ -171,8 +164,7 @@ bool TranslatorVisitor::thumb16_AND_reg(Reg m, Reg d_n) { ir.SetRegister(d, result); if (!ir.current_location.IT().IsInITBlock()) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); + ir.SetCpsrNZ(ir.NZFrom(result)); } return true; } @@ -186,8 +178,7 @@ bool TranslatorVisitor::thumb16_EOR_reg(Reg m, Reg d_n) { ir.SetRegister(d, result); if (!ir.current_location.IT().IsInITBlock()) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); + ir.SetCpsrNZ(ir.NZFrom(result)); } return true; } @@ -202,9 +193,7 @@ bool TranslatorVisitor::thumb16_LSL_reg(Reg m, Reg d_n) { ir.SetRegister(d, result_carry.result); if (!ir.current_location.IT().IsInITBlock()) { - ir.SetNFlag(ir.MostSignificantBit(result_carry.result)); - ir.SetZFlag(ir.IsZero(result_carry.result)); - ir.SetCFlag(result_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result_carry.result), result_carry.carry); } return true; } @@ -219,9 +208,7 @@ bool TranslatorVisitor::thumb16_LSR_reg(Reg m, Reg d_n) { ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { - ir.SetNFlag(ir.MostSignificantBit(result.result)); - ir.SetZFlag(ir.IsZero(result.result)); - ir.SetCFlag(result.carry); + ir.SetCpsrNZC(ir.NZFrom(result.result), result.carry); } return true; } @@ -236,9 +223,7 @@ bool TranslatorVisitor::thumb16_ASR_reg(Reg m, Reg d_n) { ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { - ir.SetNFlag(ir.MostSignificantBit(result.result)); - ir.SetZFlag(ir.IsZero(result.result)); - ir.SetCFlag(result.carry); + ir.SetCpsrNZC(ir.NZFrom(result.result), result.carry); } return true; } @@ -283,9 +268,7 @@ bool TranslatorVisitor::thumb16_ROR_reg(Reg m, Reg d_n) { ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { - ir.SetNFlag(ir.MostSignificantBit(result.result)); - ir.SetZFlag(ir.IsZero(result.result)); - ir.SetCFlag(result.carry); + ir.SetCpsrNZC(ir.NZFrom(result.result), result.carry); } return true; } @@ -293,8 +276,7 @@ bool TranslatorVisitor::thumb16_ROR_reg(Reg m, Reg d_n) { // TST , bool TranslatorVisitor::thumb16_TST_reg(Reg m, Reg n) { const auto result = ir.And(ir.GetRegister(n), ir.GetRegister(m)); - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); + ir.SetCpsrNZ(ir.NZFrom(result)); return true; } @@ -332,8 +314,7 @@ bool TranslatorVisitor::thumb16_ORR_reg(Reg m, Reg d_n) { ir.SetRegister(d, result); if (!ir.current_location.IT().IsInITBlock()) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); + ir.SetCpsrNZ(ir.NZFrom(result)); } return true; } @@ -347,8 +328,7 @@ bool TranslatorVisitor::thumb16_MUL_reg(Reg n, Reg d_m) { ir.SetRegister(d, result); if (!ir.current_location.IT().IsInITBlock()) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); + ir.SetCpsrNZ(ir.NZFrom(result)); } return true; } @@ -362,8 +342,7 @@ bool TranslatorVisitor::thumb16_BIC_reg(Reg m, Reg d_n) { ir.SetRegister(d, result); if (!ir.current_location.IT().IsInITBlock()) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); + ir.SetCpsrNZ(ir.NZFrom(result)); } return true; } @@ -375,8 +354,7 @@ bool TranslatorVisitor::thumb16_MVN_reg(Reg m, Reg d) { ir.SetRegister(d, result); if (!ir.current_location.IT().IsInITBlock()) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); + ir.SetCpsrNZ(ir.NZFrom(result)); } return true; } diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_modified_immediate.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_modified_immediate.cpp index f2cb202b8..0c0114f84 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_modified_immediate.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_modified_immediate.cpp @@ -15,9 +15,7 @@ bool TranslatorVisitor::thumb32_TST_imm(Imm<1> i, Reg n, Imm<3> imm3, Imm<8> imm const auto imm_carry = ThumbExpandImm_C(i, imm3, imm8, ir.GetCFlag()); const auto result = ir.And(ir.GetRegister(n), ir.Imm32(imm_carry.imm32)); - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); return true; } @@ -32,9 +30,7 @@ bool TranslatorVisitor::thumb32_AND_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Re ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); } return true; } @@ -49,9 +45,7 @@ bool TranslatorVisitor::thumb32_BIC_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Re ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); } return true; } @@ -66,9 +60,7 @@ bool TranslatorVisitor::thumb32_MOV_imm(Imm<1> i, bool S, Imm<3> imm3, Reg d, Im ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); } return true; } @@ -84,9 +76,7 @@ bool TranslatorVisitor::thumb32_ORR_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Re ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); } return true; } @@ -101,9 +91,7 @@ bool TranslatorVisitor::thumb32_MVN_imm(Imm<1> i, bool S, Imm<3> imm3, Reg d, Im ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); } return true; } @@ -119,9 +107,7 @@ bool TranslatorVisitor::thumb32_ORN_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Re ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); } return true; } @@ -134,9 +120,7 @@ bool TranslatorVisitor::thumb32_TEQ_imm(Imm<1> i, Reg n, Imm<3> imm3, Imm<8> imm const auto imm_carry = ThumbExpandImm_C(i, imm3, imm8, ir.GetCFlag()); const auto result = ir.Eor(ir.GetRegister(n), ir.Imm32(imm_carry.imm32)); - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); return true; } @@ -151,9 +135,7 @@ bool TranslatorVisitor::thumb32_EOR_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Re ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(imm_carry.carry); + ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry); } return true; } diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_register.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_register.cpp index 04f5803cd..1dda532d9 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_register.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_register.cpp @@ -24,9 +24,7 @@ bool ShiftInstruction(TranslatorVisitor& v, Reg m, Reg d, Reg s, bool S, ShiftFu const auto result_carry = (v.ir.*shift_fn)(v.ir.GetRegister(m), shift_s, apsr_c); if (S) { - v.ir.SetNFlag(v.ir.MostSignificantBit(result_carry.result)); - v.ir.SetZFlag(v.ir.IsZero(result_carry.result)); - v.ir.SetCFlag(result_carry.carry); + v.ir.SetCpsrNZC(v.ir.NZFrom(result_carry.result), result_carry.carry); } v.ir.SetRegister(d, result_carry.result); diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_shifted_register.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_shifted_register.cpp index ee62bb52c..305835004 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_shifted_register.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_shifted_register.cpp @@ -15,9 +15,7 @@ bool TranslatorVisitor::thumb32_TST_reg(Reg n, Imm<3> imm3, Imm<2> imm2, ShiftTy const auto shifted = EmitImmShift(ir.GetRegister(m), type, imm3, imm2, ir.GetCFlag()); const auto result = ir.And(ir.GetRegister(n), shifted.result); - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); return true; } @@ -32,9 +30,7 @@ bool TranslatorVisitor::thumb32_AND_reg(bool S, Reg n, Imm<3> imm3, Reg d, Imm<2 const auto result = ir.And(ir.GetRegister(n), shifted.result); ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; } @@ -48,9 +44,7 @@ bool TranslatorVisitor::thumb32_BIC_reg(bool S, Reg n, Imm<3> imm3, Reg d, Imm<2 const auto result = ir.AndNot(ir.GetRegister(n), shifted.result); ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; } @@ -64,9 +58,7 @@ bool TranslatorVisitor::thumb32_MOV_reg(bool S, Imm<3> imm3, Reg d, Imm<2> imm2, const auto result = shifted.result; ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; } @@ -82,9 +74,7 @@ bool TranslatorVisitor::thumb32_ORR_reg(bool S, Reg n, Imm<3> imm3, Reg d, Imm<2 const auto result = ir.Or(ir.GetRegister(n), shifted.result); ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; } @@ -98,9 +88,7 @@ bool TranslatorVisitor::thumb32_MVN_reg(bool S, Imm<3> imm3, Reg d, Imm<2> imm2, const auto result = ir.Not(shifted.result); ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; } @@ -116,9 +104,7 @@ bool TranslatorVisitor::thumb32_ORN_reg(bool S, Reg n, Imm<3> imm3, Reg d, Imm<2 const auto result = ir.Or(ir.GetRegister(n), ir.Not(shifted.result)); ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; } @@ -131,9 +117,7 @@ bool TranslatorVisitor::thumb32_TEQ_reg(Reg n, Imm<3> imm3, Imm<2> imm2, ShiftTy const auto shifted = EmitImmShift(ir.GetRegister(m), type, imm3, imm2, ir.GetCFlag()); const auto result = ir.Eor(ir.GetRegister(n), shifted.result); - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); return true; } @@ -148,9 +132,7 @@ bool TranslatorVisitor::thumb32_EOR_reg(bool S, Reg n, Imm<3> imm3, Reg d, Imm<2 const auto result = ir.Eor(ir.GetRegister(n), shifted.result); ir.SetRegister(d, result); if (S) { - ir.SetNFlag(ir.MostSignificantBit(result)); - ir.SetZFlag(ir.IsZero(result)); - ir.SetCFlag(shifted.carry); + ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry); } return true; } diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/translate_thumb.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/translate_thumb.cpp index f2b39a6ca..a3cc4e8f2 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/translate_thumb.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/translate_thumb.cpp @@ -31,7 +31,7 @@ enum class ThumbInstSize { }; bool IsThumb16(u16 first_part) { - return (first_part & 0xF800) < 0xE800; + return first_part < 0xE800; } bool IsUnconditionalInstruction(bool is_thumb_16, u32 instruction) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/a64_location_descriptor.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/a64_location_descriptor.cpp index 81c6cafe0..83a931ff4 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A64/a64_location_descriptor.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/a64_location_descriptor.cpp @@ -5,15 +5,12 @@ #include "dynarmic/frontend/A64/a64_location_descriptor.h" -#include - #include namespace Dynarmic::A64 { -std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor) { - o << fmt::format("{{{}, {}{}}}", descriptor.PC(), descriptor.FPCR().Value(), descriptor.SingleStepping() ? ", step" : ""); - return o; +std::string ToString(const LocationDescriptor& descriptor) { + return fmt::format("{{{}, {}{}}}", descriptor.PC(), descriptor.FPCR().Value(), descriptor.SingleStepping() ? ", step" : ""); } } // namespace Dynarmic::A64 diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/a64_location_descriptor.h b/externals/dynarmic/src/dynarmic/frontend/A64/a64_location_descriptor.h index 3301616fa..122bebbcb 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A64/a64_location_descriptor.h +++ b/externals/dynarmic/src/dynarmic/frontend/A64/a64_location_descriptor.h @@ -6,9 +6,10 @@ #pragma once #include -#include +#include #include +#include #include #include @@ -84,10 +85,9 @@ private: /** * Provides a string representation of a LocationDescriptor. * - * @param o Output stream * @param descriptor The descriptor to get a string representation of */ -std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor); +std::string ToString(const LocationDescriptor& descriptor); } // namespace Dynarmic::A64 @@ -105,3 +105,11 @@ struct hash { } }; } // namespace std + +template<> +struct fmt::formatter : fmt::formatter { + template + auto format(Dynarmic::A64::LocationDescriptor descriptor, FormatContext& ctx) const { + return formatter::format(Dynarmic::A64::ToString(descriptor), ctx); + } +}; diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/a64_types.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/a64_types.cpp index 822cf123a..ca1c59ab3 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A64/a64_types.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/a64_types.cpp @@ -30,14 +30,4 @@ std::string VecToString(Vec vec) { return fmt::format("v{}", static_cast(vec)); } -std::ostream& operator<<(std::ostream& o, Reg reg) { - o << RegToString(reg); - return o; -} - -std::ostream& operator<<(std::ostream& o, Vec vec) { - o << VecToString(vec); - return o; -} - } // namespace Dynarmic::A64 diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/a64_types.h b/externals/dynarmic/src/dynarmic/frontend/A64/a64_types.h index 3bcb84aaf..6bc7a2453 100755 --- a/externals/dynarmic/src/dynarmic/frontend/A64/a64_types.h +++ b/externals/dynarmic/src/dynarmic/frontend/A64/a64_types.h @@ -5,9 +5,9 @@ #pragma once -#include #include +#include #include #include @@ -101,9 +101,6 @@ const char* CondToString(Cond cond); std::string RegToString(Reg reg); std::string VecToString(Vec vec); -std::ostream& operator<<(std::ostream& o, Reg reg); -std::ostream& operator<<(std::ostream& o, Vec vec); - constexpr size_t RegNumber(Reg reg) { return static_cast(reg); } @@ -127,3 +124,19 @@ inline Vec operator+(Vec vec, size_t number) { } } // namespace Dynarmic::A64 + +template<> +struct fmt::formatter : fmt::formatter { + template + auto format(Dynarmic::A64::Reg reg, FormatContext& ctx) const { + return formatter::format(Dynarmic::A64::RegToString(reg), ctx); + } +}; + +template<> +struct fmt::formatter : fmt::formatter { + template + auto format(Dynarmic::A64::Vec vec, FormatContext& ctx) const { + return formatter::format(Dynarmic::A64::VecToString(vec), ctx); + } +}; diff --git a/externals/dynarmic/src/dynarmic/ir/ir_emitter.cpp b/externals/dynarmic/src/dynarmic/ir/ir_emitter.cpp index f588d3dbb..004b13032 100755 --- a/externals/dynarmic/src/dynarmic/ir/ir_emitter.cpp +++ b/externals/dynarmic/src/dynarmic/ir/ir_emitter.cpp @@ -135,6 +135,10 @@ U32U64 IREmitter::ConditionalSelect(Cond cond, const U32U64& a, const U32U64& b) } } +U1 IREmitter::GetCFlagFromNZCV(const NZCV& nzcv) { + return Inst(Opcode::GetCFlagFromNZCV, nzcv); +} + NZCV IREmitter::NZCVFromPackedFlags(const U32& a) { return Inst(Opcode::NZCVFromPackedFlags, a); } diff --git a/externals/dynarmic/src/dynarmic/ir/ir_emitter.h b/externals/dynarmic/src/dynarmic/ir/ir_emitter.h index 82b8acc0f..62ea7e64c 100755 --- a/externals/dynarmic/src/dynarmic/ir/ir_emitter.h +++ b/externals/dynarmic/src/dynarmic/ir/ir_emitter.h @@ -101,6 +101,7 @@ public: NZCV ConditionalSelect(Cond cond, const NZCV& a, const NZCV& b); U32U64 ConditionalSelect(Cond cond, const U32U64& a, const U32U64& b); + U1 GetCFlagFromNZCV(const NZCV& nzcv); NZCV NZCVFromPackedFlags(const U32& a); // This pseudo-instruction may only be added to instructions that support it. NZCV NZCVFrom(const Value& value); @@ -393,14 +394,24 @@ public: void SetTerm(const Terminal& terminal); - void SetInsertionPoint(IR::Inst* new_insertion_point) { + void SetInsertionPointBefore(IR::Inst* new_insertion_point) { insertion_point = IR::Block::iterator{*new_insertion_point}; } - void SetInsertionPoint(IR::Block::iterator new_insertion_point) { + void SetInsertionPointBefore(IR::Block::iterator new_insertion_point) { insertion_point = new_insertion_point; } + void SetInsertionPointAfter(IR::Inst* new_insertion_point) { + insertion_point = IR::Block::iterator{*new_insertion_point}; + ++insertion_point; + } + + void SetInsertionPointAfter(IR::Block::iterator new_insertion_point) { + insertion_point = new_insertion_point; + ++insertion_point; + } + protected: IR::Block::iterator insertion_point; diff --git a/externals/dynarmic/src/dynarmic/ir/location_descriptor.cpp b/externals/dynarmic/src/dynarmic/ir/location_descriptor.cpp index 19e67ea23..e7e640561 100755 --- a/externals/dynarmic/src/dynarmic/ir/location_descriptor.cpp +++ b/externals/dynarmic/src/dynarmic/ir/location_descriptor.cpp @@ -5,15 +5,12 @@ #include "dynarmic/ir/location_descriptor.h" -#include - #include namespace Dynarmic::IR { -std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor) { - o << fmt::format("{{{:016x}}}", descriptor.Value()); - return o; +std::string ToString(const LocationDescriptor& descriptor) { + return fmt::format("{{{:016x}}}", descriptor.Value()); } } // namespace Dynarmic::IR diff --git a/externals/dynarmic/src/dynarmic/ir/location_descriptor.h b/externals/dynarmic/src/dynarmic/ir/location_descriptor.h index 4b5d77d85..48e5e32bb 100755 --- a/externals/dynarmic/src/dynarmic/ir/location_descriptor.h +++ b/externals/dynarmic/src/dynarmic/ir/location_descriptor.h @@ -6,8 +6,9 @@ #pragma once #include -#include +#include +#include #include namespace Dynarmic::IR { @@ -31,7 +32,7 @@ private: u64 value; }; -std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor); +std::string ToString(const LocationDescriptor& descriptor); inline bool operator<(const LocationDescriptor& x, const LocationDescriptor& y) noexcept { return x.Value() < y.Value(); @@ -53,3 +54,11 @@ struct hash { } }; } // namespace std + +template<> +struct fmt::formatter : fmt::formatter { + template + auto format(Dynarmic::IR::LocationDescriptor descriptor, FormatContext& ctx) const { + return formatter::format(ToString(descriptor), ctx); + } +}; diff --git a/externals/dynarmic/src/dynarmic/ir/microinstruction.cpp b/externals/dynarmic/src/dynarmic/ir/microinstruction.cpp index 81bfbbaa3..cf51363b1 100755 --- a/externals/dynarmic/src/dynarmic/ir/microinstruction.cpp +++ b/externals/dynarmic/src/dynarmic/ir/microinstruction.cpp @@ -7,7 +7,6 @@ #include -#include #include #include "dynarmic/ir/opcodes.h" @@ -176,10 +175,8 @@ bool Inst::WritesToCPSR() const { case Opcode::A32SetCpsrNZCVRaw: case Opcode::A32SetCpsrNZCV: case Opcode::A32SetCpsrNZCVQ: - case Opcode::A32SetNFlag: - case Opcode::A32SetZFlag: - case Opcode::A32SetCFlag: - case Opcode::A32SetVFlag: + case Opcode::A32SetCpsrNZ: + case Opcode::A32SetCpsrNZC: case Opcode::A32OrQFlag: case Opcode::A32SetGEFlags: case Opcode::A32SetGEFlagsCompressed: @@ -546,8 +543,12 @@ bool Inst::IsAPseudoOperation() const { case Opcode::GetOverflowFromOp: case Opcode::GetGEFromOp: case Opcode::GetNZCVFromOp: + case Opcode::GetNZFromOp: case Opcode::GetUpperFromOp: case Opcode::GetLowerFromOp: + case Opcode::MostSignificantBit: + case Opcode::IsZero32: + case Opcode::IsZero64: return true; default: @@ -583,40 +584,19 @@ bool Inst::AreAllArgsImmediates() const { } bool Inst::HasAssociatedPseudoOperation() const { - return carry_inst - || overflow_inst - || ge_inst - || nzcv_inst - || upper_inst - || lower_inst; + return next_pseudoop && !IsAPseudoOperation(); } Inst* Inst::GetAssociatedPseudoOperation(Opcode opcode) { - // This is faster than doing a search through the block. - switch (opcode) { - case Opcode::GetCarryFromOp: - ASSERT(!carry_inst || carry_inst->GetOpcode() == Opcode::GetCarryFromOp); - return carry_inst; - case Opcode::GetOverflowFromOp: - ASSERT(!overflow_inst || overflow_inst->GetOpcode() == Opcode::GetOverflowFromOp); - return overflow_inst; - case Opcode::GetGEFromOp: - ASSERT(!ge_inst || ge_inst->GetOpcode() == Opcode::GetGEFromOp); - return ge_inst; - case Opcode::GetNZCVFromOp: - ASSERT(!nzcv_inst || nzcv_inst->GetOpcode() == Opcode::GetNZCVFromOp); - return nzcv_inst; - case Opcode::GetUpperFromOp: - ASSERT(!upper_inst || upper_inst->GetOpcode() == Opcode::GetUpperFromOp); - return upper_inst; - case Opcode::GetLowerFromOp: - ASSERT(!lower_inst || lower_inst->GetOpcode() == Opcode::GetLowerFromOp); - return lower_inst; - default: - break; + Inst* pseudoop = next_pseudoop; + while (pseudoop) { + if (pseudoop->GetOpcode() == opcode) { + ASSERT(pseudoop->GetArg(0).GetInst() == this); + return pseudoop; + } + pseudoop = pseudoop->next_pseudoop; } - - ASSERT_FALSE("Not a valid pseudo-operation"); + return nullptr; } Type Inst::GetType() const { @@ -679,67 +659,31 @@ void Inst::ReplaceUsesWith(Value replacement) { void Inst::Use(const Value& value) { value.GetInst()->use_count++; - switch (op) { - case Opcode::GetCarryFromOp: - ASSERT_MSG(!value.GetInst()->carry_inst, "Only one of each type of pseudo-op allowed"); - value.GetInst()->carry_inst = this; - break; - case Opcode::GetOverflowFromOp: - ASSERT_MSG(!value.GetInst()->overflow_inst, "Only one of each type of pseudo-op allowed"); - value.GetInst()->overflow_inst = this; - break; - case Opcode::GetGEFromOp: - ASSERT_MSG(!value.GetInst()->ge_inst, "Only one of each type of pseudo-op allowed"); - value.GetInst()->ge_inst = this; - break; - case Opcode::GetNZCVFromOp: - ASSERT_MSG(!value.GetInst()->nzcv_inst, "Only one of each type of pseudo-op allowed"); - ASSERT_MSG(value.GetInst()->MayGetNZCVFromOp(), "This value doesn't support the GetNZCVFromOp pseduo-op"); - value.GetInst()->nzcv_inst = this; - break; - case Opcode::GetUpperFromOp: - ASSERT_MSG(!value.GetInst()->upper_inst, "Only one of each type of pseudo-op allowed"); - value.GetInst()->upper_inst = this; - break; - case Opcode::GetLowerFromOp: - ASSERT_MSG(!value.GetInst()->lower_inst, "Only one of each type of pseudo-op allowed"); - value.GetInst()->lower_inst = this; - break; - default: - break; + if (IsAPseudoOperation()) { + if (op == Opcode::GetNZCVFromOp) { + ASSERT_MSG(value.GetInst()->MayGetNZCVFromOp(), "This value doesn't support the GetNZCVFromOp pseduo-op"); + } + + Inst* insert_point = value.GetInst(); + while (insert_point->next_pseudoop) { + insert_point = insert_point->next_pseudoop; + DEBUG_ASSERT(insert_point->GetArg(0).GetInst() == value.GetInst()); + } + insert_point->next_pseudoop = this; } } void Inst::UndoUse(const Value& value) { value.GetInst()->use_count--; - switch (op) { - case Opcode::GetCarryFromOp: - ASSERT(value.GetInst()->carry_inst->GetOpcode() == Opcode::GetCarryFromOp); - value.GetInst()->carry_inst = nullptr; - break; - case Opcode::GetOverflowFromOp: - ASSERT(value.GetInst()->overflow_inst->GetOpcode() == Opcode::GetOverflowFromOp); - value.GetInst()->overflow_inst = nullptr; - break; - case Opcode::GetGEFromOp: - ASSERT(value.GetInst()->ge_inst->GetOpcode() == Opcode::GetGEFromOp); - value.GetInst()->ge_inst = nullptr; - break; - case Opcode::GetNZCVFromOp: - ASSERT(value.GetInst()->nzcv_inst->GetOpcode() == Opcode::GetNZCVFromOp); - value.GetInst()->nzcv_inst = nullptr; - break; - case Opcode::GetUpperFromOp: - ASSERT(value.GetInst()->upper_inst->GetOpcode() == Opcode::GetUpperFromOp); - value.GetInst()->upper_inst = nullptr; - break; - case Opcode::GetLowerFromOp: - ASSERT(value.GetInst()->lower_inst->GetOpcode() == Opcode::GetLowerFromOp); - value.GetInst()->lower_inst = nullptr; - break; - default: - break; + if (IsAPseudoOperation()) { + Inst* insert_point = value.GetInst(); + while (insert_point->next_pseudoop != this) { + insert_point = insert_point->next_pseudoop; + DEBUG_ASSERT(insert_point->GetArg(0).GetInst() == value.GetInst()); + } + insert_point->next_pseudoop = next_pseudoop; + next_pseudoop = nullptr; } } diff --git a/externals/dynarmic/src/dynarmic/ir/microinstruction.h b/externals/dynarmic/src/dynarmic/ir/microinstruction.h index a5011b200..7c2d9dc73 100755 --- a/externals/dynarmic/src/dynarmic/ir/microinstruction.h +++ b/externals/dynarmic/src/dynarmic/ir/microinstruction.h @@ -149,18 +149,8 @@ private: size_t use_count = 0; std::array args; - // Pointers to related pseudooperations: - // Since not all combinations are possible, we use a union to save space - union { - Inst* carry_inst = nullptr; - Inst* ge_inst; - Inst* upper_inst; - }; - Inst* overflow_inst = nullptr; - union { - Inst* nzcv_inst = nullptr; - Inst* lower_inst; - }; + // Linked list of pseudooperations associated with this instruction. + Inst* next_pseudoop = nullptr; }; } // namespace Dynarmic::IR diff --git a/externals/dynarmic/src/dynarmic/ir/opcodes.cpp b/externals/dynarmic/src/dynarmic/ir/opcodes.cpp index 2741e5fbd..e80915f9a 100755 --- a/externals/dynarmic/src/dynarmic/ir/opcodes.cpp +++ b/externals/dynarmic/src/dynarmic/ir/opcodes.cpp @@ -73,8 +73,4 @@ std::string GetNameOf(Opcode op) { return OpcodeInfo::opcode_info.at(static_cast(op)).name; } -std::ostream& operator<<(std::ostream& o, Opcode opcode) { - return o << GetNameOf(opcode); -} - } // namespace Dynarmic::IR diff --git a/externals/dynarmic/src/dynarmic/ir/opcodes.h b/externals/dynarmic/src/dynarmic/ir/opcodes.h index 719fda001..404f4cdbf 100755 --- a/externals/dynarmic/src/dynarmic/ir/opcodes.h +++ b/externals/dynarmic/src/dynarmic/ir/opcodes.h @@ -5,9 +5,9 @@ #pragma once -#include #include +#include #include namespace Dynarmic::IR { @@ -43,6 +43,12 @@ Type GetArgTypeOf(Opcode op, size_t arg_index); /// Get the name of an opcode. std::string GetNameOf(Opcode op); -std::ostream& operator<<(std::ostream& o, Opcode opcode); - } // namespace Dynarmic::IR + +template<> +struct fmt::formatter : fmt::formatter { + template + auto format(Dynarmic::IR::Opcode op, FormatContext& ctx) const { + return formatter::format(Dynarmic::IR::GetNameOf(op), ctx); + } +}; diff --git a/externals/dynarmic/src/dynarmic/ir/opcodes.inc b/externals/dynarmic/src/dynarmic/ir/opcodes.inc index 791c29a5f..c2954c31d 100755 --- a/externals/dynarmic/src/dynarmic/ir/opcodes.inc +++ b/externals/dynarmic/src/dynarmic/ir/opcodes.inc @@ -22,11 +22,9 @@ A32OPC(SetCpsr, Void, U32 A32OPC(SetCpsrNZCV, Void, NZCV ) A32OPC(SetCpsrNZCVRaw, Void, U32 ) A32OPC(SetCpsrNZCVQ, Void, U32 ) -A32OPC(SetNFlag, Void, U1 ) -A32OPC(SetZFlag, Void, U1 ) +A32OPC(SetCpsrNZ, Void, NZCV ) +A32OPC(SetCpsrNZC, Void, NZCV, U1 ) A32OPC(GetCFlag, U1, ) -A32OPC(SetCFlag, Void, U1 ) -A32OPC(SetVFlag, Void, U1 ) A32OPC(OrQFlag, Void, U1 ) A32OPC(GetGEFlags, U32, ) A32OPC(SetGEFlags, Void, U32 ) @@ -90,9 +88,11 @@ OPCODE(GetCarryFromOp, U1, Opaq OPCODE(GetOverflowFromOp, U1, Opaque ) OPCODE(GetGEFromOp, U32, Opaque ) OPCODE(GetNZCVFromOp, NZCV, Opaque ) +OPCODE(GetNZFromOp, NZCV, Opaque ) OPCODE(GetUpperFromOp, U128, Opaque ) OPCODE(GetLowerFromOp, U128, Opaque ) +OPCODE(GetCFlagFromNZCV, U1, NZCV ) OPCODE(NZCVFromPackedFlags, NZCV, U32 ) // Calculations diff --git a/externals/dynarmic/src/dynarmic/ir/opt/a32_constant_memory_reads_pass.cpp b/externals/dynarmic/src/dynarmic/ir/opt/a32_constant_memory_reads_pass.cpp index 828a76877..9699f1834 100755 --- a/externals/dynarmic/src/dynarmic/ir/opt/a32_constant_memory_reads_pass.cpp +++ b/externals/dynarmic/src/dynarmic/ir/opt/a32_constant_memory_reads_pass.cpp @@ -13,13 +13,6 @@ namespace Dynarmic::Optimization { void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) { for (auto& inst : block) { switch (inst.GetOpcode()) { - case IR::Opcode::A32SetCFlag: { - const IR::Value arg = inst.GetArg(0); - if (!arg.IsImmediate() && arg.GetInst()->GetOpcode() == IR::Opcode::A32GetCFlag) { - inst.Invalidate(); - } - break; - } case IR::Opcode::A32ReadMemory8: { if (!inst.AreAllArgsImmediates()) { break; diff --git a/externals/dynarmic/src/dynarmic/ir/opt/a32_get_set_elimination_pass.cpp b/externals/dynarmic/src/dynarmic/ir/opt/a32_get_set_elimination_pass.cpp index 2689d194b..95aef3456 100755 --- a/externals/dynarmic/src/dynarmic/ir/opt/a32_get_set_elimination_pass.cpp +++ b/externals/dynarmic/src/dynarmic/ir/opt/a32_get_set_elimination_pass.cpp @@ -8,6 +8,7 @@ #include #include +#include "dynarmic/frontend/A32/a32_ir_emitter.h" #include "dynarmic/frontend/A32/a32_types.h" #include "dynarmic/ir/basic_block.h" #include "dynarmic/ir/opcodes.h" @@ -16,7 +17,7 @@ namespace Dynarmic::Optimization { -void A32GetSetElimination(IR::Block& block) { +void A32GetSetElimination(IR::Block& block, A32GetSetEliminationOptions opt) { using Iterator = IR::Block::iterator; struct RegisterInfo { IR::Value register_value; @@ -29,24 +30,34 @@ void A32GetSetElimination(IR::Block& block) { std::array ext_reg_vector_double_info; std::array ext_reg_vector_quad_info; struct CpsrInfo { - RegisterInfo n; - RegisterInfo z; + RegisterInfo nz; RegisterInfo c; - RegisterInfo v; + RegisterInfo nzc; + RegisterInfo nzcv; RegisterInfo ge; } cpsr_info; - const auto do_set = [&block](RegisterInfo& info, IR::Value value, Iterator set_inst) { + const auto do_delete_last_set = [&block](RegisterInfo& info) { if (info.set_instruction_present) { + info.set_instruction_present = false; info.last_set_instruction->Invalidate(); block.Instructions().erase(info.last_set_instruction); } + info = {}; + }; + const auto do_set = [&do_delete_last_set](RegisterInfo& info, IR::Value value, Iterator set_inst) { + do_delete_last_set(info); info.register_value = value; info.set_instruction_present = true; info.last_set_instruction = set_inst; }; + const auto do_set_without_inst = [&do_delete_last_set](RegisterInfo& info, IR::Value value) { + do_delete_last_set(info); + info.register_value = value; + }; + const auto do_get = [](RegisterInfo& info, Iterator get_inst) { if (info.register_value.IsEmpty()) { info.register_value = IR::Value(&*get_inst); @@ -55,6 +66,9 @@ void A32GetSetElimination(IR::Block& block) { get_inst->ReplaceUsesWith(info.register_value); }; + // Location and version don't matter here. + A32::IREmitter ir{block, A32::LocationDescriptor{block.Location()}, {}}; + for (auto inst = block.begin(); inst != block.end(); ++inst) { switch (inst->GetOpcode()) { case IR::Opcode::A32SetRegister: { @@ -167,24 +181,66 @@ void A32GetSetElimination(IR::Block& block) { } break; } - case IR::Opcode::A32SetNFlag: { - do_set(cpsr_info.n, inst->GetArg(0), inst); - break; - } - case IR::Opcode::A32SetZFlag: { - do_set(cpsr_info.z, inst->GetArg(0), inst); - break; - } - case IR::Opcode::A32SetCFlag: { - do_set(cpsr_info.c, inst->GetArg(0), inst); - break; - } case IR::Opcode::A32GetCFlag: { + if (cpsr_info.c.register_value.IsEmpty() && cpsr_info.nzcv.register_value.GetType() == IR::Type::NZCVFlags) { + ir.SetInsertionPointBefore(inst); + IR::U1 c = ir.GetCFlagFromNZCV(IR::NZCV{cpsr_info.nzcv.register_value}); + inst->ReplaceUsesWith(c); + cpsr_info.c.register_value = c; + break; + } + do_get(cpsr_info.c, inst); + // ensure source is not deleted + cpsr_info.nzc = {}; + cpsr_info.nzcv = {}; break; } - case IR::Opcode::A32SetVFlag: { - do_set(cpsr_info.v, inst->GetArg(0), inst); + case IR::Opcode::A32SetCpsrNZCV: + case IR::Opcode::A32SetCpsrNZCVRaw: { + do_delete_last_set(cpsr_info.nz); + do_delete_last_set(cpsr_info.c); + do_delete_last_set(cpsr_info.nzc); + do_set(cpsr_info.nzcv, inst->GetArg(0), inst); + break; + } + case IR::Opcode::A32SetCpsrNZCVQ: { + do_delete_last_set(cpsr_info.nz); + do_delete_last_set(cpsr_info.c); + do_delete_last_set(cpsr_info.nzc); + do_delete_last_set(cpsr_info.nzcv); + break; + } + case IR::Opcode::A32SetCpsrNZ: { + if (cpsr_info.nzc.set_instruction_present) { + cpsr_info.nzc.last_set_instruction->SetArg(0, IR::Value::EmptyNZCVImmediateMarker()); + } + + if (opt.convert_nz_to_nzc && !cpsr_info.c.register_value.IsEmpty()) { + ir.SetInsertionPointAfter(inst); + ir.SetCpsrNZC(IR::NZCV{inst->GetArg(0)}, ir.GetCFlag()); + inst->Invalidate(); + break; + } + + // cpsr_info.c remains valid + cpsr_info.nzc = {}; + cpsr_info.nzcv = {}; + do_set(cpsr_info.nz, inst->GetArg(0), inst); + break; + } + case IR::Opcode::A32SetCpsrNZC: { + if (opt.convert_nzc_to_nz && !inst->GetArg(1).IsImmediate() && inst->GetArg(1).GetInstRecursive()->GetOpcode() == IR::Opcode::A32GetCFlag) { + ir.SetInsertionPointAfter(inst); + ir.SetCpsrNZ(IR::NZCV{inst->GetArg(0)}); + inst->Invalidate(); + break; + } + + cpsr_info.nzcv = {}; + do_set(cpsr_info.nzc, {}, inst); + do_set_without_inst(cpsr_info.nz, inst->GetArg(0)); + do_set_without_inst(cpsr_info.c, inst->GetArg(1)); break; } case IR::Opcode::A32SetGEFlags: { diff --git a/externals/dynarmic/src/dynarmic/ir/opt/a64_callback_config_pass.cpp b/externals/dynarmic/src/dynarmic/ir/opt/a64_callback_config_pass.cpp index 1e0195c4b..79d976952 100755 --- a/externals/dynarmic/src/dynarmic/ir/opt/a64_callback_config_pass.cpp +++ b/externals/dynarmic/src/dynarmic/ir/opt/a64_callback_config_pass.cpp @@ -26,7 +26,7 @@ void A64CallbackConfigPass(IR::Block& block, const A64::UserConfig& conf) { if (op == A64::DataCacheOperation::ZeroByVA) { A64::IREmitter ir{block}; ir.current_location = A64::LocationDescriptor{IR::LocationDescriptor{inst.GetArg(0).GetU64()}}; - ir.SetInsertionPoint(&inst); + ir.SetInsertionPointBefore(&inst); size_t bytes = 4 << static_cast(conf.dczid_el0 & 0b1111); IR::U64 addr{inst.GetArg(2)}; diff --git a/externals/dynarmic/src/dynarmic/ir/opt/passes.h b/externals/dynarmic/src/dynarmic/ir/opt/passes.h index ad5d09dfd..08bfe4fe9 100755 --- a/externals/dynarmic/src/dynarmic/ir/opt/passes.h +++ b/externals/dynarmic/src/dynarmic/ir/opt/passes.h @@ -26,9 +26,14 @@ struct PolyfillOptions { bool operator==(const PolyfillOptions&) const = default; }; +struct A32GetSetEliminationOptions { + bool convert_nzc_to_nz = false; + bool convert_nz_to_nzc = false; +}; + void PolyfillPass(IR::Block& block, const PolyfillOptions& opt); void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb); -void A32GetSetElimination(IR::Block& block); +void A32GetSetElimination(IR::Block& block, A32GetSetEliminationOptions opt); void A64CallbackConfigPass(IR::Block& block, const A64::UserConfig& conf); void A64GetSetElimination(IR::Block& block); void A64MergeInterpretBlocksPass(IR::Block& block, A64::UserCallbacks* cb); diff --git a/externals/dynarmic/src/dynarmic/ir/opt/polyfill_pass.cpp b/externals/dynarmic/src/dynarmic/ir/opt/polyfill_pass.cpp index 77800ddc9..8580f4827 100755 --- a/externals/dynarmic/src/dynarmic/ir/opt/polyfill_pass.cpp +++ b/externals/dynarmic/src/dynarmic/ir/opt/polyfill_pass.cpp @@ -148,7 +148,7 @@ void PolyfillPass(IR::Block& block, const PolyfillOptions& polyfill) { IR::IREmitter ir{block}; for (auto& inst : block) { - ir.SetInsertionPoint(&inst); + ir.SetInsertionPointBefore(&inst); switch (inst.GetOpcode()) { case IR::Opcode::SHA256MessageSchedule0: diff --git a/externals/dynarmic/src/dynarmic/ir/type.cpp b/externals/dynarmic/src/dynarmic/ir/type.cpp index bf1a35c49..7c0f6bf62 100755 --- a/externals/dynarmic/src/dynarmic/ir/type.cpp +++ b/externals/dynarmic/src/dynarmic/ir/type.cpp @@ -43,8 +43,4 @@ bool AreTypesCompatible(Type t1, Type t2) { return t1 == t2 || t1 == Type::Opaque || t2 == Type::Opaque; } -std::ostream& operator<<(std::ostream& o, Type type) { - return o << GetNameOf(type); -} - } // namespace Dynarmic::IR diff --git a/externals/dynarmic/src/dynarmic/ir/type.h b/externals/dynarmic/src/dynarmic/ir/type.h index 7f99e4749..65fe76dd6 100755 --- a/externals/dynarmic/src/dynarmic/ir/type.h +++ b/externals/dynarmic/src/dynarmic/ir/type.h @@ -5,9 +5,9 @@ #pragma once -#include #include +#include #include namespace Dynarmic::IR { @@ -49,6 +49,12 @@ std::string GetNameOf(Type type); /// @returns true if t1 and t2 are compatible types bool AreTypesCompatible(Type t1, Type t2); -std::ostream& operator<<(std::ostream& o, Type type); - } // namespace Dynarmic::IR + +template<> +struct fmt::formatter : fmt::formatter { + template + auto format(Dynarmic::IR::Type type, FormatContext& ctx) const { + return formatter::format(Dynarmic::IR::GetNameOf(type), ctx); + } +}; diff --git a/externals/dynarmic/src/dynarmic/ir/value.cpp b/externals/dynarmic/src/dynarmic/ir/value.cpp index 5b86f731a..20f94cb5f 100755 --- a/externals/dynarmic/src/dynarmic/ir/value.cpp +++ b/externals/dynarmic/src/dynarmic/ir/value.cpp @@ -79,6 +79,12 @@ Value::Value(AccType value) inner.imm_acctype = value; } +Value Value::EmptyNZCVImmediateMarker() { + Value result{}; + result.type = Type::NZCVFlags; + return result; +} + bool Value::IsIdentity() const { if (type == Type::Opaque) return inner.inst->GetOpcode() == Opcode::Identity; diff --git a/externals/dynarmic/src/dynarmic/ir/value.h b/externals/dynarmic/src/dynarmic/ir/value.h index 122d86e5d..8f70110a8 100755 --- a/externals/dynarmic/src/dynarmic/ir/value.h +++ b/externals/dynarmic/src/dynarmic/ir/value.h @@ -53,6 +53,8 @@ public: explicit Value(Cond value); explicit Value(AccType value); + static Value EmptyNZCVImmediateMarker(); + bool IsIdentity() const; bool IsEmpty() const; bool IsImmediate() const; diff --git a/externals/dynarmic/tests/A32/fuzz_thumb.cpp b/externals/dynarmic/tests/A32/fuzz_thumb.cpp index d412b97e8..ff68f2f2e 100755 --- a/externals/dynarmic/tests/A32/fuzz_thumb.cpp +++ b/externals/dynarmic/tests/A32/fuzz_thumb.cpp @@ -176,7 +176,7 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn (uses: 0) + // [0000556569455a50] %20 = GetRegister r2 (uses: 1) + // [0000556569455ab8] %21 = GetRegister r5 (uses: 1) + // [0000556569455b20] %22 = Add32 %21, %20, #0 (uses: 1) + // [0000556569455b88] SetRegister r5, %22 (uses: 0) + // terminal = LinkBlock{{000000010000000a}} + + ThumbTestEnv test_env; + Dynarmic::A32::Jit jit{GetUserConfig(&test_env)}; + test_env.code_mem = { + 0x434e, // muls r6, r1, r6 + 0x4557, // cmp r7, r10 + 0x415b, // adcs r3, r3 + 0x94b1, // str r4, [sp, #708] + 0x4415, // add r5, r2 + 0xe7fe // b +#0 + }; + + jit.Regs() = {0x2154abb5, 0xdbaa6333, 0xf8a7bc0e, 0x989f6096, 0x19cd7783, 0xe1cf5b7f, 0x9bb1aa6c, 0x6b700f5c, + 0xc04f6cb2, 0xc8df07f0, 0x217d83de, 0xe77fdffa, 0x98bcceaf, 0xbfcab4f7, 0xdb9d5405, 0x00000000}; + jit.SetCpsr(0x000001f0); // Thumb, User-mode + + test_env.ticks_left = 7; + jit.Run(); + + REQUIRE(jit.Regs()[0] == 0x2154abb5); + REQUIRE(jit.Regs()[1] == 0xdbaa6333); + REQUIRE(jit.Regs()[2] == 0xf8a7bc0e); + REQUIRE(jit.Regs()[3] == 0x313ec12d); + REQUIRE(jit.Regs()[4] == 0x19cd7783); + REQUIRE(jit.Regs()[5] == 0xda77178d); + REQUIRE(jit.Regs()[6] == 0x4904b784); + REQUIRE(jit.Regs()[7] == 0x6b700f5c); + REQUIRE(jit.Regs()[8] == 0xc04f6cb2); + REQUIRE(jit.Regs()[9] == 0xc8df07f0); + REQUIRE(jit.Regs()[10] == 0x217d83de); + REQUIRE(jit.Regs()[11] == 0xe77fdffa); + REQUIRE(jit.Regs()[12] == 0x98bcceaf); + REQUIRE(jit.Regs()[13] == 0xbfcab4f7); + REQUIRE(jit.Regs()[14] == 0xdb9d5405); + REQUIRE(jit.Regs()[15] == 0x0000000a); + REQUIRE(jit.Cpsr() == 0x300001f0); +} + +TEST_CASE("thumb: Opt Failure: Get/Set Elimination for Flags 2", "[thumb]") { + // This was a randomized test-case that was failing. + + ThumbTestEnv test_env; + Dynarmic::A32::Jit jit{GetUserConfig(&test_env)}; + test_env.code_mem = { + 0x442a, // add r2, r5 + 0x065d, // lsls r5, r3, #25 + 0xbc64, // pop {r2, r5, r6} + 0x2666, // movs r6, #102 + 0x7471, // strb r1, [r6, #17] + 0xe7fe // b +#0 + }; + + jit.Regs() = {0x954d53b0, 0x4caaad40, 0xa42325b8, 0x0da0cdb6, 0x0f43507e, 0x31d68ae1, 0x9c471808, 0x892a6888, + 0x3b9ffb23, 0x0a92ef93, 0x38dee619, 0xc0e95e81, 0x6a448690, 0xc2d4d6ad, 0xe93600b9, 0x00000000}; + jit.SetCpsr(0x000001f0); // Thumb, User-mode + + test_env.ticks_left = 7; + jit.Run(); + + const std::array expected = {0x954d53b0, 0x4caaad40, 0xb0afaead, 0x0da0cdb6, 0x0f43507e, 0xb4b3b2b1, 0x00000066, 0x892a6888, + 0x3b9ffb23, 0x0a92ef93, 0x38dee619, 0xc0e95e81, 0x6a448690, 0xc2d4d6b9, 0xe93600b9, 0x0000000a}; + REQUIRE(jit.Regs() == expected); + REQUIRE(jit.Cpsr() == 0x200001f0); +} diff --git a/externals/dynarmic/tests/A64/a64.cpp b/externals/dynarmic/tests/A64/a64.cpp index 795205692..d6ac341b2 100755 --- a/externals/dynarmic/tests/A64/a64.cpp +++ b/externals/dynarmic/tests/A64/a64.cpp @@ -1179,3 +1179,23 @@ TEST_CASE("A64: Memory access (fastmem)", "[a64]") { jit.Run(); REQUIRE(strncmp(backing_memory + 0x100, backing_memory + 0x1F0, 23) == 0); } + +TEST_CASE("A64: SQRDMULH QC flag when output invalidated", "[a64]") { + A64TestEnv env; + A64::Jit jit{A64::UserConfig{&env}}; + + env.code_mem.emplace_back(0x0fbcd38b); // SQRDMULH.2S V11, V28, V28[1] + env.code_mem.emplace_back(0x7ef0f8eb); // FMINP.2D D11, V7 + env.code_mem.emplace_back(0x14000000); // B . + + jit.SetPC(0); + jit.SetVector(7, {0xb1b5'd0b1'4e54'e281, 0xb4cb'4fec'8563'1032}); + jit.SetVector(28, {0x8000'0000'0000'0000, 0x0000'0000'0000'0000}); + jit.SetFpcr(0x05400000); + + env.ticks_left = 3; + jit.Run(); + + REQUIRE(jit.GetFpsr() == 0x08000000); + REQUIRE(jit.GetVector(11) == Vector{0xb4cb'4fec'8563'1032, 0x0000'0000'0000'0000}); +} diff --git a/externals/dynarmic/tests/A64/fuzz_with_unicorn.cpp b/externals/dynarmic/tests/A64/fuzz_with_unicorn.cpp index 0307751cf..847a2ae9f 100755 --- a/externals/dynarmic/tests/A64/fuzz_with_unicorn.cpp +++ b/externals/dynarmic/tests/A64/fuzz_with_unicorn.cpp @@ -211,7 +211,7 @@ static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv fmt::print("{:3s}: {:016x}\n", A64::RegToString(static_cast(i)), regs[i]); } for (size_t i = 0; i < vecs.size(); ++i) { - fmt::print("{:3s}: {}{}\n", A64::VecToString(static_cast(i)), vecs[i][1], vecs[i][0]); + fmt::print("{:3s}: {:016x}{:016x}\n", A64::VecToString(static_cast(i)), vecs[i][1], vecs[i][0]); } fmt::print("sp : {:016x}\n", initial_sp); fmt::print("pc : {:016x}\n", instructions_start); diff --git a/externals/dynarmic/tests/print_info.cpp b/externals/dynarmic/tests/print_info.cpp index ac07e3ca5..a388e3fc8 100755 --- a/externals/dynarmic/tests/print_info.cpp +++ b/externals/dynarmic/tests/print_info.cpp @@ -64,7 +64,7 @@ void PrintA32Instruction(u32 instruction) { fmt::print("IR:\n"); fmt::print("{}\n", IR::DumpBlock(block)); - Optimization::A32GetSetElimination(block); + Optimization::A32GetSetElimination(block, {}); Optimization::DeadCodeElimination(block); Optimization::ConstantPropagation(block); Optimization::DeadCodeElimination(block); @@ -109,7 +109,7 @@ void PrintThumbInstruction(u32 instruction) { fmt::print("IR:\n"); fmt::print("{}\n", IR::DumpBlock(block)); - Optimization::A32GetSetElimination(block); + Optimization::A32GetSetElimination(block, {}); Optimization::DeadCodeElimination(block); Optimization::ConstantPropagation(block); Optimization::DeadCodeElimination(block); diff --git a/externals/dynarmic/tests/unicorn_emu/a32_unicorn.cpp b/externals/dynarmic/tests/unicorn_emu/a32_unicorn.cpp index ed8be3d19..db704c97d 100755 --- a/externals/dynarmic/tests/unicorn_emu/a32_unicorn.cpp +++ b/externals/dynarmic/tests/unicorn_emu/a32_unicorn.cpp @@ -12,12 +12,12 @@ #include "../A32/testenv.h" -#define CHECKED(expr) \ - do { \ - if (auto cerr_ = (expr)) { \ - ASSERT_MSG(false, "Call " #expr " failed with error: {} ({})\n", cerr_, \ - uc_strerror(cerr_)); \ - } \ +#define CHECKED(expr) \ + do { \ + if (auto cerr_ = (expr)) { \ + ASSERT_MSG(false, "Call " #expr " failed with error: {} ({})\n", static_cast(cerr_), \ + uc_strerror(cerr_)); \ + } \ } while (0) constexpr u32 BEGIN_ADDRESS = 0; @@ -52,7 +52,7 @@ void A32Unicorn::Run() { return; } if (auto cerr_ = uc_emu_start(uc, pc, END_ADDRESS, 0, 1)) { - fmt::print("uc_emu_start failed @ {:08x} (code = {:08x}) with error {} ({})", pc, *testenv.MemoryReadCode(pc), cerr_, uc_strerror(cerr_)); + fmt::print("uc_emu_start failed @ {:08x} (code = {:08x}) with error {} ({})", pc, *testenv.MemoryReadCode(pc), static_cast(cerr_), uc_strerror(cerr_)); throw "A32Unicorn::Run() failure"; } testenv.ticks_left--; diff --git a/externals/dynarmic/tests/unicorn_emu/a64_unicorn.cpp b/externals/dynarmic/tests/unicorn_emu/a64_unicorn.cpp index f4e14b25b..3c202c7c8 100755 --- a/externals/dynarmic/tests/unicorn_emu/a64_unicorn.cpp +++ b/externals/dynarmic/tests/unicorn_emu/a64_unicorn.cpp @@ -7,12 +7,12 @@ #include -#define CHECKED(expr) \ - do { \ - if (auto cerr_ = (expr)) { \ - ASSERT_MSG(false, "Call " #expr " failed with error: {} ({})\n", cerr_, \ - uc_strerror(cerr_)); \ - } \ +#define CHECKED(expr) \ + do { \ + if (auto cerr_ = (expr)) { \ + ASSERT_MSG(false, "Call " #expr " failed with error: {} ({})\n", static_cast(cerr_), \ + uc_strerror(cerr_)); \ + } \ } while (0) constexpr u64 BEGIN_ADDRESS = 0; diff --git a/src/audio_core/renderer/effect/effect_info_base.h b/src/audio_core/renderer/effect/effect_info_base.h index 43d0589cc..8c9583878 100755 --- a/src/audio_core/renderer/effect/effect_info_base.h +++ b/src/audio_core/renderer/effect/effect_info_base.h @@ -419,13 +419,13 @@ protected: /// Workbuffers assigned to this effect std::array workbuffers{AddressInfo(CpuAddr(0), 0), AddressInfo(CpuAddr(0), 0)}; /// Aux/Capture buffer info for reading - CpuAddr send_buffer_info; + CpuAddr send_buffer_info{}; /// Aux/Capture buffer for reading - CpuAddr send_buffer; + CpuAddr send_buffer{}; /// Aux/Capture buffer info for writing - CpuAddr return_buffer_info; + CpuAddr return_buffer_info{}; /// Aux/Capture buffer for writing - CpuAddr return_buffer; + CpuAddr return_buffer{}; /// Parameters of this effect std::array parameter{}; /// State of this effect used by the AudioRenderer across calls diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h index f7ae9d8c2..053798e79 100755 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -39,7 +39,7 @@ public: template void Push(Arg&& t) { // create the element, add it to the queue - write_ptr->current = std::forward(t); + write_ptr->current = std::move(t); // set the next pointer to a new element ptr // then advance the write pointer ElementPtr* new_ptr = new ElementPtr(); diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 354f8ed0b..b119b1815 100755 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -296,7 +296,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, state.has_loaded = true; lock.unlock(); - workers->WaitForRequests(); + workers->WaitForRequests(stop_loading); if (!use_asynchronous_shaders) { workers.reset(); } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 6129fda61..503d4aeed 100755 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -450,7 +450,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading state.has_loaded = true; lock.unlock(); - workers.WaitForRequests(); + workers.WaitForRequests(stop_loading); if (state.statistics) { state.statistics->Report();