early-access version 2875

This commit is contained in:
pineappleEA 2022-07-30 11:08:16 +02:00
parent 8ac1d0357b
commit b65fa338c9
111 changed files with 5509 additions and 4354 deletions

View file

@ -38,9 +38,13 @@ option(YUZU_USE_BUNDLED_OPUS "Compile bundled opus" ON)
option(YUZU_TESTS "Compile tests" 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_USE_BUNDLED_VCPKG)
if (YUZU_TESTS)
list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests")
endif()
include(${CMAKE_SOURCE_DIR}/externals/vcpkg/scripts/buildsystems/vcpkg.cmake) include(${CMAKE_SOURCE_DIR}/externals/vcpkg/scripts/buildsystems/vcpkg.cmake)
elseif(NOT "$ENV{VCPKG_TOOLCHAIN_FILE}" STREQUAL "") elseif(NOT "$ENV{VCPKG_TOOLCHAIN_FILE}" STREQUAL "")
# Disable manifest mode (use vcpkg classic mode) when using a custom vcpkg installation # 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(fmt 8.0.1 REQUIRED CONFIG)
find_package(lz4 1.8 REQUIRED)
find_package(nlohmann_json 3.8 REQUIRED CONFIG) find_package(nlohmann_json 3.8 REQUIRED CONFIG)
find_package(ZLIB 1.2 REQUIRED) find_package(ZLIB 1.2 REQUIRED)
@ -175,6 +178,12 @@ if (NOT zstd_FOUND)
find_package(zstd 1.5 REQUIRED) find_package(zstd 1.5 REQUIRED)
endif() 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) if (YUZU_TESTS)
find_package(Catch2 2.13.7 REQUIRED CONFIG) find_package(Catch2 2.13.7 REQUIRED CONFIG)
endif() endif()
@ -360,16 +369,10 @@ if (ENABLE_SDL2)
endif() endif()
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
#
# Reexport some targets that are named differently when using the upstream CmakeConfig vs the generated Conan config
# In order to ALIAS targets to a new name, they first need to be IMPORTED_GLOBAL # 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 # 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) 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)
set_target_properties(Boost::boost PROPERTIES IMPORTED_GLOBAL TRUE) set_target_properties(Boost::boost PROPERTIES IMPORTED_GLOBAL TRUE)
add_library(boost ALIAS Boost::boost) add_library(boost ALIAS Boost::boost)
endif() endif()

View file

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 2874. This is the source code for early-access 2875.
## Legal Notice ## Legal Notice

View file

@ -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}}

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.8) 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) # Determine if we're built as a subproject (using add_subdirectory)
# or if this is the master project. # or if this is the master project.

View file

@ -0,0 +1,6 @@
<!--
Please make sure that the problem reproduces on the current master before
submitting an issue.
If possible please provide a repro on Compiler Explorer:
https://godbolt.org/z/fxccbh53W.
-->

View file

@ -2,6 +2,9 @@ name: doc
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
build: build:
# Use Ubuntu 20.04 because doxygen 1.8.13 from Ubuntu 18.04 is broken. # Use Ubuntu 20.04 because doxygen 1.8.13 from Ubuntu 18.04 is broken.

View file

@ -2,6 +2,9 @@ name: linux
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
build: build:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -20,6 +23,11 @@ jobs:
std: 14 std: 14
install: sudo apt install g++-8 install: sudo apt install g++-8
os: ubuntu-18.04 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 - cxx: g++-10
build_type: Debug build_type: Debug
std: 17 std: 17
@ -29,6 +37,12 @@ jobs:
std: 20 std: 20
os: ubuntu-20.04 os: ubuntu-20.04
install: sudo apt install g++-11 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 - cxx: clang++-9
build_type: Debug build_type: Debug
fuzz: -DFMT_FUZZ=ON -DFMT_FUZZ_LINKMAIN=ON fuzz: -DFMT_FUZZ=ON -DFMT_FUZZ_LINKMAIN=ON
@ -52,6 +66,7 @@ jobs:
- name: Create Build Environment - name: Create Build Environment
run: | run: |
${{matrix.install}} ${{matrix.install}}
sudo apt update
sudo apt install locales-all sudo apt install locales-all
cmake -E make_directory ${{runner.workspace}}/build cmake -E make_directory ${{runner.workspace}}/build

View file

@ -2,6 +2,9 @@ name: macos
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
build: build:
runs-on: macos-10.15 runs-on: macos-10.15

View file

@ -2,31 +2,34 @@ name: windows
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
build: build:
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
strategy: strategy:
matrix: matrix:
# windows-2016 and windows-2019 have MSVC 2017 and 2019 installed # windows-2019 has MSVC 2019 installed;
# respectively: https://github.com/actions/virtual-environments. # windows-2022 has MSVC 2022 installed:
os: [windows-2016, windows-2019] # https://github.com/actions/virtual-environments.
os: [windows-2019]
platform: [Win32, x64] platform: [Win32, x64]
build_type: [Debug, Release] build_type: [Debug, Release]
standard: [11, 17, 20] standard: [11, 17, 20]
include: include:
- os: windows-2016 - os: windows-2019
platform: Win32 platform: Win32
build_type: Debug build_type: Debug
shared: -DBUILD_SHARED_LIBS=ON shared: -DBUILD_SHARED_LIBS=ON
exclude: - os: windows-2022
- os: windows-2016 platform: x64
platform: Win32 build_type: Debug
- os: windows-2016
standard: 17
- os: windows-2016
standard: 20 standard: 20
exclude:
- os: windows-2019 - os: windows-2019
standard: 11 standard: 11
platform: Win32
- os: windows-2019 - os: windows-2019
standard: 20 standard: 20
platform: Win32 platform: Win32

View file

@ -125,7 +125,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake") "${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
include(cxx14) include(cxx14)
include(CheckCXXCompilerFlag)
include(JoinPaths) include(JoinPaths)
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index) 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}\" %*") ${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
endif () 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) function(add_headers VAR)
set(headers ${${VAR}}) set(headers ${${VAR}})
foreach (header ${ARGN}) foreach (header ${ARGN})
@ -231,7 +218,7 @@ endfunction()
# Define the fmt library, its includes and the needed defines. # 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 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) xchar.h)
if (FMT_MODULE) if (FMT_MODULE)
set(FMT_SOURCES src/fmt.cc) 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_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt) 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) if (FMT_WERROR)
target_compile_options(fmt PRIVATE ${WERROR_FLAG}) target_compile_options(fmt PRIVATE ${WERROR_FLAG})
endif () endif ()
@ -275,6 +251,7 @@ set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
set_target_properties(fmt PROPERTIES set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR} VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
PUBLIC_HEADER "${FMT_HEADERS}"
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}") DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target # 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} install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
LIBRARY DESTINATION ${FMT_LIB_DIR} LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR} ARCHIVE DESTINATION ${FMT_LIB_DIR}
PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt"
FRAMEWORK DESTINATION "."
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
# Use a namespace because CMake provides better diagnostics for namespaced # Use a namespace because CMake provides better diagnostics for namespaced
@ -368,7 +347,6 @@ if (FMT_INSTALL)
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}> install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
DESTINATION ${FMT_LIB_DIR} OPTIONAL) DESTINATION ${FMT_LIB_DIR} OPTIONAL)
install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt")
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif () endif ()

View file

@ -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 <https://godbolt.org/z/G37PTeG3b>`__):
.. code:: c++
#include <array>
#include <fmt/compile.h>
consteval auto compile_time_dtoa(double value) -> std::array<char, 10> {
auto result = std::array<char, 10>();
fmt::format_to(result.data(), FMT_COMPILE("{}"), value);
return result;
}
constexpr auto answer = compile_time_itoa(0.42);
works with the default settings.
* Improved the implementation of
`Dragonbox <https://github.com/jk-jeon/dragonbox>`_, the algorithm used for
the default floating-point formatting
(`#2713 <https://github.com/fmtlib/fmt/pull/2713>`_,
`#2750 <https://github.com/fmtlib/fmt/pull/2750>`_).
Thanks `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_.
* Made ``fmt::to_string`` work with ``__float128``. This uses the internal
FP formatter and works even on system without ``__float128`` support in
``[s]printf``.
* Disabled automatic ``std::ostream`` insertion operator (``operator<<``)
discovery when ``fmt/ostream.h`` is included to prevent ODR violations.
You can get the old behavior by defining ``FMT_DEPRECATED_OSTREAM`` but this
will be removed in the next major release. Use ``fmt::streamed`` or
``fmt::ostream_formatter`` to enable formatting via ``std::ostream`` instead.
* Added ``fmt::ostream_formatter`` that can be used to write ``formatter``
specializations that perform formatting via ``std::ostream``.
For example (`godbolt <https://godbolt.org/z/5sEc5qMsf>`__):
.. code:: c++
#include <fmt/ostream.h>
struct date {
int year, month, day;
friend std::ostream& operator<<(std::ostream& os, const date& d) {
return os << d.year << '-' << d.month << '-' << d.day;
}
};
template <> struct fmt::formatter<date> : ostream_formatter {};
std::string s = fmt::format("The date is {}", date{2012, 12, 9});
// s == "The date is 2012-12-9"
* Added the ``fmt::streamed`` function that takes an object and formats it
via ``std::ostream``.
For example (`godbolt <https://godbolt.org/z/5G3346G1f>`__):
.. code:: c++
#include <thread>
#include <fmt/ostream.h>
int main() {
fmt::print("Current thread id: {}\n",
fmt::streamed(std::this_thread::get_id()));
}
Note that ``fmt/std.h`` provides a ``formatter`` specialization for
``std::thread::id`` so you don't need to format it via ``std::ostream``.
* Deprecated implicit conversions of unscoped enums to integers for consistency
with scoped enums.
* Added an argument-dependent lookup based ``format_as`` extension API to
simplify formatting of enums.
* Added experimental ``std::variant`` formatting support
(`#2941 <https://github.com/fmtlib/fmt/pull/2941>`_).
For example (`godbolt <https://godbolt.org/z/KG9z6cq68>`__):
.. code:: c++
#include <variant>
#include <fmt/std.h>
int main() {
auto v = std::variant<int, std::string>(42);
fmt::print("{}\n", v);
}
prints::
variant(42)
Thanks `@jehelset <https://github.com/jehelset>`_.
* Added experimental ``std::filesystem::path`` formatting support
(`#2865 <https://github.com/fmtlib/fmt/issues/2865>`_,
`#2902 <https://github.com/fmtlib/fmt/pull/2902>`_,
`#2917 <https://github.com/fmtlib/fmt/issues/2917>`_,
`#2918 <https://github.com/fmtlib/fmt/pull/2918>`_).
For example (`godbolt <https://godbolt.org/z/o44dMexEb>`__):
.. code:: c++
#include <filesystem>
#include <fmt/std.h>
int main() {
fmt::print("There is no place like {}.", std::filesystem::path("/home"));
}
prints::
There is no place like "/home".
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Added a ``std::thread::id`` formatter to ``fmt/std.h``.
For example (`godbolt <https://godbolt.org/z/j1azbYf3E>`__):
.. code:: c++
#include <thread>
#include <fmt/std.h>
int main() {
fmt::print("Current thread id: {}\n", std::this_thread::get_id());
}
* Added ``fmt::styled`` that applies a text style to an individual argument
(`#2793 <https://github.com/fmtlib/fmt/pull/2793>`_).
For example (`godbolt <https://godbolt.org/z/vWGW7v5M6>`__):
.. code:: c++
#include <fmt/chrono.h>
#include <fmt/color.h>
int main() {
auto now = std::chrono::system_clock::now();
fmt::print(
"[{}] {}: {}\n",
fmt::styled(now, fmt::emphasis::bold),
fmt::styled("error", fg(fmt::color::red)),
"something went wrong");
}
prints
.. image:: https://user-images.githubusercontent.com/576385/
175071215-12809244-dab0-4005-96d8-7cd911c964d5.png
Thanks `@rbrugo (Riccardo Brugo) <https://github.com/rbrugo>`_.
* Made ``fmt::print`` overload for text styles correctly handle UTF-8
(`#2681 <https://github.com/fmtlib/fmt/issues/2681>`_,
`#2701 <https://github.com/fmtlib/fmt/pull/2701>`_).
Thanks `@AlexGuteniev (Alex Guteniev) <https://github.com/AlexGuteniev>`_.
* Fixed Unicode handling when writing to an ostream.
* Added support for nested specifiers to range formatting
(`#2673 <https://github.com/fmtlib/fmt/pull/2673>`_).
For example (`godbolt <https://godbolt.org/z/xd3Gj38cf>`__):
.. code:: c++
#include <vector>
#include <fmt/ranges.h>
int main() {
fmt::print("{::#x}\n", std::vector{10, 20, 30});
}
prints ``[0xa, 0x14, 0x1e]``.
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
* Implemented escaping of wide strings in ranges
(`#2904 <https://github.com/fmtlib/fmt/pull/2904>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Added support for ranges with ``begin`` / ``end`` found via the
argument-dependent lookup
(`#2807 <https://github.com/fmtlib/fmt/pull/2807>`_).
Thanks `@rbrugo (Riccardo Brugo) <https://github.com/rbrugo>`_.
* Fixed formatting of certain kinds of ranges of ranges
(`#2787 <https://github.com/fmtlib/fmt/pull/2787>`_).
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
* Fixed handling of maps with element types other than ``std::pair``
(`#2944 <https://github.com/fmtlib/fmt/pull/2944>`_).
Thanks `@BrukerJWD (Jonathan W) <https://github.com/BrukerJWD>`_.
* Made tuple formatter enabled only if elements are formattable
(`#2939 <https://github.com/fmtlib/fmt/issues/2939>`_,
`#2940 <https://github.com/fmtlib/fmt/pull/2940>`_).
Thanks `@jehelset <https://github.com/jehelset>`_.
* Made ``fmt::join`` compatible with format string compilation
(`#2719 <https://github.com/fmtlib/fmt/issues/2719>`_,
`#2720 <https://github.com/fmtlib/fmt/pull/2720>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Made compile-time checks work with named arguments of custom types and
``std::ostream`` ``print`` overloads
(`#2816 <https://github.com/fmtlib/fmt/issues/2816>`_,
`#2817 <https://github.com/fmtlib/fmt/issues/2817>`_,
`#2819 <https://github.com/fmtlib/fmt/pull/2819>`_).
Thanks `@timsong-cpp <https://github.com/timsong-cpp>`_.
* Removed ``make_args_checked`` because it is no longer needed for compile-time
checks (`#2760 <https://github.com/fmtlib/fmt/pull/2760>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Removed the following deprecated APIs: ``_format``, ``arg_join``,
the ``format_to`` overload that takes a memory buffer,
``[v]fprintf`` that takes an ``ostream``.
* Removed the deprecated implicit conversion of ``[const] signed char*`` and
``[const] unsigned char*`` to C strings.
* Removed the deprecated ``fmt/locale.h``.
* Replaced the deprecated ``fileno()`` with ``descriptor()`` in
``buffered_file``.
* Moved ``to_string_view`` to the ``detail`` namespace since it's an
implementation detail.
* Made access mode of a created file consistent with ``fopen`` by setting
``S_IWGRP`` and ``S_IWOTH``
(`#2733 <https://github.com/fmtlib/fmt/pull/2733>`_).
Thanks `@arogge (Andreas Rogge) <https://github.com/arogge>`_.
* Removed a redundant buffer resize when formatting to ``std::ostream``
(`#2842 <https://github.com/fmtlib/fmt/issues/2842>`_,
`#2843 <https://github.com/fmtlib/fmt/pull/2843>`_).
Thanks `@jcelerier (Jean-Michaël Celerier) <https://github.com/jcelerier>`_.
* Made precision computation for strings consistent with width
(`#2888 <https://github.com/fmtlib/fmt/issues/2888>`_).
* Fixed handling of locale separators in floating point formatting
(`#2830 <https://github.com/fmtlib/fmt/issues/2830>`_).
* Made sign specifiers work with ``__int128_t``
(`#2773 <https://github.com/fmtlib/fmt/issues/2773>`_).
* Improved support for systems such as CHERI with extra data stored in pointers
(`#2932 <https://github.com/fmtlib/fmt/pull/2932>`_).
Thanks `@davidchisnall (David Chisnall) <https://github.com/davidchisnall>`_.
* Improved documentation
(`#2706 <https://github.com/fmtlib/fmt/pull/2706>`_,
`#2712 <https://github.com/fmtlib/fmt/pull/2712>`_,
`#2789 <https://github.com/fmtlib/fmt/pull/2789>`_,
`#2803 <https://github.com/fmtlib/fmt/pull/2803>`_,
`#2805 <https://github.com/fmtlib/fmt/pull/2805>`_,
`#2815 <https://github.com/fmtlib/fmt/pull/2815>`_,
`#2924 <https://github.com/fmtlib/fmt/pull/2924>`_).
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_,
`@Pokechu22 <https://github.com/Pokechu22>`_,
`@setoye (Alta) <https://github.com/setoye>`_,
`@rtobar <https://github.com/rtobar>`_,
`@rbrugo (Riccardo Brugo) <https://github.com/rbrugo>`_,
`@anoonD (cre) <https://github.com/anoonD>`_,
`@leha-bot (Alex) <https://github.com/leha-bot>`_.
* Improved build configuration
(`#2766 <https://github.com/fmtlib/fmt/pull/2766>`_,
`#2772 <https://github.com/fmtlib/fmt/pull/2772>`_,
`#2836 <https://github.com/fmtlib/fmt/pull/2836>`_,
`#2852 <https://github.com/fmtlib/fmt/pull/2852>`_,
`#2907 <https://github.com/fmtlib/fmt/pull/2907>`_,
`#2913 <https://github.com/fmtlib/fmt/pull/2913>`_,
`#2914 <https://github.com/fmtlib/fmt/pull/2914>`_).
Thanks `@kambala-decapitator (Andrey Filipenkov)
<https://github.com/kambala-decapitator>`_,
`@mattiasljungstrom (Mattias Ljungström)
<https://github.com/mattiasljungstrom>`_,
`@kieselnb (Nick Kiesel) <https://github.com/kieselnb>`_,
`@nathannaveen <https://github.com/nathannaveen>`_,
`@Vertexwahn <https://github.com/Vertexwahn>`_.
* Fixed various warnings and compilation issues
(`#2408 <https://github.com/fmtlib/fmt/issues/2408>`_,
`#2507 <https://github.com/fmtlib/fmt/issues/2507>`_,
`#2697 <https://github.com/fmtlib/fmt/issues/2697>`_,
`#2715 <https://github.com/fmtlib/fmt/issues/2715>`_,
`#2717 <https://github.com/fmtlib/fmt/issues/2717>`_,
`#2722 <https://github.com/fmtlib/fmt/pull/2722>`_,
`#2724 <https://github.com/fmtlib/fmt/pull/2724>`_,
`#2725 <https://github.com/fmtlib/fmt/pull/2725>`_,
`#2726 <https://github.com/fmtlib/fmt/issues/2726>`_,
`#2728 <https://github.com/fmtlib/fmt/pull/2728>`_,
`#2732 <https://github.com/fmtlib/fmt/pull/2732>`_,
`#2738 <https://github.com/fmtlib/fmt/issues/2738>`_,
`#2742 <https://github.com/fmtlib/fmt/pull/2742>`_,
`#2744 <https://github.com/fmtlib/fmt/issues/2744>`_,
`#2745 <https://github.com/fmtlib/fmt/issues/2745>`_,
`#2746 <https://github.com/fmtlib/fmt/issues/2746>`_,
`#2754 <https://github.com/fmtlib/fmt/issues/2754>`_,
`#2755 <https://github.com/fmtlib/fmt/pull/2755>`_,
`#2757 <https://github.com/fmtlib/fmt/issues/2757>`_,
`#2758 <https://github.com/fmtlib/fmt/pull/2758>`_,
`#2761 <https://github.com/fmtlib/fmt/issues/2761>`_,
`#2762 <https://github.com/fmtlib/fmt/pull/2762>`_,
`#2763 <https://github.com/fmtlib/fmt/issues/2763>`_,
`#2765 <https://github.com/fmtlib/fmt/pull/2765>`_,
`#2769 <https://github.com/fmtlib/fmt/issues/2769>`_,
`#2770 <https://github.com/fmtlib/fmt/pull/2770>`_,
`#2771 <https://github.com/fmtlib/fmt/issues/2771>`_,
`#2777 <https://github.com/fmtlib/fmt/issues/2777>`_,
`#2779 <https://github.com/fmtlib/fmt/pull/2779>`_,
`#2782 <https://github.com/fmtlib/fmt/pull/2782>`_,
`#2783 <https://github.com/fmtlib/fmt/pull/2783>`_,
`#2794 <https://github.com/fmtlib/fmt/issues/2794>`_,
`#2796 <https://github.com/fmtlib/fmt/issues/2796>`_,
`#2797 <https://github.com/fmtlib/fmt/pull/2797>`_,
`#2801 <https://github.com/fmtlib/fmt/pull/2801>`_,
`#2802 <https://github.com/fmtlib/fmt/pull/2802>`_,
`#2808 <https://github.com/fmtlib/fmt/issues/2808>`_,
`#2818 <https://github.com/fmtlib/fmt/issues/2818>`_,
`#2819 <https://github.com/fmtlib/fmt/pull/2819>`_,
`#2829 <https://github.com/fmtlib/fmt/issues/2829>`_,
`#2835 <https://github.com/fmtlib/fmt/issues/2835>`_,
`#2848 <https://github.com/fmtlib/fmt/issues/2848>`_,
`#2860 <https://github.com/fmtlib/fmt/issues/2860>`_,
`#2861 <https://github.com/fmtlib/fmt/pull/2861>`_,
`#2882 <https://github.com/fmtlib/fmt/pull/2882>`_,
`#2886 <https://github.com/fmtlib/fmt/issues/2886>`_,
`#2891 <https://github.com/fmtlib/fmt/issues/2891>`_,
`#2892 <https://github.com/fmtlib/fmt/pull/2892>`_,
`#2895 <https://github.com/fmtlib/fmt/issues/2895>`_,
`#2896 <https://github.com/fmtlib/fmt/issues/2896>`_,
`#2903 <https://github.com/fmtlib/fmt/pull/2903>`_,
`#2906 <https://github.com/fmtlib/fmt/issues/2906>`_,
`#2908 <https://github.com/fmtlib/fmt/issues/2908>`_,
`#2909 <https://github.com/fmtlib/fmt/pull/2909>`_,
`#2920 <https://github.com/fmtlib/fmt/issues/2920>`_,
`#2922 <https://github.com/fmtlib/fmt/pull/2922>`_,
`#2927 <https://github.com/fmtlib/fmt/pull/2927>`_,
`#2929 <https://github.com/fmtlib/fmt/pull/2929>`_,
`#2936 <https://github.com/fmtlib/fmt/issues/2936>`_,
`#2937 <https://github.com/fmtlib/fmt/pull/2937>`_,
`#2938 <https://github.com/fmtlib/fmt/pull/2938>`_,
`#2951 <https://github.com/fmtlib/fmt/pull/2951>`_,
`#2954 <https://github.com/fmtlib/fmt/issues/2954>`_,
`#2957 <https://github.com/fmtlib/fmt/pull/2957>`_,
`#2958 <https://github.com/fmtlib/fmt/issues/2958>`_,
`#2960 <https://github.com/fmtlib/fmt/pull/2960>`_).
Thanks `@matrackif <https://github.com/matrackif>`_
`@Tobi823 (Tobias Hellmann) <https://github.com/Tobi823>`_,
`@ivan-volnov (Ivan Volnov) <https://github.com/ivan-volnov>`_,
`@VasiliPupkin256 <https://github.com/VasiliPupkin256>`_,
`@federico-busato (Federico) <https://github.com/federico-busato>`_,
`@barcharcraz (Charlie Barto) <https://github.com/barcharcraz>`_,
`@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_,
`@HazardyKnusperkeks (Björn Schäpers)
<https://github.com/HazardyKnusperkeks>`_,
`@dalboris (Boris Dalstein) <https://github.com/dalboris>`_,
`@seanm (Sean McBride) <https://github.com/seanm>`_,
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@timsong-cpp <https://github.com/timsong-cpp>`_,
`@seanm (Sean McBride) <https://github.com/seanm>`_,
`@frithrah <https://github.com/frithrah>`_,
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
`@Agga <https://github.com/Agga>`_,
`@madmaxoft (Mattes D) <https://github.com/madmaxoft>`_,
`@JurajX (Juraj) <https://github.com/JurajX>`_,
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_,
`@Dani-Hub (Daniel Krügler) <https://github.com/Dani-Hub>`_.
8.1.1 - 2022-01-06 8.1.1 - 2022-01-06
------------------ ------------------
@ -6,7 +392,7 @@
`#2696 <https://github.com/fmtlib/fmt/pull/2696>`_). `#2696 <https://github.com/fmtlib/fmt/pull/2696>`_).
Thanks `@saraedum (Julian Rüth) <https://github.com/saraedum>`_. Thanks `@saraedum (Julian Rüth) <https://github.com/saraedum>`_.
* Fixed chorno formatting on big endian systems * Fixed chrono formatting on big endian systems
(`#2698 <https://github.com/fmtlib/fmt/issues/2698>`_, (`#2698 <https://github.com/fmtlib/fmt/issues/2698>`_,
`#2699 <https://github.com/fmtlib/fmt/pull/2699>`_). `#2699 <https://github.com/fmtlib/fmt/pull/2699>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_ and Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_ and
@ -148,6 +534,10 @@
[" ["
aan"] aan"]
* Added an experimental ``?`` specifier for escaping strings.
(`#2674 <https://github.com/fmtlib/fmt/pull/2674>`_).
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
* Switched to JSON-like representation of maps and sets for consistency with * Switched to JSON-like representation of maps and sets for consistency with
Python's ``str.format``. Python's ``str.format``.
For example (`godbolt <https://godbolt.org/z/seKjoY9W5>`__): For example (`godbolt <https://godbolt.org/z/seKjoY9W5>`__):
@ -367,7 +757,8 @@
`@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_, `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_,
`@andrewcorrigan (Andrew Corrigan) <https://github.com/andrewcorrigan>`_, `@andrewcorrigan (Andrew Corrigan) <https://github.com/andrewcorrigan>`_,
`@lucpelletier <https://github.com/lucpelletier>`_, `@lucpelletier <https://github.com/lucpelletier>`_,
`@HazardyKnusperkeks (Björn Schäpers) <https://github.com/HazardyKnusperkeks>`_. `@HazardyKnusperkeks (Björn Schäpers)
<https://github.com/HazardyKnusperkeks>`_.
8.0.1 - 2021-07-02 8.0.1 - 2021-07-02
------------------ ------------------

View file

@ -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 .. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux :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 **{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams. alternative to C stdio and C++ iostreams.
If you like this project, please consider donating to the BYSOL If you like this project, please consider donating to one of the funds that
Foundation that helps victims of political repressions in Belarus: help victims of the war in Ukraine: https://www.stopputin.net/.
https://bysol.org/en/bs/general/.
`Documentation <https://fmt.dev>`__ `Documentation <https://fmt.dev>`__
`Cheat Sheets <https://hackingcpp.com/cpp/libs/fmt.html>`__
Q&A: ask questions on `StackOverflow with the tag fmt Q&A: ask questions on `StackOverflow with the tag fmt
<https://stackoverflow.com/questions/tagged/fmt>`_. <https://stackoverflow.com/questions/tagged/fmt>`_.
@ -123,7 +126,7 @@ Output::
Default format: 42s 100ms Default format: 42s 100ms
strftime-like format: 03:15:30 strftime-like format: 03:15:30
**Print a container** (`run <https://godbolt.org/z/MjsY7c>`_) **Print a container** (`run <https://godbolt.org/z/MxM1YqjE7>`_)
.. code:: c++ .. code:: c++
@ -341,9 +344,12 @@ Projects using this library
* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library * `Folly <https://github.com/facebook/folly>`_: Facebook open-source library
* `GemRB <https://gemrb.org/>`_: a portable open-source implementation of
Bioware’s Infinity Engine
* `Grand Mountain Adventure * `Grand Mountain Adventure
<https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/>`_: <https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/>`_:
A beautiful open-world ski & snowboarding game a beautiful open-world ski & snowboarding game
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_: * `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
Player vs Player Gaming Network with tweaks Player vs Player Gaming Network with tweaks

View file

@ -12,6 +12,7 @@ The {fmt} library API consists of the following parts:
formatting functions and locale support formatting functions and locale support
* :ref:`fmt/ranges.h <ranges-api>`: formatting of ranges and tuples * :ref:`fmt/ranges.h <ranges-api>`: formatting of ranges and tuples
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting * :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
* :ref:`fmt/std.h <std-api>`: formatters for standard library types
* :ref:`fmt/compile.h <compile-api>`: format string compilation * :ref:`fmt/compile.h <compile-api>`: format string compilation
* :ref:`fmt/color.h <color-api>`: terminal color and text style * :ref:`fmt/color.h <color-api>`: terminal color and text style
* :ref:`fmt/os.h <os-api>`: system APIs * :ref:`fmt/os.h <os-api>`: 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<T...> fmt, T&&... args) .. doxygenfunction:: print(std::FILE *f, format_string<T...> fmt, T&&... args)
.. doxygenfunction:: vprint(std::FILE *f, string_view fmt, format_args 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 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 <typename S, typename... Args> template <typename S, typename... Args>
void log(const char* file, int line, const S& format, Args&&... args) { void log(const char* file, int line, const S& format, Args&&... args) {
vlog(file, line, format, vlog(file, line, format, fmt::make_format_args(args...));
fmt::make_args_checked<Args...>(format, args...));
} }
#define MY_LOG(format, ...) \ #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 Note that ``vlog`` is not parameterized on argument types which improves compile
times and reduces binary code size compared to a fully parameterized version. times and reduces binary code size compared to a fully parameterized version.
.. doxygenfunction:: fmt::make_args_checked(const S&, const remove_reference_t<Args>&...)
.. doxygenfunction:: fmt::make_format_args(const Args&...) .. doxygenfunction:: fmt::make_format_args(const Args&...)
.. doxygenclass:: fmt::format_arg_store .. 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 .. doxygenclass:: fmt::basic_format_arg
:members: :members:
.. doxygenclass:: fmt::basic_format_parse_context
:members:
.. doxygenclass:: fmt::basic_format_context .. doxygenclass:: fmt::basic_format_context
:members: :members:
@ -179,9 +180,15 @@ functions and locale support.
.. _udt: .. _udt:
Formatting User-defined Types Formatting User-Defined Types
----------------------------- -----------------------------
The {fmt} library provides formatters for many standard C++ types.
See :ref:`fmt/ranges.h <ranges-api>` for ranges and tuples including standard
containers such as ``std::vector``, :ref:`fmt/chrono.h <chrono-api>` for date
and time formatting and :ref:`fmt/std.h <std-api>` for path and variant
formatting.
To make a user-defined type formattable, specialize the ``formatter<T>`` struct To make a user-defined type formattable, specialize the ``formatter<T>`` struct
template and implement ``parse`` and ``format`` methods:: template and implement ``parse`` and ``format`` methods::
@ -208,6 +215,10 @@ template and implement ``parse`` and ``format`` methods::
// the formatter should parse the 'f' specifier and return an iterator // the formatter should parse the 'f' specifier and return an iterator
// pointing to '}'. // 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: // Parse the presentation format and store it in the formatter:
auto it = ctx.begin(), end = ctx.end(); auto it = ctx.begin(), end = ctx.end();
if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++; if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++;
@ -222,11 +233,11 @@ template and implement ``parse`` and ``format`` methods::
// Formats the point p using the parsed format specification (presentation) // Formats the point p using the parsed format specification (presentation)
// stored in this formatter. // stored in this formatter.
template <typename FormatContext> template <typename FormatContext>
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. // ctx.out() is an output iterator to write to.
return presentation == 'f' return presentation == 'f'
? format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y) ? fmt::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(), "({:.1e}, {:.1e})", p.x, p.y);
} }
}; };
@ -244,7 +255,7 @@ example::
template <> struct fmt::formatter<color>: formatter<string_view> { template <> struct fmt::formatter<color>: formatter<string_view> {
// parse is inherited from formatter<string_view>. // parse is inherited from formatter<string_view>.
template <typename FormatContext> template <typename FormatContext>
auto format(color c, FormatContext& ctx) { auto format(color c, FormatContext& ctx) const {
string_view name = "unknown"; string_view name = "unknown";
switch (c) { switch (c) {
case color::red: name = "red"; break; case color::red: name = "red"; break;
@ -282,7 +293,7 @@ You can also write a formatter for a hierarchy of classes::
struct fmt::formatter<T, std::enable_if_t<std::is_base_of<A, T>::value, char>> : struct fmt::formatter<T, std::enable_if_t<std::is_base_of<A, T>::value, char>> :
fmt::formatter<std::string> { fmt::formatter<std::string> {
template <typename FormatCtx> template <typename FormatCtx>
auto format(const A& a, FormatCtx& ctx) { auto format(const A& a, FormatCtx& ctx) const {
return fmt::formatter<std::string>::format(a.name(), ctx); return fmt::formatter<std::string>::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 to a formattable type, the specialization takes precedence over the
conversion. conversion.
.. doxygenclass:: fmt::basic_format_parse_context For enums {fmt} also provides the ``format_as`` extension API. To format an enum
:members: 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 <fmt/format.h>
namespace kevin_namespacy {
enum class film {
house_of_cards, american_beauty, se7en = 7
};
auto format_as(film f) { return fmt::underlying(f); }
}
int main() {
fmt::print("{}\n", kevin_namespacy::film::se7en); // prints "7"
}
Literal-Based API
----------------- -----------------
The following user-defined literals are defined in ``fmt/format.h``. The following user-defined literals are defined in ``fmt/format.h``.
.. doxygenfunction:: operator""_format(const char *s, size_t n) -> detail::udl_formatter<char> .. doxygenfunction:: operator""_a()
.. doxygenfunction:: operator""_a(const char *s, size_t) -> detail::udl_arg<char>
Utilities Utilities
--------- ---------
@ -316,9 +342,9 @@ Utilities
.. doxygenfunction:: fmt::ptr(const std::unique_ptr<T> &p) -> const void* .. doxygenfunction:: fmt::ptr(const std::unique_ptr<T> &p) -> const void*
.. doxygenfunction:: fmt::ptr(const std::shared_ptr<T> &p) -> const void* .. doxygenfunction:: fmt::ptr(const std::shared_ptr<T> &p) -> const void*
.. doxygenfunction:: fmt::to_string(const T &value) -> std::string .. doxygenfunction:: fmt::underlying(Enum e) -> typename std::underlying_type<Enum>::type
.. doxygenfunction:: fmt::to_string_view(const Char *s) -> basic_string_view<Char> .. doxygenfunction:: fmt::to_string(const T &value) -> std::string
.. doxygenfunction:: fmt::join(Range &&range, string_view sep) -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>> .. doxygenfunction:: fmt::join(Range &&range, string_view sep) -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>>
@ -381,8 +407,8 @@ non-default floating-point formatting that occasionally falls back on
.. _ranges-api: .. _ranges-api:
Ranges and Tuple Formatting Range and Tuple Formatting
=========================== ==========================
The library also supports convenient formatting of ranges and tuples:: 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) .. doxygenfunction:: gmtime(std::time_t time)
.. _std-api:
Standard Library Types Formatting
=================================
``fmt/std.h`` provides formatters for:
* `std::filesystem::path <std::filesystem::path>`_
* `std::thread::id <https://en.cppreference.com/w/cpp/thread/thread/id>`_
* `std::monostate <https://en.cppreference.com/w/cpp/utility/variant/monostate>`_
* `std::variant <https://en.cppreference.com/w/cpp/utility/variant/variant>`_
Formatting Variants
-------------------
A ``std::variant`` is only formattable if every variant alternative is formattable, and requires the
``__cpp_lib_variant`` `library feature <https://en.cppreference.com/w/cpp/feature_test>`_.
**Example**::
#include <fmt/std.h>
std::variant<char, float> v0{'x'};
// Prints "variant('x')"
fmt::print("{}", v0);
std::variant<std::monostate, char> v1;
// Prints "variant(monostate)"
.. _compile-api: .. _compile-api:
Format string compilation Format String Compilation
========================= =========================
``fmt/compile.h`` provides format string compilation support when using ``fmt/compile.h`` provides format string compilation enabled via the
``FMT_COMPILE``. Format strings are parsed, checked and converted into efficient ``FMT_COMPILE`` macro or the ``_cf`` user-defined literal. Format strings
formatting code at compile-time. This supports arguments of built-in and string marked with ``FMT_COMPILE`` or ``_cf`` are parsed, checked and converted into
types as well as user-defined types with ``constexpr`` ``parse`` functions in efficient formatting code at compile-time. This supports arguments of built-in
their ``formatter`` specializations. Format string compilation can generate more and string types as well as user-defined types with ``constexpr`` ``parse``
binary code compared to the default API and is only recommended in places where functions in their ``formatter`` specializations. Format string compilation can
formatting is a performance bottleneck. generate more binary code compared to the default API and is only recommended in
places where formatting is a performance bottleneck.
.. doxygendefine:: FMT_COMPILE .. doxygendefine:: FMT_COMPILE
.. doxygenfunction:: operator""_cf()
.. _color-api: .. _color-api:
Terminal color and text style Terminal Color and Text Style
============================= =============================
``fmt/color.h`` provides support for terminal color and text style output. ``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:: bg(detail::color_type)
.. doxygenfunction:: styled(const T& value, text_style ts)
.. _os-api: .. _os-api:
System APIs System APIs
@ -486,27 +546,28 @@ System APIs
======================== ========================
``fmt/ostream.h`` provides ``std::ostream`` support including formatting of ``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 <fmt/ostream.h> #include <fmt/ostream.h>
class date { struct date {
int year_, month_, day_; int year, month, day;
public:
date(int year, int month, int day): year_(year), month_(month), day_(day) {}
friend std::ostream& operator<<(std::ostream& os, const date& d) { 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<date> : ostream_formatter {};
std::string s = fmt::format("The date is {}", date{2012, 12, 9});
// s == "The date is 2012-12-9" // s == "The date is 2012-12-9"
{fmt} only supports insertion operators that are defined in the same namespaces .. doxygenfunction:: streamed(const T &)
as the types they format and can be found with the argument-dependent lookup.
.. doxygenfunction:: print(std::basic_ostream<Char> &os, const S &format_str, Args&&... args) .. doxygenfunction:: print(std::ostream &os, format_string<T...> fmt, T&&... args)
.. _printf-api: .. _printf-api:

View file

@ -4,7 +4,7 @@
import errno, os, re, sys import errno, os, re, sys
from subprocess import check_call, CalledProcessError, Popen, PIPE, STDOUT 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: class Pip:
def __init__(self, venv_dir): def __init__(self, venv_dir):
@ -28,6 +28,9 @@ def create_build_env(venv_dir='virtualenv'):
pip.install('six') pip.install('six')
# See: https://github.com/sphinx-doc/sphinx/issues/9777 # See: https://github.com/sphinx-doc/sphinx/issues/9777
pip.install('docutils==0.17.1') 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('sphinx-doc/sphinx', 'v3.3.0')
pip.install('michaeljones/breathe', 'v4.25.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_RVALUE_REFERENCES=1 \
FMT_USE_USER_DEFINED_LITERALS=1 \ FMT_USE_USER_DEFINED_LITERALS=1 \
FMT_USE_ALIAS_TEMPLATES=1 \ FMT_USE_ALIAS_TEMPLATES=1 \
FMT_USE_NONTYPE_TEMPLATE_ARGS=1 \
FMT_API= \ FMT_API= \
"FMT_BEGIN_NAMESPACE=namespace fmt {{" \ "FMT_BEGIN_NAMESPACE=namespace fmt {{" \
"FMT_END_NAMESPACE=}}" \ "FMT_END_NAMESPACE=}}" \

View file

@ -101,7 +101,7 @@ The code
format(FMT_STRING("The answer is {:d}"), "forty-two"); format(FMT_STRING("The answer is {:d}"), "forty-two");
reports a compile-time error on compilers that support relaxed ``constexpr``. reports a compile-time error on compilers that support relaxed ``constexpr``.
See `here <api.html#c.fmt>`_ for details. See `here <api.html#compile-time-format-string-checks>`_ for details.
The following code The following code

View file

@ -304,7 +304,8 @@ The available presentation types for pointers are:
Chrono Format Specifications 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 .. productionlist:: sf
chrono_format_spec: [[`fill`]`align`][`width`]["." `precision`][`chrono_specs`] chrono_format_spec: [[`fill`]`align`][`width`]["." `precision`][`chrono_specs`]
@ -345,12 +346,38 @@ points are:
| | command ``%OS`` produces the locale's alternative representation. | | | 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. are valid only for ``std::tm`` and not durations or time points.
``std::tm`` uses the system's `strftime .. range-specs:
<https://en.cppreference.com/w/cpp/chrono/c/strftime>`_ so refer to its
documentation for details on supported conversion specifiers. 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: .. _formatexamples:

View file

@ -95,10 +95,10 @@ class dynamic_format_arg_store
}; };
template <typename T> template <typename T>
using stored_type = conditional_t<detail::is_string<T>::value && using stored_type = conditional_t<
!has_formatter<T, Context>::value && std::is_convertible<T, std::basic_string<char_type>>::value &&
!detail::is_reference_wrapper<T>::value, !detail::is_reference_wrapper<T>::value,
std::basic_string<char_type>, T>; std::basic_string<char_type>, T>;
// Storage of basic_format_arg must be contiguous. // Storage of basic_format_arg must be contiguous.
std::vector<basic_format_arg<Context>> data_; std::vector<basic_format_arg<Context>> data_;

View file

@ -10,6 +10,8 @@
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
#include <cmath> // std::isfinite
#include <cstring> // std::memcpy
#include <ctime> #include <ctime>
#include <iterator> #include <iterator>
#include <locale> #include <locale>
@ -321,14 +323,13 @@ constexpr const size_t codecvt_result<CodeUnit>::max_size;
template <typename CodeUnit> template <typename CodeUnit>
void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf, void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
const std::locale& loc) { const std::locale& loc) {
using codecvt = std::codecvt<CodeUnit, char, std::mbstate_t>;
#if FMT_CLANG_VERSION #if FMT_CLANG_VERSION
# pragma clang diagnostic push # pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated" # pragma clang diagnostic ignored "-Wdeprecated"
auto& f = std::use_facet<codecvt>(loc); auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
# pragma clang diagnostic pop # pragma clang diagnostic pop
#else #else
auto& f = std::use_facet<codecvt>(loc); auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
#endif #endif
auto mb = std::mbstate_t(); auto mb = std::mbstate_t();
const char* from_next = nullptr; 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()) { if (detail::is_utf8() && loc != get_classic_locale()) {
// char16_t and char32_t codecvts are broken in MSVC (linkage errors) and // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
// gcc-4. // gcc-4.
#if FMT_MSC_VER != 0 || \ #if FMT_MSC_VERSION != 0 || \
(defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
// The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
// and newer. // and newer.
@ -468,7 +469,7 @@ inline std::tm localtime(std::time_t time) {
bool fallback(int res) { return res == 0; } bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER #if !FMT_MSC_VERSION
bool fallback(detail::null<>) { bool fallback(detail::null<>) {
using namespace fmt::detail; using namespace fmt::detail;
std::tm* tm = std::localtime(&time_); 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; } bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER #if !FMT_MSC_VERSION
bool fallback(detail::null<>) { bool fallback(detail::null<>) {
std::tm* tm = std::gmtime(&time_); std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm; 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; constexpr const size_t len = 8;
if (const_check(is_big_endian())) { if (const_check(is_big_endian())) {
char tmp[len]; char tmp[len];
memcpy(tmp, &digits, len); std::memcpy(tmp, &digits, len);
std::reverse_copy(tmp, tmp + len, buf); std::reverse_copy(tmp, tmp + len, buf);
} else { } else {
memcpy(buf, &digits, len); std::memcpy(buf, &digits, len);
} }
} }
@ -1214,7 +1215,7 @@ template <typename OutputIt, typename Char> class tm_writer {
char buf[10]; char buf[10];
size_t offset = 0; size_t offset = 0;
if (year >= 0 && year < 10000) { if (year >= 0 && year < 10000) {
copy2(buf, digits2(to_unsigned(year / 100))); copy2(buf, digits2(static_cast<size_t>(year / 100)));
} else { } else {
offset = 4; offset = 4;
write_year_extended(year); write_year_extended(year);
@ -1387,15 +1388,6 @@ struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
FMT_CONSTEXPR void on_duration_unit() {} FMT_CONSTEXPR void on_duration_unit() {}
}; };
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline bool isnan(T) {
return false;
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
inline bool isnan(T value) {
return std::isnan(value);
}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline bool isfinite(T) { inline bool isfinite(T) {
return true; return true;
@ -1470,14 +1462,22 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
#endif #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 // C++20 spec. If more than 18 fractional digits are required then returns 6 for
// microseconds precision. // microseconds precision.
constexpr int count_fractional_digits(long long num, long long den, int n = 0) { template <long long Num, long long Den, int N = 0,
return num % den == 0 bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
? n struct count_fractional_digits {
: (n > 18 ? 6 : count_fractional_digits(num * 10, den, n + 1)); static constexpr int value =
} Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
};
// Base case that doesn't instantiate any more templates
// in order to avoid overflow.
template <long long Num, long long Den, int N>
struct count_fractional_digits<Num, Den, N, false> {
static constexpr int value = (Num % Den == 0) ? N : 6;
};
constexpr long long pow10(std::uint32_t n) { constexpr long long pow10(std::uint32_t n) {
return n == 0 ? 1 : 10 * pow10(n - 1); return n == 0 ? 1 : 10 * pow10(n - 1);
@ -1663,9 +1663,11 @@ struct chrono_formatter {
out = format_decimal<char_type>(out, n, num_digits).end; out = format_decimal<char_type>(out, n, num_digits).end;
} }
template <class Duration> void write_fractional_seconds(Duration d) { template <typename Duration> void write_fractional_seconds(Duration d) {
FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
constexpr auto num_fractional_digits = constexpr auto num_fractional_digits =
count_fractional_digits(Duration::period::num, Duration::period::den); count_fractional_digits<Duration::period::num,
Duration::period::den>::value;
using subsecond_precision = std::chrono::duration< using subsecond_precision = std::chrono::duration<
typename std::common_type<typename Duration::rep, typename std::common_type<typename Duration::rep,
@ -1674,12 +1676,9 @@ struct chrono_formatter {
if (std::ratio_less<typename subsecond_precision::period, if (std::ratio_less<typename subsecond_precision::period,
std::chrono::seconds::period>::value) { std::chrono::seconds::period>::value) {
*out++ = '.'; *out++ = '.';
// Don't convert long double to integer seconds to avoid overflow. auto fractional =
using sec = conditional_t< detail::abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d);
std::is_same<typename Duration::rep, long double>::value, auto subseconds =
std::chrono::duration<long double>, std::chrono::seconds>;
auto fractional = detail::abs(d) - std::chrono::duration_cast<sec>(d);
const auto subseconds =
std::chrono::treat_as_floating_point< std::chrono::treat_as_floating_point<
typename subsecond_precision::rep>::value typename subsecond_precision::rep>::value
? fractional.count() ? fractional.count()
@ -1770,8 +1769,22 @@ struct chrono_formatter {
if (handle_nan_inf()) return; if (handle_nan_inf()) return;
if (ns == numeric_system::standard) { if (ns == numeric_system::standard) {
write(second(), 2); if (std::is_floating_point<rep>::value) {
write_fractional_seconds(std::chrono::duration<rep, Period>{val}); constexpr auto num_fractional_digits =
count_fractional_digits<Period::num, Period::den>::value;
auto buf = memory_buffer();
format_to(std::back_inserter(buf), runtime("{:.{}f}"),
std::fmod(val * static_cast<rep>(Period::num) /
static_cast<rep>(Period::den),
60),
num_fractional_digits);
if (negative) *out++ = '-';
if (buf.size() < 2 || buf[1] == '.') *out++ = '0';
out = std::copy(buf.begin(), buf.end(), out);
} else {
write(second(), 2);
write_fractional_seconds(std::chrono::duration<rep, Period>(val));
}
return; return;
} }
auto time = tm(); auto time = tm();

View file

@ -10,13 +10,6 @@
#include "format.h" #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_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN 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. // color is a struct of either a rgb color or a terminal color.
struct color_type { struct color_type {
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} {
value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color); value.rgb_color = static_cast<uint32_t>(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<uint32_t>(rgb_color.r) << 16) | value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b; (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
} }
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), FMT_CONSTEXPR color_type(terminal_color term_color) noexcept
value{} { : is_rgb(), value{} {
value.term_color = static_cast<uint8_t>(term_color); value.term_color = static_cast<uint8_t>(term_color);
} }
bool is_rgb; bool is_rgb;
@ -239,10 +231,8 @@ FMT_END_DETAIL_NAMESPACE
/** A text style consisting of foreground and background colors and emphasis. */ /** A text style consisting of foreground and background colors and emphasis. */
class text_style { class text_style {
public: public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
: set_foreground_color(), : set_foreground_color(), set_background_color(), ems(em) {}
set_background_color(),
ems(em) {}
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
if (!set_foreground_color) { if (!set_foreground_color) {
@ -273,44 +263,32 @@ class text_style {
return lhs |= rhs; return lhs |= rhs;
} }
FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=( FMT_CONSTEXPR bool has_foreground() const noexcept {
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 {
return set_foreground_color; return set_foreground_color;
} }
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { FMT_CONSTEXPR bool has_background() const noexcept {
return set_background_color; return set_background_color;
} }
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { FMT_CONSTEXPR bool has_emphasis() const noexcept {
return static_cast<uint8_t>(ems) != 0; return static_cast<uint8_t>(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"); FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color; 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"); FMT_ASSERT(has_background(), "no background specified for this style");
return background_color; 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"); FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems; return ems;
} }
private: private:
FMT_CONSTEXPR text_style(bool is_foreground, FMT_CONSTEXPR text_style(bool is_foreground,
detail::color_type text_color) FMT_NOEXCEPT detail::color_type text_color) noexcept
: set_foreground_color(), : set_foreground_color(), set_background_color(), ems() {
set_background_color(),
ems() {
if (is_foreground) { if (is_foreground) {
foreground_color = text_color; foreground_color = text_color;
set_foreground_color = true; set_foreground_color = true;
@ -320,36 +298,9 @@ class text_style {
} }
} }
// DEPRECATED! friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept;
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;
}
if (!set_background_color) { friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept;
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<emphasis>(static_cast<uint8_t>(ems) &
static_cast<uint8_t>(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;
detail::color_type foreground_color; detail::color_type foreground_color;
detail::color_type background_color; detail::color_type background_color;
@ -359,17 +310,16 @@ class text_style {
}; };
/** Creates a text style from the foreground (text) color. */ /** 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); return text_style(true, foreground);
} }
/** Creates a text style from the background color. */ /** 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); return text_style(false, background);
} }
FMT_CONSTEXPR inline text_style operator|(emphasis lhs, FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs; return text_style(lhs) | rhs;
} }
@ -377,7 +327,7 @@ FMT_BEGIN_DETAIL_NAMESPACE
template <typename Char> struct ansi_color_escape { template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, 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 // If we have a terminal color, we need to output another escape code
// sequence. // sequence.
if (!text_color.is_rgb) { if (!text_color.is_rgb) {
@ -412,7 +362,7 @@ template <typename Char> struct ansi_color_escape {
to_esc(color.b, buffer + 15, 'm'); to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0); buffer[19] = static_cast<Char>(0);
} }
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
uint8_t em_codes[num_emphases] = {}; uint8_t em_codes[num_emphases] = {};
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2; if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
@ -433,10 +383,10 @@ template <typename Char> struct ansi_color_escape {
} }
buffer[index++] = static_cast<Char>(0); buffer[index++] = static_cast<Char>(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 const Char* begin() const noexcept { return buffer; }
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT { FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept {
return buffer + std::char_traits<Char>::length(buffer); return buffer + std::char_traits<Char>::length(buffer);
} }
@ -445,59 +395,64 @@ template <typename Char> struct ansi_color_escape {
Char buffer[7u + 3u * num_emphases + 1u]; Char buffer[7u + 3u * num_emphases + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) FMT_NOEXCEPT { char delimiter) noexcept {
out[0] = static_cast<Char>('0' + c / 100); out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10); out[1] = static_cast<Char>('0' + c / 10 % 10);
out[2] = static_cast<Char>('0' + c % 10); out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter); out[3] = static_cast<Char>(delimiter);
} }
static FMT_CONSTEXPR bool has_emphasis(emphasis em, static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept {
emphasis mask) FMT_NOEXCEPT {
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask); return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
} }
}; };
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color( FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
detail::color_type foreground) FMT_NOEXCEPT { detail::color_type foreground) noexcept {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;"); return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color( FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
detail::color_type background) FMT_NOEXCEPT { detail::color_type background) noexcept {
return ansi_color_escape<Char>(background, "\x1b[48;2;"); return ansi_color_escape<Char>(background, "\x1b[48;2;");
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT { FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
return ansi_color_escape<Char>(em); return ansi_color_escape<Char>(em);
} }
template <typename Char> template <typename Char> inline void fputs(const Char* chars, FILE* stream) {
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { int result = std::fputs(chars, stream);
std::fputs(chars, stream); if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
} }
template <> template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) {
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { int result = std::fputws(chars, stream);
std::fputws(chars, stream); if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
} }
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT { template <typename Char> inline void reset_color(FILE* stream) {
fputs("\x1b[0m", stream); fputs("\x1b[0m", stream);
} }
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT { template <> inline void reset_color<wchar_t>(FILE* stream) {
fputs(L"\x1b[0m", stream); fputs(L"\x1b[0m", stream);
} }
template <typename Char> template <typename Char> inline void reset_color(buffer<Char>& buffer) {
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
auto reset_color = string_view("\x1b[0m"); auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end()); buffer.append(reset_color.begin(), reset_color.end());
} }
template <typename T> struct styled_arg {
const T& value;
text_style style;
};
template <typename Char> template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts, void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str, basic_string_view<Char> format_str,
@ -528,9 +483,13 @@ template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format, void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format), args); detail::vformat_to(buf, ts, detail::to_string_view(format), args);
buf.push_back(Char(0)); if (detail::is_utf8()) {
detail::fputs(buf.data(), f); detail::print(f, basic_string_view<Char>(buf.begin(), buf.size()));
} else {
buf.push_back(Char(0));
detail::fputs(buf.data(), f);
}
} }
/** /**
@ -549,7 +508,7 @@ template <typename S, typename... Args,
void print(std::FILE* f, const text_style& ts, const S& format_str, void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) { const Args&... args) {
vprint(f, ts, format_str, vprint(f, ts, format_str,
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_format_args<buffer_context<char_t<S>>>(args...));
} }
/** /**
@ -574,7 +533,7 @@ inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str, const text_style& ts, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> 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); return fmt::to_string(buf);
} }
@ -593,8 +552,8 @@ inline std::basic_string<Char> vformat(
template <typename S, typename... Args, typename Char = char_t<S>> template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str, inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) { const Args&... args) {
return fmt::vformat(ts, to_string_view(format_str), return fmt::vformat(ts, detail::to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_format_args<buffer_context<Char>>(args...));
} }
/** /**
@ -628,8 +587,62 @@ template <typename OutputIt, typename S, typename... Args,
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) -> Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type { typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, to_string_view(format_str), return vformat_to(out, ts, detail::to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_format_args<buffer_context<char_t<S>>>(args...));
}
template <typename T, typename Char>
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
template <typename FormatContext>
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
-> decltype(ctx.out()) {
const auto& ts = arg.style;
const auto& value = arg.value;
auto out = ctx.out();
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
out = std::copy(emphasis.begin(), emphasis.end(), out);
}
if (ts.has_foreground()) {
has_style = true;
auto foreground =
detail::make_foreground_color<Char>(ts.get_foreground());
out = std::copy(foreground.begin(), foreground.end(), out);
}
if (ts.has_background()) {
has_style = true;
auto background =
detail::make_background_color<Char>(ts.get_background());
out = std::copy(background.begin(), background.end(), out);
}
out = formatter<T, Char>::format(value, ctx);
if (has_style) {
auto reset_color = string_view("\x1b[0m");
out = std::copy(reset_color.begin(), reset_color.end(), out);
}
return out;
}
};
/**
\rst
Returns an argument that will be formatted using ANSI escape sequences,
to be used in a formatting function.
**Example**::
fmt::print("Elapsed time: {s:.2f} seconds",
fmt::styled(1.23, fmt::fg(fmt::color::green) |
fmt::bg(fmt::color::blue)));
\endrst
*/
template <typename T>
FMT_CONSTEXPR auto styled(const T& value, text_style ts)
-> detail::styled_arg<remove_cvref_t<T>> {
return detail::styled_arg<remove_cvref_t<T>>{value, ts};
} }
FMT_MODULE_EXPORT_END FMT_MODULE_EXPORT_END

View file

@ -13,45 +13,6 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { 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 <typename T> void operator=(const T&) {}
};
counting_iterator() : count_(0) {}
size_t count() const { return count_; }
counting_iterator& operator++() {
++count_;
return *this;
}
counting_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
friend counting_iterator operator+(counting_iterator it, difference_type n) {
it.count_ += static_cast<size_t>(n);
return it;
}
value_type operator*() const { return {}; }
};
template <typename Char, typename InputIt> template <typename Char, typename InputIt>
inline counting_iterator copy_str(InputIt begin, InputIt end, inline counting_iterator copy_str(InputIt begin, InputIt end,
counting_iterator it) { counting_iterator it) {
@ -75,8 +36,7 @@ template <typename OutputIt> class truncating_iterator_base {
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using pointer = void; using pointer = void;
using reference = void; using reference = void;
using _Unchecked_type = FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
truncating_iterator_base; // Mark iterator as checked.
OutputIt base() const { return out_; } OutputIt base() const { return out_; }
size_t count() const { return count_; } size_t count() const { return count_; }
@ -163,12 +123,12 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
# define FMT_COMPILE(s) FMT_STRING(s) # define FMT_COMPILE(s) FMT_STRING(s)
#endif #endif
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS #if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <typename Char, size_t N, template <typename Char, size_t N,
fmt::detail_exported::fixed_string<Char, N> Str> fmt::detail_exported::fixed_string<Char, N> Str>
struct udl_compiled_string : compiled_string { struct udl_compiled_string : compiled_string {
using char_type = Char; using char_type = Char;
constexpr operator basic_string_view<char_type>() const { explicit constexpr operator basic_string_view<char_type>() const {
return {Str.data, N - 1}; return {Str.data, N - 1};
} }
}; };
@ -377,7 +337,8 @@ template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) { size_t pos, int next_arg_id) {
str.remove_prefix(pos); str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str, {}, next_arg_id); auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, {},
next_arg_id);
auto f = formatter<T, Char>(); auto f = formatter<T, Char>();
auto end = f.parse(ctx); auto end = f.parse(ctx);
return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1, return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1,
@ -573,10 +534,11 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
constexpr auto compiled = detail::compile<Args...>(S()); constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) { detail::unknown_format>()) {
return format(static_cast<basic_string_view<typename S::char_type>>(S()), return fmt::format(
std::forward<Args>(args)...); static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else { } else {
return format(compiled, std::forward<Args>(args)...); return fmt::format(compiled, std::forward<Args>(args)...);
} }
} }
@ -586,11 +548,11 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S()); constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) { detail::unknown_format>()) {
return format_to(out, return fmt::format_to(
static_cast<basic_string_view<typename S::char_type>>(S()), out, static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...); std::forward<Args>(args)...);
} else { } else {
return format_to(out, compiled, std::forward<Args>(args)...); return fmt::format_to(out, compiled, std::forward<Args>(args)...);
} }
} }
#endif #endif
@ -599,22 +561,23 @@ template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const S& format_str, Args&&... args) { const S& format_str, Args&&... args) {
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str, auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
std::forward<Args>(args)...); format_str, std::forward<Args>(args)...);
return {it.base(), it.count()}; return {it.base(), it.count()};
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
size_t formatted_size(const S& format_str, const Args&... args) { 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 <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(std::FILE* f, const S& format_str, const Args&... args) { void print(std::FILE* f, const S& format_str, const Args&... args) {
memory_buffer buffer; 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()}); 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...); print(stdout, format_str, args...);
} }
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS #if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals { inline namespace literals {
template <detail_exported::fixed_string Str> template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
constexpr detail::udl_compiled_string< using char_t = remove_cvref_t<decltype(Str.data[0])>;
remove_cvref_t<decltype(Str.data[0])>, return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> Str>();
operator""_cf() {
return {};
} }
} // namespace literals } // namespace literals
#endif #endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -9,10 +9,8 @@
#define FMT_OS_H_ #define FMT_OS_H_
#include <cerrno> #include <cerrno>
#include <clocale> // locale_t
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdio>
#include <cstdlib> // strtod_l
#include <system_error> // std::system_error #include <system_error> // std::system_error
#if defined __APPLE__ || defined(__FreeBSD__) #if defined __APPLE__ || defined(__FreeBSD__)
@ -141,7 +139,7 @@ template <typename Char> struct formatter<std::error_code, Char> {
}; };
#ifdef _WIN32 #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 FMT_BEGIN_DETAIL_NAMESPACE
// A converter from UTF-16 to UTF-8. // A converter from UTF-16 to UTF-8.
@ -165,7 +163,7 @@ class utf16_to_utf8 {
}; };
FMT_API void format_windows_error(buffer<char>& out, int error_code, FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) FMT_NOEXCEPT; const char* message) noexcept;
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
FMT_API std::system_error vwindows_error(int error_code, string_view format_str, 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. // Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors. // Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code, FMT_API void report_windows_error(int error_code, const char* message) noexcept;
const char* message) FMT_NOEXCEPT;
#else #else
inline const std::error_category& system_category() FMT_NOEXCEPT { inline const std::error_category& system_category() noexcept {
return std::system_category(); return std::system_category();
} }
#endif // _WIN32 #endif // _WIN32
@ -237,13 +234,13 @@ class buffered_file {
void operator=(const buffered_file&) = delete; void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file. // 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. // Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT; FMT_API ~buffered_file() noexcept;
public: public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) { buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
other.file_ = nullptr; other.file_ = nullptr;
} }
@ -261,11 +258,9 @@ class buffered_file {
FMT_API void close(); FMT_API void close();
// Returns the pointer to a FILE object representing this file. // 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 FMT_API int descriptor() const;
// of MinGW that define fileno as a macro.
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) { void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args); fmt::vprint(file_, format_str, args);
@ -279,12 +274,12 @@ class buffered_file {
#if FMT_USE_FCNTL #if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1. // 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 // 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 // closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the // than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler. // invalid parameter handler with _set_invalid_parameter_handler.
class file { class FMT_API file {
private: private:
int fd_; // File descriptor. int fd_; // File descriptor.
@ -303,16 +298,16 @@ class file {
}; };
// Constructs a file object which doesn't represent any 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. // 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: public:
file(const file&) = delete; file(const file&) = delete;
void operator=(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. // Move assignment is not noexcept because close may throw.
file& operator=(file&& other) { file& operator=(file&& other) {
@ -323,43 +318,43 @@ class file {
} }
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT; ~file() noexcept;
// Returns the file descriptor. // Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; } int descriptor() const noexcept { return fd_; }
// Closes the file. // Closes the file.
FMT_API void close(); void close();
// Returns the file size. The size has signed type for consistency with // Returns the file size. The size has signed type for consistency with
// stat::st_size. // 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. // 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. // 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 // Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object. // 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 // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd); void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // 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 // Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively. // 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 // Creates a buffered_file object associated with this file and detaches
// this file object from the file. // 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. // Returns the memory page size.
@ -462,7 +457,7 @@ class FMT_API ostream final : private detail::buffer<char> {
* ``<integer>``: Flags passed to `open * ``<integer>``: Flags passed to `open
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_ <https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
(``file::WRONLY | file::CREATE`` by default) (``file::WRONLY | file::CREATE | file::TRUNC`` by default)
* ``buffer_size=<integer>``: Output buffer size * ``buffer_size=<integer>``: Output buffer size
**Example**:: **Example**::
@ -477,50 +472,6 @@ inline ostream output_file(cstring_view path, T... params) {
} }
#endif // FMT_USE_FCNTL #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_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE

View file

@ -8,6 +8,7 @@
#ifndef FMT_OSTREAM_H_ #ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
#include <fstream>
#include <ostream> #include <ostream>
#include "format.h" #include "format.h"
@ -45,15 +46,59 @@ struct is_streamable<
enable_if_t< enable_if_t<
std::is_arithmetic<T>::value || std::is_array<T>::value || std::is_arithmetic<T>::value || std::is_array<T>::value ||
std::is_pointer<T>::value || std::is_same<T, char8_type>::value || std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
std::is_same<T, std::basic_string<Char>>::value || std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
std::is_same<T, std_string_view<Char>>::value || std::is_same<T, std_string_view<Char>>::value ||
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>> (std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
: std::false_type {}; : std::false_type {};
template <typename Char> FILE* get_file(std::basic_filebuf<Char>&) {
return nullptr;
}
struct dummy_filebuf {
FILE* _Myfile;
};
template <typename T, typename U = int> struct ms_filebuf {
using type = dummy_filebuf;
};
template <typename T> struct ms_filebuf<T, decltype(T::_Myfile, 0)> {
using type = T;
};
using filebuf_type = ms_filebuf<std::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 <typename Tag, typename FileMemberPtr, FileMemberPtr file>
class filebuf_access {
friend FILE* get_file(filebuf_type& buf) { return buf.*file; }
};
template class filebuf_access<filebuf_access_tag,
decltype(&filebuf_type::_Myfile),
&filebuf_type::_Myfile>;
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<wchar_t>) {
return false;
}
// Write the content of buf to os. // Write the content of buf to os.
// It is a separate function rather than a part of vprint to simplify testing. // It is a separate function rather than a part of vprint to simplify testing.
template <typename Char> template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
if (const_check(FMT_MSC_VERSION)) {
auto filebuf = dynamic_cast<std::basic_filebuf<Char>*>(os.rdbuf());
if (filebuf && write(*filebuf, {buf.data(), buf.size()})) return;
}
const Char* buf_data = buf.data(); const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size(); unsigned_streamsize size = buf.size();
@ -76,38 +121,65 @@ void format_value(buffer<Char>& buf, const T& value,
#endif #endif
output << value; output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); 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 <typename T> struct streamed_view { const T& value; };
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: private formatter<basic_string_view<Char>, Char> {
using formatter<basic_string_view<Char>, Char>::parse;
template <typename OutputIt> } // namespace detail
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename Char>
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
template <typename T, typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
-> OutputIt { -> OutputIt {
auto buffer = basic_memory_buffer<Char>(); auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale()); format_value(buffer, value, ctx.locale());
return formatter<basic_string_view<Char>, Char>::format( return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx); {buffer.data(), buffer.size()}, ctx);
} }
};
// DEPRECATED! using ostream_formatter = basic_ostream_formatter<char>;
template <typename T>
struct formatter<detail::streamed_view<T>> : ostream_formatter {
template <typename OutputIt> template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx) auto format(detail::streamed_view<T> view,
-> OutputIt { basic_format_context<OutputIt, char>& ctx) const -> OutputIt {
auto buffer = basic_memory_buffer<Char>(); return ostream_formatter::format(view.value, ctx);
format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out());
} }
}; };
/**
\rst
Returns a view that formats `value` via an ostream ``operator<<``.
**Example**::
fmt::print("Current thread id: {}\n",
fmt::streamed(std::this_thread::get_id()));
\endrst
*/
template <typename T>
auto streamed(const T& value) -> detail::streamed_view<T> {
return {value};
}
namespace detail {
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: basic_ostream_formatter<Char> {
using basic_ostream_formatter<Char>::format;
};
} // namespace detail } // namespace detail
FMT_MODULE_EXPORT FMT_MODULE_EXPORT template <typename Char>
template <typename Char> void vprint(std::basic_ostream<Char>& os,
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, basic_string_view<type_identity_t<Char>> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto buffer = basic_memory_buffer<Char>(); auto buffer = basic_memory_buffer<Char>();
detail::vformat_to(buffer, format_str, args); detail::vformat_to(buffer, format_str, args);
@ -123,13 +195,19 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
fmt::print(cerr, "Don't {}!", "panic"); fmt::print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
FMT_MODULE_EXPORT FMT_MODULE_EXPORT template <typename... T>
template <typename S, typename... Args, void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> vprint(os, fmt, fmt::make_format_args(args...));
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
} }
FMT_MODULE_EXPORT
template <typename... Args>
void print(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
}
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_ #endif // FMT_OSTREAM_H_

View file

@ -10,7 +10,6 @@
#include <algorithm> // std::max #include <algorithm> // std::max
#include <limits> // std::numeric_limits #include <limits> // std::numeric_limits
#include <ostream>
#include "format.h" #include "format.h"
@ -561,7 +560,7 @@ inline auto vsprintf(
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args); vprintf(buffer, detail::to_string_view(fmt), args);
return to_string(buffer); return to_string(buffer);
} }
@ -578,7 +577,8 @@ template <typename S, typename... T,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> { inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(fmt), fmt::make_format_args<context>(args...)); return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<context>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
@ -587,7 +587,7 @@ inline auto vfprintf(
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int { -> int {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args); vprintf(buffer, detail::to_string_view(fmt), args);
size_t size = buffer.size(); size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1 ? -1
@ -606,7 +606,7 @@ inline auto vfprintf(
template <typename S, typename... T, typename Char = char_t<S>> template <typename S, typename... T, typename Char = char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(fmt), return vfprintf(f, detail::to_string_view(fmt),
fmt::make_format_args<context>(args...)); fmt::make_format_args<context>(args...));
} }
@ -615,7 +615,7 @@ inline auto vprintf(
const S& fmt, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int { -> 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 <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)> template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
inline auto printf(const S& fmt, const T&... args) -> int { inline auto printf(const S& fmt, const T&... args) -> int {
return vprintf( return vprintf(
to_string_view(fmt), detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...)); fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
} }
template <typename S, typename Char = char_t<S>>
FMT_DEPRECATED auto vfprintf(
std::basic_ostream<Char>& os, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args);
os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
return static_cast<int>(buffer.size());
}
template <typename S, typename... T, typename Char = char_t<S>>
FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt,
const T&... args) -> int {
return vfprintf(os, to_string_view(fmt),
fmt::make_format_args<basic_printf_context_t<Char>>(args...));
}
FMT_MODULE_EXPORT_END FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE

View file

@ -55,7 +55,7 @@ template <typename T> class is_std_string_like {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = static constexpr const bool value =
is_string<T>::value || is_string<T>::value ||
std::is_convertible<T, std_string_view<char>>::value || std::is_convertible<T, std_string_view<char>>::value ||
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
@ -70,9 +70,9 @@ template <typename T> class is_map {
public: public:
#ifdef FMT_FORMAT_MAP_AS_LIST #ifdef FMT_FORMAT_MAP_AS_LIST
static FMT_CONSTEXPR_DECL const bool value = false; static constexpr const bool value = false;
#else #else
static FMT_CONSTEXPR_DECL const bool value = static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
#endif #endif
}; };
@ -83,9 +83,9 @@ template <typename T> class is_set {
public: public:
#ifdef FMT_FORMAT_SET_AS_LIST #ifdef FMT_FORMAT_SET_AS_LIST
static FMT_CONSTEXPR_DECL const bool value = false; static constexpr const bool value = false;
#else #else
static FMT_CONSTEXPR_DECL const bool value = static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value; !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
#endif #endif
}; };
@ -94,7 +94,7 @@ template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {}; template <typename T, typename _ = void> 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) \ # define FMT_DECLTYPE_RETURN(val) \
->decltype(val) { return val; } \ ->decltype(val) { return val; } \
@ -174,12 +174,12 @@ template <typename T> class is_tuple_like_ {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
}; };
// Check for integer_sequence // 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 <typename T, T... N> template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>; using integer_sequence = std::integer_sequence<T, N...>;
template <size_t... N> using index_sequence = std::index_sequence<N...>; template <size_t... N> using index_sequence = std::index_sequence<N...>;
@ -202,8 +202,33 @@ template <size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>; using make_index_sequence = make_integer_sequence<size_t, N>;
#endif #endif
template <typename T>
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
template <typename T, typename C, bool = is_tuple_like_<T>::value>
class is_tuple_formattable_ {
public:
static constexpr const bool value = false;
};
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <std::size_t... I>
static std::true_type check2(index_sequence<I...>,
integer_sequence<bool, (I == I)...>);
static std::false_type check2(...);
template <std::size_t... I>
static decltype(check2(
index_sequence<I...>{},
integer_sequence<
bool, (is_formattable<typename std::tuple_element<I, T>::type,
C>::value)...>{})) check(index_sequence<I...>);
public:
static constexpr const bool value =
decltype(check(tuple_index_sequence<T>{}))::value;
};
template <class Tuple, class F, size_t... Is> template <class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT { void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) noexcept {
using std::get; using std::get;
// using free function get<I>(T) now. // using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
@ -221,9 +246,36 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
} }
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
// Older MSVC doesn't get the reference type correctly for arrays.
template <typename R> struct range_reference_type_impl {
using type = decltype(*detail::range_begin(std::declval<R&>()));
};
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
using type = T&;
};
template <typename T>
using range_reference_type = typename range_reference_type_impl<T>::type;
#else
template <typename Range> template <typename Range>
using value_type = using range_reference_type =
remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>; decltype(*detail::range_begin(std::declval<Range&>()));
#endif
// We don't use the Range's value_type for anything, but we do need the Range's
// reference type, with cv-ref stripped.
template <typename Range>
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
template <typename Range>
using uncvref_first_type = remove_cvref_t<
decltype(std::declval<range_reference_type<Range>>().first)>;
template <typename Range>
using uncvref_second_type = remove_cvref_t<
decltype(std::declval<range_reference_type<Range>>().second)>;
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) { template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
*out++ = ','; *out++ = ',';
@ -231,286 +283,9 @@ template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
return 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<int>(x);
auto current = true;
for (size_t i = 0; i < normal_size; ++i) {
auto v = static_cast<int>(normal[i]);
auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v;
xsigned -= len;
if (xsigned < 0) break;
current = !current;
}
return current;
}
// 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<uint16_t>(cp);
if (cp < 0x10000) {
return is_printable(lower, singletons0,
sizeof(singletons0) / sizeof(*singletons0),
singletons0_lower, normal0, sizeof(normal0));
}
if (cp < 0x20000) {
return is_printable(lower, singletons1,
sizeof(singletons1) / sizeof(*singletons1),
singletons1_lower, normal1, sizeof(normal1));
}
if (0x2a6de <= cp && cp < 0x2a700) return false;
if (0x2b735 <= cp && cp < 0x2b740) return false;
if (0x2b81e <= cp && cp < 0x2b820) return false;
if (0x2cea2 <= cp && cp < 0x2ceb0) return false;
if (0x2ebe1 <= cp && cp < 0x2f800) return false;
if (0x2fa1e <= cp && cp < 0x30000) return false;
if (0x3134b <= cp && cp < 0xe0100) return false;
if (0xe01f0 <= cp && cp < 0x110000) return false;
return cp < 0x110000;
}
inline auto needs_escape(uint32_t cp) -> bool {
return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
!is_printable(cp);
}
template <typename Char> struct find_escape_result {
const Char* begin;
const Char* end;
uint32_t cp;
};
template <typename Char>
auto find_escape(const Char* begin, const Char* end)
-> find_escape_result<Char> {
for (; begin != end; ++begin) {
auto cp = static_cast<typename std::make_unsigned<Char>::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<char> {
if (!is_utf8()) return find_escape<char>(begin, end);
auto result = find_escape_result<char>{end, nullptr, 0};
for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
[&](uint32_t cp, string_view sv) {
if (needs_escape(cp)) {
result = {sv.begin(), sv.end(), cp};
return false;
}
return true;
});
return result;
}
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt { auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
*out++ = '"'; return write_escaped_string(out, str);
auto begin = str.begin(), end = str.end();
do {
auto escape = find_escape(begin, end);
out = copy_str<Char>(begin, escape.begin, out);
begin = escape.end;
if (!begin) break;
auto c = static_cast<Char>(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<Char>(
escape.begin, to_unsigned(escape.end - escape.begin))) {
out = format_to(
out, "\\x{:02x}",
static_cast<typename std::make_unsigned<Char>::type>(escape_char));
}
continue;
}
*out++ = c;
} while (begin != end);
*out++ = '"';
return out;
} }
template <typename Char, typename OutputIt, typename T, template <typename Char, typename OutputIt, typename T,
@ -523,10 +298,7 @@ inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
template <typename Char, typename OutputIt, typename Arg, template <typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)> FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
OutputIt write_range_entry(OutputIt out, const Arg v) { OutputIt write_range_entry(OutputIt out, const Arg v) {
*out++ = '\''; return write_escaped_char(out, v);
*out++ = v;
*out++ = '\'';
return out;
} }
template < template <
@ -540,12 +312,19 @@ OutputIt write_range_entry(OutputIt out, const Arg& v) {
} // namespace detail } // namespace detail
template <typename T> struct is_tuple_like { template <typename T> struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value = static constexpr const bool value =
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value; detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
}; };
template <typename T, typename C> struct is_tuple_formattable {
static constexpr const bool value =
detail::is_tuple_formattable_<T, C>::value;
};
template <typename TupleT, typename Char> template <typename TupleT, typename Char>
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> { struct formatter<TupleT, Char,
enable_if_t<fmt::is_tuple_like<TupleT>::value &&
fmt::is_tuple_formattable<TupleT, Char>::value>> {
private: private:
// C++11 generic lambda for format(). // C++11 generic lambda for format().
template <typename FormatContext> struct format_each { template <typename FormatContext> struct format_each {
@ -565,7 +344,8 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
} }
template <typename FormatContext = format_context> template <typename FormatContext = format_context>
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(); auto out = ctx.out();
*out++ = '('; *out++ = '(';
detail::for_each(values, format_each<FormatContext>{0, out}); detail::for_each(values, format_each<FormatContext>{0, out});
@ -575,50 +355,101 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
}; };
template <typename T, typename Char> struct is_range { template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value = static constexpr const bool value =
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value && detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
!detail::is_map<T>::value && !detail::is_map<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value && !std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<detail::std_string_view<Char>, T>::value; !std::is_constructible<detail::std_string_view<Char>, T>::value;
}; };
template <typename T, typename Char> namespace detail {
template <typename Context> struct range_mapper {
using mapper = arg_mapper<Context>;
template <typename T,
FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
static auto map(T&& value) -> T&& {
return static_cast<T&&>(value);
}
template <typename T,
FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
static auto map(T&& value)
-> decltype(mapper().map(static_cast<T&&>(value))) {
return mapper().map(static_cast<T&&>(value));
}
};
template <typename Char, typename Element>
using range_formatter_type = conditional_t<
is_formattable<Element, Char>::value,
formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
std::declval<Element>()))>,
Char>,
fallback_formatter<Element, Char>>;
template <typename R>
using maybe_const_range =
conditional_t<has_const_begin_end<R>::value, const R, R>;
} // namespace detail
template <typename R, typename Char>
struct formatter< struct formatter<
T, Char, R, Char,
enable_if_t< enable_if_t<
fmt::is_range<T, Char>::value conjunction<fmt::is_range<R, Char>
// Workaround a bug in MSVC 2019 and earlier. // Workaround a bug in MSVC 2017 and earlier.
#if !FMT_MSC_VER #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920
&& (is_formattable<detail::value_type<T>, Char>::value || ,
detail::has_fallback_formatter<detail::value_type<T>, Char>::value) disjunction<
is_formattable<detail::uncvref_type<detail::maybe_const_range<R>>,
Char>,
detail::has_fallback_formatter<
detail::uncvref_type<detail::maybe_const_range<R>>, Char>
>
#endif #endif
>::value
>> { >> {
using range_type = detail::maybe_const_range<R>;
using formatter_type =
detail::range_formatter_type<Char, detail::uncvref_type<range_type>>;
formatter_type underlying_;
bool custom_specs_ = false;
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 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 < template <typename FormatContext>
typename FormatContext, typename U, auto format(range_type& range, FormatContext& ctx) const
FMT_ENABLE_IF( -> decltype(ctx.out()) {
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value, Char prefix = detail::is_set<R>::value ? '{' : '[';
const T, T>>::value)> Char postfix = detail::is_set<R>::value ? '}' : ']';
auto format(U& range, FormatContext& ctx) -> decltype(ctx.out()) { detail::range_mapper<buffer_context<Char>> mapper;
#ifdef FMT_DEPRECATED_BRACED_RANGES
Char prefix = '{';
Char postfix = '}';
#else
Char prefix = detail::is_set<T>::value ? '{' : '[';
Char postfix = detail::is_set<T>::value ? '}' : ']';
#endif
auto out = ctx.out(); auto out = ctx.out();
*out++ = prefix; *out++ = prefix;
int i = 0; int i = 0;
auto it = std::begin(range); auto it = detail::range_begin(range);
auto end = std::end(range); auto end = detail::range_end(range);
for (; it != end; ++it) { for (; it != end; ++it) {
if (i > 0) out = detail::write_delimiter(out); if (i > 0) out = detail::write_delimiter(out);
out = detail::write_range_entry<Char>(out, *it); if (custom_specs_) {
ctx.advance_to(out);
out = underlying_.format(mapper.map(*it), ctx);
} else {
out = detail::write_range_entry<Char>(out, *it);
}
++i; ++i;
} }
*out++ = postfix; *out++ = postfix;
@ -629,14 +460,21 @@ struct formatter<
template <typename T, typename Char> template <typename T, typename Char>
struct formatter< struct formatter<
T, Char, T, Char,
enable_if_t< enable_if_t<conjunction<detail::is_map<T>
detail::is_map<T>::value // Workaround a bug in MSVC 2017 and earlier.
// Workaround a bug in MSVC 2019 and earlier. #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920
#if !FMT_MSC_VER ,
&& (is_formattable<detail::value_type<T>, Char>::value || disjunction<
detail::has_fallback_formatter<detail::value_type<T>, Char>::value) is_formattable<detail::uncvref_first_type<T>, Char>,
detail::has_fallback_formatter<detail::uncvref_first_type<T>, Char>
>,
disjunction<
is_formattable<detail::uncvref_second_type<T>, Char>,
detail::has_fallback_formatter<detail::uncvref_second_type<T>, Char>
>
#endif #endif
>> { >::value
>> {
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return ctx.begin();
@ -647,7 +485,7 @@ struct formatter<
FMT_ENABLE_IF( FMT_ENABLE_IF(
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value, std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
const T, T>>::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(); auto out = ctx.out();
*out++ = '{'; *out++ = '{';
int i = 0; int i = 0;

View file

@ -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 <thread>
#include <type_traits>
#include <utility>
#include "ostream.h"
#if FMT_HAS_INCLUDE(<version>)
# include <version>
#endif
// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
#if FMT_CPLUSPLUS >= 201703L
# if FMT_HAS_INCLUDE(<filesystem>)
# include <filesystem>
# endif
# if FMT_HAS_INCLUDE(<variant>)
# include <variant>
# endif
#endif
#ifdef __cpp_lib_filesystem
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char>
void write_escaped_path(basic_memory_buffer<Char>& quoted,
const std::filesystem::path& p) {
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
}
# ifdef _WIN32
template <>
inline void write_escaped_path<char>(basic_memory_buffer<char>& quoted,
const std::filesystem::path& p) {
auto s = p.u8string();
write_escaped_string<char>(
std::back_inserter(quoted),
string_view(reinterpret_cast<const char*>(s.c_str()), s.size()));
}
# endif
template <>
inline void write_escaped_path<std::filesystem::path::value_type>(
basic_memory_buffer<std::filesystem::path::value_type>& quoted,
const std::filesystem::path& p) {
write_escaped_string<std::filesystem::path::value_type>(
std::back_inserter(quoted), p.native());
}
} // namespace detail
#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 <typename Char>
struct formatter<std::filesystem::path, Char>
: formatter<basic_string_view<Char>> {
template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const ->
typename FormatContext::iterator {
basic_memory_buffer<Char> quoted;
detail::write_escaped_path(quoted, p);
return formatter<basic_string_view<Char>>::format(
basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
}
};
#endif
FMT_END_NAMESPACE
#endif
FMT_BEGIN_NAMESPACE
template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE
#ifdef __cpp_lib_variant
FMT_BEGIN_NAMESPACE
template <typename Char> struct formatter<std::monostate, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write<Char>(out, "monostate");
return out;
}
};
namespace detail {
template <typename T>
using variant_index_sequence =
std::make_index_sequence<std::variant_size<T>::value>;
// variant_size and variant_alternative check.
template <typename T, typename U = void>
struct is_variant_like_ : std::false_type {};
template <typename T>
struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>>
: std::true_type {};
// formattable element check
template <typename T, typename C> class is_variant_formattable_ {
template <std::size_t... I>
static std::conjunction<
is_formattable<std::variant_alternative_t<I, T>, C>...>
check(std::index_sequence<I...>);
public:
static constexpr const bool value =
decltype(check(variant_index_sequence<T>{}))::value;
};
template <typename Char, typename OutputIt, typename T>
auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (is_string<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
else if constexpr (std::is_same_v<T, Char>)
return write_escaped_char(out, v);
else
return write<Char>(out, v);
}
} // namespace detail
template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value;
};
template <typename T, typename C> struct is_variant_formattable {
static constexpr const bool value =
detail::is_variant_formattable_<T, C>::value;
};
template <typename Variant, typename Char>
struct formatter<
Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Variant& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write<Char>(out, "variant(");
std::visit(
[&](const auto& v) {
out = detail::write_variant_alternative<Char>(out, v);
},
value);
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif
#endif // FMT_STD_H_

View file

@ -47,12 +47,7 @@ constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
} }
inline namespace literals { inline namespace literals {
constexpr auto operator"" _format(const wchar_t* s, size_t n) #if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
-> detail::udl_formatter<wchar_t> {
return {{s, n}};
}
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) { constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
return {s}; return {s};
} }
@ -87,13 +82,23 @@ auto vformat(basic_string_view<Char> format_str,
return to_string(buffer); return to_string(buffer);
} }
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 409
template <typename... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
#endif
template <typename... T>
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
return vformat(fmt, fmt::make_wformat_args(args...));
}
// Pass char_t as a default template parameter instead of using // Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size. // std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... Args, typename Char = char_t<S>, template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)> FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> { auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); return vformat(detail::to_string_view(format_str),
return vformat(to_string_view(format_str), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename Locale, typename S, typename Char = char_t<S>, template <typename Locale, typename S, typename Char = char_t<S>,
@ -103,7 +108,7 @@ inline auto vformat(
const Locale& loc, const S& format_str, const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str), args); return detail::vformat(loc, detail::to_string_view(format_str), args);
} }
template <typename Locale, typename S, typename... Args, template <typename Locale, typename S, typename... Args,
@ -112,8 +117,8 @@ template <typename Locale, typename S, typename... Args,
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& format_str, Args&&... args) inline auto format(const Locale& loc, const S& format_str, Args&&... args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str), return detail::vformat(loc, detail::to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename OutputIt, typename S, typename Char = char_t<S>, template <typename OutputIt, typename S, typename Char = char_t<S>,
@ -123,7 +128,7 @@ auto vformat_to(OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> OutputIt { -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out); auto&& buf = detail::get_buffer<Char>(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); return detail::get_iterator(buf);
} }
@ -132,18 +137,8 @@ template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); return vformat_to(out, detail::to_string_view(fmt),
return vformat_to(out, to_string_view(fmt), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
}
template <typename S, typename... Args, typename Char, size_t SIZE,
typename Allocator, FMT_ENABLE_IF(detail::is_string<S>::value)>
FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf,
const S& format_str, Args&&... args) ->
typename buffer_context<Char>::iterator {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
detail::vformat_to(buf, to_string_view(format_str), vargs, {});
return detail::buffer_appender<Char>(buf);
} }
template <typename Locale, typename S, typename OutputIt, typename... Args, template <typename Locale, typename S, typename OutputIt, typename... Args,
@ -155,7 +150,8 @@ inline auto vformat_to(
OutputIt out, const Locale& loc, const S& format_str, OutputIt out, const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt { basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out); auto&& buf = detail::get_buffer<Char>(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); return detail::get_iterator(buf);
} }
@ -167,8 +163,8 @@ template <
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
Args&&... args) -> Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type { typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); return vformat_to(out, loc, to_string_view(format_str),
return vformat_to(out, loc, to_string_view(format_str), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename OutputIt, typename Char, typename... Args, template <typename OutputIt, typename Char, typename... Args,
@ -190,16 +186,16 @@ template <typename OutputIt, typename S, typename... Args,
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
const Args&... args) -> format_to_n_result<OutputIt> { const Args&... args) -> format_to_n_result<OutputIt> {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); return vformat_to_n(out, n, detail::to_string_view(fmt),
return vformat_to_n(out, n, to_string_view(fmt), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename S, typename... Args, typename Char = char_t<S>, template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t { inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
detail::counting_buffer<Char> buf; detail::counting_buffer<Char> buf;
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); detail::vformat_to(buf, detail::to_string_view(fmt),
detail::vformat_to(buf, to_string_view(fmt), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
return buf.count(); return buf.count();
} }

View file

@ -10,115 +10,38 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
// DEPRECATED! template FMT_API auto dragonbox::to_decimal(float x) noexcept
template <typename T = void> struct basic_data { -> dragonbox::decimal_fp<float>;
FMT_API static constexpr const char digits[100][2] = { template FMT_API auto dragonbox::to_decimal(double x) noexcept
{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, -> dragonbox::decimal_fp<double>;
{'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<void>;
#endif
#if __cplusplus < 201703L
// DEPRECATED! These are here only for ABI compatiblity.
template <typename T> constexpr const char basic_data<T>::digits[][2];
template <typename T> constexpr const char basic_data<T>::hex_digits[];
template <typename T> constexpr const char basic_data<T>::signs[];
template <typename T> constexpr const char basic_data<T>::left_padding_shifts[];
template <typename T>
constexpr const char basic_data<T>::right_padding_shifts[];
template <typename T> constexpr const unsigned basic_data<T>::prefixes[];
#endif
template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) {
#ifdef FMT_FUZZ
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about nonliteral format string.
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
FMT_NOEXCEPT;
template FMT_API dragonbox::decimal_fp<double> 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<char>&) = detail::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc); template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale detail::locale_ref::get<std::locale>() const; template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif #endif
// Explicit instantiations for char. // 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<char>; -> thousands_sep_result<char>;
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<char>::append(const char*, const char*); template FMT_API void buffer<char>::append(const char*, const char*);
// DEPRECATED! // DEPRECATED!
// There is no correspondent extern template in format.h because of // There is no correspondent extern template in format.h because of
// incompatibility between clang and gcc (#2377). // incompatibility between clang and gcc (#2377).
template FMT_API void detail::vformat_to( template FMT_API void vformat_to(buffer<char>&, string_view,
detail::buffer<char>&, string_view, basic_format_args<FMT_BUFFER_CONTEXT(char)>,
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref); locale_ref);
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::snprintf_float(long double, int,
detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(long double, int, detail::float_specs,
detail::buffer<char>&);
// Explicit instantiations for wchar_t. // 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<wchar_t>; -> thousands_sep_result<wchar_t>;
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<wchar_t>::append(const wchar_t*, template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
const wchar_t*);
template struct detail::basic_data<void>;
} // namespace detail
FMT_END_NAMESPACE FMT_END_NAMESPACE

View file

@ -35,9 +35,15 @@
# ifndef S_IRGRP # ifndef S_IRGRP
# define S_IRGRP 0 # define S_IRGRP 0
# endif # endif
# ifndef S_IWGRP
# define S_IWGRP 0
# endif
# ifndef S_IROTH # ifndef S_IROTH
# define S_IROTH 0 # define S_IROTH 0
# endif # endif
# ifndef S_IWOTH
# define S_IWOTH 0
# endif
# endif // _WIN32 # endif // _WIN32
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
@ -45,10 +51,6 @@
# include <windows.h> # include <windows.h>
#endif #endif
#ifdef fileno
# undef fileno
#endif
namespace { namespace {
#ifdef _WIN32 #ifdef _WIN32
// Return type of read and write functions. // Return type of read and write functions.
@ -107,7 +109,7 @@ class system_message {
unsigned long result_; unsigned long result_;
wchar_t* message_; 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'; 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_); } ~system_message() { LocalFree(message_); }
explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; } explicit operator bool() const noexcept { return result_ != 0; }
operator basic_string_view<wchar_t>() const FMT_NOEXCEPT { operator basic_string_view<wchar_t>() const noexcept {
return basic_string_view<wchar_t>(message_, result_); return basic_string_view<wchar_t>(message_, result_);
} }
}; };
class utf8_system_category final : public std::error_category { class utf8_system_category final : public std::error_category {
public: 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 { std::string message(int error_code) const override {
system_message msg(error_code); system_message msg(error_code);
if (msg) { if (msg) {
@ -149,7 +151,7 @@ class utf8_system_category final : public std::error_category {
} // namespace detail } // 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; static const detail::utf8_system_category category;
return 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<char>& out, int error_code, void detail::format_windows_error(detail::buffer<char>& out, int error_code,
const char* message) FMT_NOEXCEPT { const char* message) noexcept {
FMT_TRY { FMT_TRY {
system_message msg(error_code); system_message msg(error_code);
if (msg) { if (msg) {
utf16_to_utf8 utf8_message; utf16_to_utf8 utf8_message;
if (utf8_message.convert(msg) == ERROR_SUCCESS) { if (utf8_message.convert(msg) == ERROR_SUCCESS) {
format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message); fmt::format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message);
return; return;
} }
} }
@ -176,12 +178,12 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
format_error_code(out, error_code, message); 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); report_error(detail::format_windows_error, error_code, message);
} }
#endif // _WIN32 #endif // _WIN32
buffered_file::~buffered_file() FMT_NOEXCEPT { buffered_file::~buffered_file() noexcept {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0) if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
report_system_error(errno, "cannot close file"); 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")); if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
} }
// A macro used to prevent expansion of fileno on broken versions of MinGW. int buffered_file::descriptor() const {
#define FMT_ARGS int fd = FMT_POSIX_CALL(fileno(file_));
int buffered_file::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor")); if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
return fd; return fd;
} }
@ -214,7 +213,8 @@ file::file(cstring_view path, int oflag) {
# ifdef _WIN32 # ifdef _WIN32
using mode_t = int; using mode_t = int;
# endif # 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__) # if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1; fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); 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())); 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! // Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) 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; int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) ec = std::error_code(errno, std::generic_category()); if (result == -1) ec = std::error_code(errno, std::generic_category());

View file

@ -1 +1 @@
4.2.1 5.1.1

View file

@ -11,18 +11,17 @@ cc_library(
"include/fmt/color.h", "include/fmt/color.h",
"include/fmt/compile.h", "include/fmt/compile.h",
"include/fmt/core.h", "include/fmt/core.h",
"include/fmt/format.h",
"include/fmt/format-inl.h", "include/fmt/format-inl.h",
"include/fmt/locale.h", "include/fmt/format.h",
"include/fmt/os.h", "include/fmt/os.h",
"include/fmt/ostream.h", "include/fmt/ostream.h",
"include/fmt/printf.h", "include/fmt/printf.h",
"include/fmt/ranges.h", "include/fmt/ranges.h",
"include/fmt/std.h",
"include/fmt/xchar.h", "include/fmt/xchar.h",
], ],
includes = [ includes = [
"include", "include",
"src",
], ],
strip_include_prefix = "include", strip_include_prefix = "include",
visibility = ["//visibility:public"], visibility = ["//visibility:public"],

View file

@ -1,7 +1,11 @@
# C++14 feature support detection # C++14 feature support detection
include(CheckCXXSourceCompiles)
include(CheckCXXCompilerFlag) 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) if (NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
@ -9,35 +13,38 @@ endif()
message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}") message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}")
if (CMAKE_CXX_STANDARD EQUAL 20) if (CMAKE_CXX_STANDARD EQUAL 20)
check_cxx_compiler_flag(-std=c++20 has_std_20_flag) fmt_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++2a has_std_2a_flag)
if (has_std_20_flag) if (has_std_20_flag)
set(CXX_STANDARD_FLAG -std=c++20) set(CXX_STANDARD_FLAG -std=c++20)
elseif (has_std_2a_flag) elseif (has_std_2a_flag)
set(CXX_STANDARD_FLAG -std=c++2a) set(CXX_STANDARD_FLAG -std=c++2a)
endif () endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 17) elseif (CMAKE_CXX_STANDARD EQUAL 17)
check_cxx_compiler_flag(-std=c++17 has_std_17_flag) fmt_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++1z has_std_1z_flag)
if (has_std_17_flag) if (has_std_17_flag)
set(CXX_STANDARD_FLAG -std=c++17) set(CXX_STANDARD_FLAG -std=c++17)
elseif (has_std_1z_flag) elseif (has_std_1z_flag)
set(CXX_STANDARD_FLAG -std=c++1z) set(CXX_STANDARD_FLAG -std=c++1z)
endif () endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 14) elseif (CMAKE_CXX_STANDARD EQUAL 14)
check_cxx_compiler_flag(-std=c++14 has_std_14_flag) fmt_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++1y has_std_1y_flag)
if (has_std_14_flag) if (has_std_14_flag)
set(CXX_STANDARD_FLAG -std=c++14) set(CXX_STANDARD_FLAG -std=c++14)
elseif (has_std_1y_flag) elseif (has_std_1y_flag)
set(CXX_STANDARD_FLAG -std=c++1y) set(CXX_STANDARD_FLAG -std=c++1y)
endif () endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 11) elseif (CMAKE_CXX_STANDARD EQUAL 11)
check_cxx_compiler_flag(-std=c++11 has_std_11_flag) fmt_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++0x has_std_0x_flag)
if (has_std_11_flag) if (has_std_11_flag)
set(CXX_STANDARD_FLAG -std=c++11) set(CXX_STANDARD_FLAG -std=c++11)
@ -45,26 +52,3 @@ elseif (CMAKE_CXX_STANDARD EQUAL 11)
set(CXX_STANDARD_FLAG -std=c++0x) set(CXX_STANDARD_FLAG -std=c++0x)
endif () endif ()
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 <variant> is available
set(CMAKE_REQUIRED_FLAGS -std=c++1z)
check_cxx_source_compiles("
#include <variant>
int main() {}"
FMT_HAS_VARIANT)
if (NOT FMT_HAS_VARIANT)
set (FMT_HAS_VARIANT OFF)
endif ()
set(CMAKE_REQUIRED_FLAGS )

View file

@ -1,4 +1,7 @@
@PACKAGE_INIT@ @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) check_required_components(fmt)

View file

@ -171,7 +171,7 @@ def main():
normal1 = compress_normal(normal1) normal1 = compress_normal(normal1)
print("""\ 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(singletons0u, singletons0l, 'singletons0', 'singletons0_lower')
print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower') print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower')

View file

@ -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}) add_library(test-main STATIC ${TEST_MAIN_SRC})
target_include_directories(test-main PUBLIC target_include_directories(test-main PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>) $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
target_link_libraries(test-main gtest) target_link_libraries(test-main gtest fmt)
include(CheckCXXCompilerFlag)
function(add_fmt_executable name) function(add_fmt_executable name)
add_executable(${name} ${ARGN}) add_executable(${name} ${ARGN})
@ -79,6 +77,15 @@ endif()
add_fmt_test(printf-test) add_fmt_test(printf-test)
add_fmt_test(ranges-test ranges-odr-test.cc) add_fmt_test(ranges-test ranges-odr-test.cc)
add_fmt_test(scan-test) 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) add_fmt_test(unicode-test HEADER_ONLY)
if (MSVC) if (MSVC)
target_compile_options(unicode-test PRIVATE /utf-8) target_compile_options(unicode-test PRIVATE /utf-8)
@ -128,9 +135,6 @@ if (NOT MSVC_STATIC_RUNTIME)
if (FMT_PEDANTIC) if (FMT_PEDANTIC)
target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif () 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_test(NAME posix-mock-test COMMAND posix-mock-test)
add_fmt_test(os-test) add_fmt_test(os-test)
endif () endif ()
@ -148,9 +152,7 @@ if (FMT_PEDANTIC)
target_include_directories( target_include_directories(
noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include) noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_options(noexception-test PRIVATE -fno-exceptions) target_compile_options(noexception-test PRIVATE -fno-exceptions)
if (FMT_PEDANTIC) target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
endif () endif ()
# Test that the library compiles without locale. # 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_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}" "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
"-DFMT_DIR=${CMAKE_SOURCE_DIR}" "-DCXX_STANDARD_FLAG=${CXX_STANDARD_FLAG}"
"-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}") "-DFMT_DIR=${CMAKE_SOURCE_DIR}")
# Test if the targets are found from the build directory. # Test if the targets are found from the build directory.
add_test(find-package-test ${CMAKE_CTEST_COMMAND} add_test(find-package-test ${CMAKE_CTEST_COMMAND}

View file

@ -557,6 +557,9 @@ TEST(chrono_test, special_durations) {
"03:33"); "03:33");
EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration<char, std::mega>{2}), EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration<char, std::mega>{2}),
"03:33:20"); "03:33:20");
EXPECT_EQ("44.000000000000",
fmt::format("{:%S}", std::chrono::duration<float, std::pico>(
1.54213895E+26)));
} }
TEST(chrono_test, unsigned_duration) { 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. // fixed precision, and print zeros even if there is no fractional part.
EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}), EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}),
"07.000000"); "07.000000");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 3>>(1)),
"00.333333");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 7>>(1)),
"00.142857");
} }
#endif // FMT_STATIC_THOUSANDS_SEPARATOR #endif // FMT_STATIC_THOUSANDS_SEPARATOR

View file

@ -50,6 +50,12 @@ TEST(color_test, format) {
"\x1b[105mtbmagenta\x1b[0m"); "\x1b[105mtbmagenta\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"), EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"),
"\x1b[31mfoo\x1b[0m"); "\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) { TEST(color_test, format_to) {

View file

@ -6,6 +6,8 @@ project(compile-error-test CXX)
set(fmt_headers " set(fmt_headers "
#include <fmt/format.h> #include <fmt/format.h>
#include <fmt/xchar.h> #include <fmt/xchar.h>
#include <fmt/ostream.h>
#include <iostream>
") ")
set(error_test_names "") set(error_test_names "")
@ -154,6 +156,28 @@ expect_compile(format-function-error "
fmt::format(\"{}\", f); fmt::format(\"{}\", f);
" ERROR) " 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 # Make sure that compiler features detected in the header
# match the features detected in CMake. # match the features detected in CMake.
if (SUPPORTS_USER_DEFINED_LITERALS) if (SUPPORTS_USER_DEFINED_LITERALS)
@ -181,16 +205,30 @@ if (CMAKE_CXX_STANDARD GREATER_EQUAL 20)
#error #error
#endif #endif
" ERROR) " 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 # Compile-time argument name check
expect_compile(format-string-name " 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; using namespace fmt::literals;
fmt::print(\"{foo}\", \"foo\"_a=42); fmt::print(\"{foo}\", \"foo\"_a=42);
#endif #endif
") ")
expect_compile(format-string-name-error " 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; using namespace fmt::literals;
fmt::print(\"{foo}\", \"bar\"_a=42); fmt::print(\"{foo}\", \"bar\"_a=42);
#else #else

View file

@ -11,7 +11,7 @@
#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 && \ #if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 && \
defined(__cpp_constexpr) && __cpp_constexpr >= 201907 && \ defined(__cpp_constexpr) && __cpp_constexpr >= 201907 && \
defined(__cpp_constexpr_dynamic_alloc) && \ defined(__cpp_constexpr_dynamic_alloc) && \
__cpp_constexpr_dynamic_alloc >= 201907 && __cplusplus >= 202002L __cpp_constexpr_dynamic_alloc >= 201907 && FMT_CPLUSPLUS >= 202002L
template <size_t max_string_length, typename Char = char> struct test_string { template <size_t max_string_length, typename Char = char> struct test_string {
template <typename T> constexpr bool operator==(const T& rhs) const noexcept { template <typename T> constexpr bool operator==(const T& rhs) const noexcept {
return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0; return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0;

View file

@ -187,7 +187,7 @@ TEST(compile_test, named) {
EXPECT_THROW(fmt::format(FMT_COMPILE("{invalid}"), fmt::arg("valid", 42)), EXPECT_THROW(fmt::format(FMT_COMPILE("{invalid}"), fmt::arg("valid", 42)),
fmt::format_error); fmt::format_error);
# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS # if FMT_USE_NONTYPE_TEMPLATE_ARGS
using namespace fmt::literals; using namespace fmt::literals;
auto statically_named_field_compiled = auto statically_named_field_compiled =
fmt::detail::compile<decltype("arg"_a = 42)>(FMT_COMPILE("{arg}")); fmt::detail::compile<decltype("arg"_a = 42)>(FMT_COMPILE("{arg}"));
@ -201,6 +201,11 @@ TEST(compile_test, named) {
# endif # 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) { TEST(compile_test, format_to) {
char buf[8]; char buf[8];
auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42); auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42);
@ -280,7 +285,7 @@ TEST(compile_test, print) {
} }
#endif #endif
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS #if FMT_USE_NONTYPE_TEMPLATE_ARGS
TEST(compile_test, compile_format_string_literal) { TEST(compile_test, compile_format_string_literal) {
using namespace fmt::literals; using namespace fmt::literals;
EXPECT_EQ("", fmt::format(""_cf)); EXPECT_EQ("", fmt::format(""_cf));
@ -289,8 +294,15 @@ TEST(compile_test, compile_format_string_literal) {
} }
#endif #endif
#if __cplusplus >= 202002L || \ // MSVS 2019 19.29.30145.0 - Support C++20 and OK.
(__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002) // MSVS 2022 19.32.31332.0 - compile-test.cc(362,3): fatal error C1001: Internal
// compiler error.
// (compiler file
// 'D:\a\_work\1\s\src\vctools\Compiler\CxxFE\sl\p1\c\constexpr\constexpr.cpp',
// line 8635)
#if ((FMT_CPLUSPLUS >= 202002L) && \
(!FMT_MSC_VERSION || FMT_MSC_VERSION < 1930)) || \
(FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
template <size_t max_string_length, typename Char = char> struct test_string { template <size_t max_string_length, typename Char = char> struct test_string {
template <typename T> constexpr bool operator==(const T& rhs) const noexcept { template <typename T> constexpr bool operator==(const T& rhs) const noexcept {
return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0; return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0;

View file

@ -128,7 +128,7 @@ TEST(core_test, buffer_appender) {
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
TEST(buffer_test, noncopyable) { TEST(buffer_test, noncopyable) {
EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value); EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value);
# if !FMT_MSC_VER # if !FMT_MSC_VERSION
// std::is_copy_assignable is broken in MSVC2013. // std::is_copy_assignable is broken in MSVC2013.
EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value); EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value);
# endif # endif
@ -136,7 +136,7 @@ TEST(buffer_test, noncopyable) {
TEST(buffer_test, nonmoveable) { TEST(buffer_test, nonmoveable) {
EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value); EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value);
# if !FMT_MSC_VER # if !FMT_MSC_VERSION
// std::is_move_assignable is broken in MSVC2013. // std::is_move_assignable is broken in MSVC2013.
EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value); EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value);
# endif # endif
@ -385,11 +385,11 @@ VISIT_TYPE(unsigned long, unsigned long long);
template <typename T> class numeric_arg_test : public testing::Test {}; template <typename T> class numeric_arg_test : public testing::Test {};
using types = using test_types =
testing::Types<bool, signed char, unsigned char, short, unsigned short, int, testing::Types<bool, signed char, unsigned char, short, unsigned short, int,
unsigned, long, unsigned long, long long, unsigned long long, unsigned, long, unsigned long, long long, unsigned long long,
float, double, long double>; float, double, long double>;
TYPED_TEST_SUITE(numeric_arg_test, types); TYPED_TEST_SUITE(numeric_arg_test, test_types);
template <typename T, fmt::enable_if_t<std::is_integral<T>::value, int> = 0> template <typename T, fmt::enable_if_t<std::is_integral<T>::value, int> = 0>
T test_value() { T test_value() {
@ -679,6 +679,7 @@ TEST(format_test, constexpr_parse_format_string) {
#endif // FMT_USE_CONSTEXPR #endif // FMT_USE_CONSTEXPR
struct enabled_formatter {}; struct enabled_formatter {};
struct enabled_ptr_formatter {};
struct disabled_formatter {}; struct disabled_formatter {};
struct disabled_formatter_convertible { struct disabled_formatter_convertible {
operator int() const { return 42; } operator int() const { return 42; }
@ -693,6 +694,16 @@ template <> struct formatter<enabled_formatter> {
return ctx.out(); return ctx.out();
} }
}; };
template <> struct formatter<enabled_ptr_formatter*> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(enabled_ptr_formatter*, format_context& ctx)
-> decltype(ctx.out()) {
return ctx.out();
}
};
FMT_END_NAMESPACE FMT_END_NAMESPACE
TEST(core_test, has_formatter) { TEST(core_test, has_formatter) {
@ -737,7 +748,34 @@ struct convertible_to_pointer {
operator const int*() const { return nullptr; } 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<convertible_to_pointer_formattable> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(convertible_to_pointer_formattable, format_context& ctx) const
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
}
};
FMT_END_NAMESPACE
enum class unformattable_scoped_enum {};
namespace test {
enum class formattable_scoped_enum {};
auto format_as(formattable_scoped_enum) -> int { return 42; }
struct convertible_to_enum {
operator formattable_scoped_enum() const { return {}; }
};
} // namespace test
TEST(core_test, is_formattable) { TEST(core_test, is_formattable) {
#if 0 #if 0
@ -758,6 +796,7 @@ TEST(core_test, is_formattable) {
static_assert(!fmt::is_formattable<fmt::basic_string_view<wchar_t>>::value, static_assert(!fmt::is_formattable<fmt::basic_string_view<wchar_t>>::value,
""); "");
static_assert(fmt::is_formattable<enabled_formatter>::value, ""); static_assert(fmt::is_formattable<enabled_formatter>::value, "");
static_assert(!fmt::is_formattable<enabled_ptr_formatter*>::value, "");
static_assert(!fmt::is_formattable<disabled_formatter>::value, ""); static_assert(!fmt::is_formattable<disabled_formatter>::value, "");
static_assert(fmt::is_formattable<disabled_formatter_convertible>::value, ""); static_assert(fmt::is_formattable<disabled_formatter_convertible>::value, "");
@ -765,19 +804,22 @@ TEST(core_test, is_formattable) {
static_assert(fmt::is_formattable<const const_formattable&>::value, ""); static_assert(fmt::is_formattable<const const_formattable&>::value, "");
static_assert(fmt::is_formattable<nonconst_formattable&>::value, ""); static_assert(fmt::is_formattable<nonconst_formattable&>::value, "");
#if !FMT_MSC_VER || FMT_MSC_VER >= 1910 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
static_assert(!fmt::is_formattable<const nonconst_formattable&>::value, ""); static_assert(!fmt::is_formattable<const nonconst_formattable&>::value, "");
#endif #endif
static_assert(!fmt::is_formattable<convertible_to_pointer>::value, ""); static_assert(!fmt::is_formattable<convertible_to_pointer>::value, "");
const auto f = convertible_to_pointer_formattable();
EXPECT_EQ(fmt::format("{}", f), "test");
static_assert(!fmt::is_formattable<void (*)()>::value, ""); static_assert(!fmt::is_formattable<void (*)()>::value, "");
struct s; struct s;
static_assert(!fmt::is_formattable<int(s::*)>::value, ""); static_assert(!fmt::is_formattable<int(s::*)>::value, "");
static_assert(!fmt::is_formattable<int (s::*)()>::value, ""); static_assert(!fmt::is_formattable<int (s::*)()>::value, "");
static_assert(!fmt::is_formattable<test_scoped_enum>::value, ""); static_assert(!fmt::is_formattable<unformattable_scoped_enum>::value, "");
static_assert(fmt::is_formattable<test::formattable_scoped_enum>::value, "");
static_assert(!fmt::is_formattable<test::convertible_to_enum>::value, "");
} }
TEST(core_test, format) { EXPECT_EQ(fmt::format("{}", 42), "42"); } TEST(core_test, format) { EXPECT_EQ(fmt::format("{}", 42), "42"); }
@ -788,6 +830,10 @@ TEST(core_test, format_to) {
EXPECT_EQ(s, "42"); EXPECT_EQ(s, "42");
} }
TEST(core_test, format_as) {
EXPECT_EQ(fmt::format("{}", test::formattable_scoped_enum()), "42");
}
struct convertible_to_int { struct convertible_to_int {
operator int() const { return 42; } 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. // 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 { struct explicitly_convertible_to_string_view {
explicit operator fmt::string_view() const { return "foo"; } explicit operator fmt::string_view() const { return "foo"; }
}; };
TEST(core_test, format_explicitly_convertible_to_string_view) { 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<explicitly_convertible_to_string_view>::value, "");
} }
# ifdef FMT_USE_STRING_VIEW # 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) { TEST(core_test, format_explicitly_convertible_to_std_string_view) {
EXPECT_EQ("foo", // Types explicitly convertible to string_view are not formattable by
fmt::format("{}", explicitly_convertible_to_std_string_view())); // default because it may introduce ODR violations.
static_assert(
!fmt::is_formattable<explicitly_convertible_to_std_string_view>::value,
"");
} }
# endif # endif
#endif #endif

View file

@ -0,0 +1,18 @@
// Formatting library for C++ - tests of formatters for standard library types
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include <exception> // _GLIBCXX_RELEASE & _LIBCPP_VERSION
#if defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8
# error libfound "stdc++fs"
#elif !defined(__apple_build_version__) && defined(_LIBCPP_VERSION) && \
_LIBCPP_VERSION >= 7000 && _LIBCPP_VERSION < 9000
# error libfound "c++fs"
#else
// none if std::filesystem does not require additional libraries
# error libfound ""
#endif

View file

@ -24,9 +24,9 @@ static_assert(!std::is_copy_constructible<bigint>::value, "");
static_assert(!std::is_copy_assignable<bigint>::value, ""); static_assert(!std::is_copy_assignable<bigint>::value, "");
TEST(bigint_test, construct) { TEST(bigint_test, construct) {
EXPECT_EQ("", fmt::format("{}", bigint())); EXPECT_EQ(fmt::to_string(bigint()), "");
EXPECT_EQ("42", fmt::format("{}", bigint(0x42))); EXPECT_EQ(fmt::to_string(bigint(0x42)), "42");
EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0))); EXPECT_EQ(fmt::to_string(bigint(0x123456789abcedf0)), "123456789abcedf0");
} }
TEST(bigint_test, compare) { TEST(bigint_test, compare) {
@ -72,63 +72,56 @@ TEST(bigint_test, add_compare) {
TEST(bigint_test, shift_left) { TEST(bigint_test, shift_left) {
bigint n(0x42); bigint n(0x42);
n <<= 0; n <<= 0;
EXPECT_EQ("42", fmt::format("{}", n)); EXPECT_EQ(fmt::to_string(n), "42");
n <<= 1; n <<= 1;
EXPECT_EQ("84", fmt::format("{}", n)); EXPECT_EQ(fmt::to_string(n), "84");
n <<= 25; n <<= 25;
EXPECT_EQ("108000000", fmt::format("{}", n)); EXPECT_EQ(fmt::to_string(n), "108000000");
} }
TEST(bigint_test, multiply) { TEST(bigint_test, multiply) {
bigint n(0x42); bigint n(0x42);
EXPECT_THROW(n *= 0, assertion_failure); EXPECT_THROW(n *= 0, assertion_failure);
n *= 1; n *= 1;
EXPECT_EQ("42", fmt::format("{}", n)); EXPECT_EQ(fmt::to_string(n), "42");
n *= 2; n *= 2;
EXPECT_EQ("84", fmt::format("{}", n)); EXPECT_EQ(fmt::to_string(n), "84");
n *= 0x12345678; n *= 0x12345678;
EXPECT_EQ("962fc95e0", fmt::format("{}", n)); EXPECT_EQ(fmt::to_string(n), "962fc95e0");
bigint bigmax(max_value<uint32_t>()); bigint bigmax(max_value<uint32_t>());
bigmax *= max_value<uint32_t>(); bigmax *= max_value<uint32_t>();
EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax)); EXPECT_EQ(fmt::to_string(bigmax), "fffffffe00000001");
bigmax.assign(max_value<uint64_t>());
bigmax *= max_value<uint64_t>();
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax));
}
TEST(bigint_test, accumulator) { const auto max64 = max_value<uint64_t>();
fmt::detail::accumulator acc; bigmax = max64;
EXPECT_EQ(acc.lower, 0); bigmax *= max64;
EXPECT_EQ(acc.upper, 0); EXPECT_EQ(fmt::to_string(bigmax), "fffffffffffffffe0000000000000001");
acc.upper = 12;
acc.lower = 34; const auto max128 = (fmt::detail::uint128_t(max64) << 64) | max64;
EXPECT_EQ(static_cast<uint32_t>(acc), 34); bigmax = max128;
acc += 56; bigmax *= max128;
EXPECT_EQ(acc.lower, 90); EXPECT_EQ(fmt::to_string(bigmax),
acc += max_value<uint64_t>(); "fffffffffffffffffffffffffffffffe00000000000000000000000000000001");
EXPECT_EQ(acc.upper, 13);
EXPECT_EQ(acc.lower, 89);
acc >>= 32;
EXPECT_EQ(acc.upper, 0);
EXPECT_EQ(acc.lower, 13 * 0x100000000);
} }
TEST(bigint_test, square) { TEST(bigint_test, square) {
bigint n0(0); bigint n0(0);
n0.square(); n0.square();
EXPECT_EQ("0", fmt::format("{}", n0)); EXPECT_EQ(fmt::to_string(n0), "0");
bigint n1(0x100); bigint n1(0x100);
n1.square(); n1.square();
EXPECT_EQ("10000", fmt::format("{}", n1)); EXPECT_EQ(fmt::to_string(n1), "10000");
bigint n2(0xfffffffff); bigint n2(0xfffffffff);
n2.square(); n2.square();
EXPECT_EQ("ffffffffe000000001", fmt::format("{}", n2)); EXPECT_EQ(fmt::to_string(n2), "ffffffffe000000001");
bigint n3(max_value<uint64_t>()); bigint n3(max_value<uint64_t>());
n3.square(); n3.square();
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", n3)); EXPECT_EQ(fmt::to_string(n3), "fffffffffffffffe0000000000000001");
bigint n4; bigint n4;
n4.assign_pow10(10); n4.assign_pow10(10);
EXPECT_EQ("2540be400", fmt::format("{}", n4)); EXPECT_EQ(fmt::to_string(n4), "2540be400");
} }
TEST(bigint_test, divmod_assign_zero_divisor) { TEST(bigint_test, divmod_assign_zero_divisor) {
@ -150,8 +143,8 @@ TEST(bigint_test, divmod_assign_unaligned) {
n2.assign_pow10(100); n2.assign_pow10(100);
int result = n1.divmod_assign(n2); int result = n1.divmod_assign(n2);
EXPECT_EQ(result, 9406); EXPECT_EQ(result, 9406);
EXPECT_EQ("10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96", EXPECT_EQ(fmt::to_string(n1),
fmt::format("{}", n1)); "10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96");
} }
TEST(bigint_test, divmod_assign) { TEST(bigint_test, divmod_assign) {
@ -159,19 +152,19 @@ TEST(bigint_test, divmod_assign) {
bigint n1(100); bigint n1(100);
int result = n1.divmod_assign(bigint(10)); int result = n1.divmod_assign(bigint(10));
EXPECT_EQ(result, 10); EXPECT_EQ(result, 10);
EXPECT_EQ("0", fmt::format("{}", n1)); EXPECT_EQ(fmt::to_string(n1), "0");
// pow(10, 100) / (42 << 320): // pow(10, 100) / (42 << 320):
n1.assign_pow10(100); n1.assign_pow10(100);
result = n1.divmod_assign(bigint(42) <<= 320); result = n1.divmod_assign(bigint(42) <<= 320);
EXPECT_EQ(result, 111); EXPECT_EQ(result, 111);
EXPECT_EQ("13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96", EXPECT_EQ(fmt::to_string(n1),
fmt::format("{}", n1)); "13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96");
// 42 / 100: // 42 / 100:
bigint n2(42); bigint n2(42);
n1.assign_pow10(2); n1.assign_pow10(2);
result = n2.divmod_assign(n1); result = n2.divmod_assign(n1);
EXPECT_EQ(result, 0); EXPECT_EQ(result, 0);
EXPECT_EQ("2a", fmt::format("{}", n2)); EXPECT_EQ(fmt::to_string(n2), "2a");
} }
template <bool is_iec559> void run_double_tests() { template <bool is_iec559> void run_double_tests() {
@ -190,8 +183,8 @@ TEST(fp_test, double_tests) {
TEST(fp_test, normalize) { TEST(fp_test, normalize) {
const auto v = fp(0xbeef, 42); const auto v = fp(0xbeef, 42);
auto normalized = normalize(v); auto normalized = normalize(v);
EXPECT_EQ(0xbeef000000000000, normalized.f); EXPECT_EQ(normalized.f, 0xbeef000000000000);
EXPECT_EQ(-6, normalized.e); EXPECT_EQ(normalized.e, -6);
} }
TEST(fp_test, multiply) { TEST(fp_test, multiply) {
@ -207,18 +200,18 @@ TEST(fp_test, get_cached_power) {
using limits = std::numeric_limits<double>; using limits = std::numeric_limits<double>;
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) { for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
int dec_exp = 0; int dec_exp = 0;
auto fp = fmt::detail::get_cached_power(exp, dec_exp); auto power = fmt::detail::get_cached_power(exp, dec_exp);
bigint exact, cache(fp.f); bigint exact, cache(power.f);
if (dec_exp >= 0) { if (dec_exp >= 0) {
exact.assign_pow10(dec_exp); exact.assign_pow10(dec_exp);
if (fp.e <= 0) if (power.e <= 0)
exact <<= -fp.e; exact <<= -power.e;
else else
cache <<= fp.e; cache <<= power.e;
exact.align(cache); exact.align(cache);
cache.align(exact); cache.align(exact);
auto exact_str = fmt::format("{}", exact); auto exact_str = fmt::to_string(exact);
auto cache_str = fmt::format("{}", cache); auto cache_str = fmt::to_string(cache);
EXPECT_EQ(exact_str.size(), cache_str.size()); EXPECT_EQ(exact_str.size(), cache_str.size());
EXPECT_EQ(exact_str.substr(0, 15), cache_str.substr(0, 15)); EXPECT_EQ(exact_str.substr(0, 15), cache_str.substr(0, 15));
int diff = cache_str[15] - exact_str[15]; int diff = cache_str[15] - exact_str[15];
@ -228,12 +221,12 @@ TEST(fp_test, get_cached_power) {
EXPECT_EQ(diff, 0); EXPECT_EQ(diff, 0);
} else { } else {
cache.assign_pow10(-dec_exp); cache.assign_pow10(-dec_exp);
cache *= fp.f + 1; // Inexact check. cache *= power.f + 1; // Inexact check.
exact.assign(1); exact = 1;
exact <<= -fp.e; exact <<= -power.e;
exact.align(cache); exact.align(cache);
auto exact_str = fmt::format("{}", exact); auto exact_str = fmt::to_string(exact);
auto cache_str = fmt::format("{}", cache); auto cache_str = fmt::to_string(cache);
EXPECT_EQ(exact_str.size(), cache_str.size()); EXPECT_EQ(exact_str.size(), cache_str.size());
EXPECT_EQ(exact_str.substr(0, 16), cache_str.substr(0, 16)); 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) { TEST(fp_test, dragonbox_max_k) {
using fmt::detail::dragonbox::floor_log10_pow2; using fmt::detail::dragonbox::floor_log10_pow2;
using float_info = fmt::detail::dragonbox::float_info<float>; using float_info = fmt::detail::dragonbox::float_info<float>;
EXPECT_EQ(fmt::detail::const_check(float_info::max_k), EXPECT_EQ(
float_info::kappa - floor_log10_pow2(float_info::min_exponent - fmt::detail::const_check(float_info::max_k),
float_info::significand_bits)); float_info::kappa -
floor_log10_pow2(std::numeric_limits<float>::min_exponent -
fmt::detail::num_significand_bits<float>() - 1));
using double_info = fmt::detail::dragonbox::float_info<double>; using double_info = fmt::detail::dragonbox::float_info<double>;
EXPECT_EQ( EXPECT_EQ(
fmt::detail::const_check(double_info::max_k), fmt::detail::const_check(double_info::max_k),
double_info::kappa - floor_log10_pow2(double_info::min_exponent - double_info::kappa -
double_info::significand_bits)); floor_log10_pow2(std::numeric_limits<double>::min_exponent -
fmt::detail::num_significand_bits<double>() - 1));
} }
TEST(fp_test, get_round_direction) { TEST(fp_test, get_round_direction) {
using fmt::detail::get_round_direction; using fmt::detail::get_round_direction;
using fmt::detail::round_direction; using fmt::detail::round_direction;
EXPECT_EQ(round_direction::down, get_round_direction(100, 50, 0)); EXPECT_EQ(get_round_direction(100, 50, 0), round_direction::down);
EXPECT_EQ(round_direction::up, get_round_direction(100, 51, 0)); EXPECT_EQ(get_round_direction(100, 51, 0), round_direction::up);
EXPECT_EQ(round_direction::down, get_round_direction(100, 40, 10)); EXPECT_EQ(get_round_direction(100, 40, 10), round_direction::down);
EXPECT_EQ(round_direction::up, get_round_direction(100, 60, 10)); EXPECT_EQ(get_round_direction(100, 60, 10), round_direction::up);
for (size_t i = 41; i < 60; ++i) 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<uint64_t>(); uint64_t max = max_value<uint64_t>();
EXPECT_THROW(get_round_direction(100, 100, 0), assertion_failure); 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, 100), assertion_failure);
EXPECT_THROW(get_round_direction(100, 0, 50), assertion_failure); EXPECT_THROW(get_round_direction(100, 0, 50), assertion_failure);
// Check that remainder + error doesn't overflow. // 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. // Check that 2 * (remainder + error) doesn't overflow.
EXPECT_EQ(round_direction::unknown, EXPECT_EQ(get_round_direction(max, max / 2 + 1, max / 2),
get_round_direction(max, max / 2 + 1, max / 2)); round_direction::unknown);
// Check that remainder - error doesn't overflow. // 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. // 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) { TEST(fp_test, fixed_handler) {
@ -297,20 +293,20 @@ TEST(fp_test, fixed_handler) {
} }
TEST(fp_test, grisu_format_compiles_with_on_ieee_double) { 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); format_float(0.42, -1, fmt::detail::float_specs(), buf);
} }
TEST(format_impl_test, format_error_code) { TEST(format_impl_test, format_error_code) {
std::string msg = "error 42", sep = ": "; std::string msg = "error 42", sep = ": ";
{ {
fmt::memory_buffer buffer; auto buffer = fmt::memory_buffer();
format_to(fmt::appender(buffer), "garbage"); format_to(fmt::appender(buffer), "garbage");
fmt::detail::format_error_code(buffer, 42, "test"); 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 = auto prefix =
std::string(fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x'); std::string(fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x');
fmt::detail::format_error_code(buffer, 42, prefix); 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. // Test with a message that doesn't fit into the buffer.
prefix += 'x'; prefix += 'x';
fmt::detail::format_error_code(buffer, codes[i], prefix); 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 <typename Int> void test_count_digits() {
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::detail::count_digits(i)); for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::detail::count_digits(i));
for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) { for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) {
n *= 10; n *= 10;
EXPECT_EQ(i, fmt::detail::count_digits(n - 1)); EXPECT_EQ(fmt::detail::count_digits(n - 1), i);
EXPECT_EQ(i + 1, fmt::detail::count_digits(n)); EXPECT_EQ(fmt::detail::count_digits(n), i + 1);
} }
} }
@ -357,21 +353,51 @@ TEST(format_impl_test, count_digits) {
test_count_digits<uint64_t>(); test_count_digits<uint64_t>();
} }
TEST(format_impl_test, write_fallback_uintptr) { #if FMT_USE_FLOAT128
std::string s; TEST(format_impl_test, write_float128) {
fmt::detail::write_ptr<char>( auto s = std::string();
std::back_inserter(s), fmt::detail::write<char>(std::back_inserter(s), __float128(42));
fmt::detail::fallback_uintptr(reinterpret_cast<void*>(0xface)), nullptr); EXPECT_EQ(s, "42");
EXPECT_EQ(s, "0xface"); }
#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<double_double> : std::true_type {};
template <> struct numeric_limits<double_double> {
// is_iec559 is true for double-double in libstdc++.
static constexpr bool is_iec559 = true;
static constexpr int digits = 106;
};
} // namespace std
TEST(format_impl_test, write_double_double) {
auto s = std::string();
fmt::detail::write<char>(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 #ifdef _WIN32
# include <windows.h> # include <windows.h>
#endif
#ifdef _WIN32
TEST(format_impl_test, write_console_signature) { TEST(format_impl_test, write_console_signature) {
decltype(WriteConsoleW)* p = fmt::detail::WriteConsoleW; decltype(::WriteConsoleW)* p = fmt::detail::WriteConsoleW;
(void)p; (void)p;
} }
#endif #endif

View file

@ -33,12 +33,98 @@ using fmt::memory_buffer;
using fmt::runtime; using fmt::runtime;
using fmt::string_view; using fmt::string_view;
using fmt::detail::max_value; using fmt::detail::max_value;
using fmt::detail::uint128_fallback;
using testing::Return; using testing::Return;
using testing::StrictMock; using testing::StrictMock;
enum { buffer_size = 256 }; enum { buffer_size = 256 };
TEST(uint128_test, ctor) {
auto n = uint128_fallback();
EXPECT_EQ(n, 0);
n = uint128_fallback(42);
EXPECT_EQ(n, 42);
EXPECT_EQ(static_cast<uint64_t>(n), 42);
}
TEST(uint128_test, shift) {
auto n = uint128_fallback(42);
n = n << 64;
EXPECT_EQ(static_cast<uint64_t>(n), 0);
n = n >> 64;
EXPECT_EQ(static_cast<uint64_t>(n), 42);
n = n << 62;
EXPECT_EQ(static_cast<uint64_t>(n >> 64), 0xa);
EXPECT_EQ(static_cast<uint64_t>(n), 0x8000000000000000);
n = n >> 62;
EXPECT_EQ(static_cast<uint64_t>(n), 42);
}
TEST(uint128_test, minus) {
auto n = uint128_fallback(42);
EXPECT_EQ(n - 2, 40);
}
TEST(uint128_test, plus_assign) {
auto n = uint128_fallback(32);
n += uint128_fallback(10);
EXPECT_EQ(n, 42);
n = uint128_fallback(max_value<uint64_t>());
n += uint128_fallback(1);
EXPECT_EQ(n, uint128_fallback(1) << 64);
}
TEST(uint128_test, multiply) {
auto n = uint128_fallback(2251799813685247);
n = n * 3611864890;
EXPECT_EQ(static_cast<uint64_t>(n >> 64), 440901);
}
template <typename Float> void check_isfinite() {
using fmt::detail::isfinite;
EXPECT_TRUE(isfinite(Float(0.0)));
EXPECT_TRUE(isfinite(Float(42.0)));
EXPECT_TRUE(isfinite(Float(-42.0)));
EXPECT_TRUE(isfinite(Float(fmt::detail::max_value<double>())));
// Use double because std::numeric_limits is broken for __float128.
using limits = std::numeric_limits<double>;
FMT_CONSTEXPR20 auto result = isfinite(Float(limits::infinity()));
EXPECT_FALSE(result);
EXPECT_FALSE(isfinite(Float(limits::infinity())));
EXPECT_FALSE(isfinite(Float(-limits::infinity())));
EXPECT_FALSE(isfinite(Float(limits::quiet_NaN())));
EXPECT_FALSE(isfinite(Float(-limits::quiet_NaN())));
}
TEST(float_test, isfinite) {
check_isfinite<double>();
#ifdef __SIZEOF_FLOAT128__
check_isfinite<fmt::detail::float128>();
#endif
}
template <typename Float> void check_isnan() {
using fmt::detail::isnan;
EXPECT_FALSE(isnan(Float(0.0)));
EXPECT_FALSE(isnan(Float(42.0)));
EXPECT_FALSE(isnan(Float(-42.0)));
EXPECT_FALSE(isnan(Float(fmt::detail::max_value<double>())));
// Use double because std::numeric_limits is broken for __float128.
using limits = std::numeric_limits<double>;
EXPECT_FALSE(isnan(Float(limits::infinity())));
EXPECT_FALSE(isnan(Float(-limits::infinity())));
EXPECT_TRUE(isnan(Float(limits::quiet_NaN())));
EXPECT_TRUE(isnan(Float(-limits::quiet_NaN())));
}
TEST(float_test, isnan) {
check_isnan<double>();
#ifdef __SIZEOF_FLOAT128__
check_isnan<fmt::detail::float128>();
#endif
}
struct uint32_pair { struct uint32_pair {
uint32_t u[2]; uint32_t u[2];
}; };
@ -223,8 +309,9 @@ TEST(memory_buffer_test, move_ctor_dynamic_buffer) {
buffer.push_back('a'); buffer.push_back('a');
basic_memory_buffer<char, 4, std_allocator> buffer2(std::move(buffer)); basic_memory_buffer<char, 4, std_allocator> buffer2(std::move(buffer));
// Move should rip the guts of the first buffer. // Move should rip the guts of the first buffer.
EXPECT_EQ(inline_buffer_ptr, &buffer[0]); EXPECT_EQ(&buffer[0], inline_buffer_ptr);
EXPECT_EQ("testa", std::string(&buffer2[0], buffer2.size())); EXPECT_EQ(buffer.size(), 0);
EXPECT_EQ(std::string(&buffer2[0], buffer2.size()), "testa");
EXPECT_GT(buffer2.capacity(), 4u); EXPECT_GT(buffer2.capacity(), 4u);
} }
@ -325,7 +412,7 @@ template <typename Allocator, size_t MaxSize>
class max_size_allocator : public Allocator { class max_size_allocator : public Allocator {
public: public:
using typename Allocator::value_type; 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) { value_type* allocate(size_t n) {
if (n > max_size()) { if (n > max_size()) {
throw std::length_error("size > 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, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ul), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ("+42", fmt::format("{0:+}", 42ll)); 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, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ull), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ("+42", fmt::format("{0:+}", 42.0)); EXPECT_EQ("+42", fmt::format("{0:+}", 42.0));
@ -918,6 +1008,14 @@ TEST(format_test, precision) {
EXPECT_THAT(outputs, EXPECT_THAT(outputs,
testing::Contains(fmt::format("{:.838A}", -2.14001164E+38))); testing::Contains(fmt::format("{:.838A}", -2.14001164E+38)));
if (std::numeric_limits<long double>::digits == 64) {
auto ld = (std::numeric_limits<long double>::min)();
EXPECT_EQ(fmt::format("{:.0}", ld), "3e-4932");
EXPECT_EQ(
fmt::format("{:0g}", std::numeric_limits<long double>::denorm_min()),
"3.6452e-4951");
}
EXPECT_EQ("123.", fmt::format("{:#.0f}", 123.0)); EXPECT_EQ("123.", fmt::format("{:#.0f}", 123.0));
EXPECT_EQ("1.23", fmt::format("{:.02f}", 1.234)); EXPECT_EQ("1.23", fmt::format("{:.02f}", 1.234));
EXPECT_EQ("0.001", fmt::format("{:.1g}", 0.001)); EXPECT_EQ("0.001", fmt::format("{:.1g}", 0.001));
@ -939,6 +1037,7 @@ TEST(format_test, precision) {
format_error, "number is too big"); format_error, "number is too big");
EXPECT_EQ("st", fmt::format("{0:.2}", "str")); EXPECT_EQ("st", fmt::format("{0:.2}", "str"));
EXPECT_EQ("вожык", fmt::format("{0:.5}", "вожыкі"));
} }
TEST(format_test, runtime_precision) { TEST(format_test, runtime_precision) {
@ -1060,7 +1159,7 @@ TEST(format_test, format_short) {
template <typename T> template <typename T>
void check_unknown_types(const T& value, const char* types, const char*) { void check_unknown_types(const T& value, const char* types, const char*) {
char format_str[buffer_size]; char format_str[buffer_size];
const char* special = ".0123456789L}"; const char* special = ".0123456789L?}";
for (int i = CHAR_MIN; i <= CHAR_MAX; ++i) { for (int i = CHAR_MIN; i <= CHAR_MAX; ++i) {
char c = static_cast<char>(i); char c = static_cast<char>(i);
if (std::strchr(types, c) || std::strchr(special, c) || !c) continue; 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) { 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"); check_unknown_types(1.2, "eEfFgGaAnL%", "double");
EXPECT_EQ("0", fmt::format("{:}", 0.0)); EXPECT_EQ(fmt::format("{:}", 0.0), "0");
EXPECT_EQ("0.000000", fmt::format("{:f}", 0.0)); EXPECT_EQ(fmt::format("{:f}", 0.0), "0.000000");
EXPECT_EQ("0", fmt::format("{:g}", 0.0)); EXPECT_EQ(fmt::format("{:g}", 0.0), "0");
EXPECT_EQ("392.65", fmt::format("{:}", 392.65)); EXPECT_EQ(fmt::format("{:}", 392.65), "392.65");
EXPECT_EQ("392.65", fmt::format("{:g}", 392.65)); EXPECT_EQ(fmt::format("{:g}", 392.65), "392.65");
EXPECT_EQ("392.65", fmt::format("{:G}", 392.65)); EXPECT_EQ(fmt::format("{:G}", 392.65), "392.65");
EXPECT_EQ("4.9014e+06", fmt::format("{:g}", 4.9014e6)); EXPECT_EQ(fmt::format("{:g}", 4.9014e6), "4.9014e+06");
EXPECT_EQ("392.650000", fmt::format("{:f}", 392.65)); EXPECT_EQ(fmt::format("{:f}", 392.65), "392.650000");
EXPECT_EQ("392.650000", fmt::format("{:F}", 392.65)); EXPECT_EQ(fmt::format("{:F}", 392.65), "392.650000");
EXPECT_EQ("42", fmt::format("{:L}", 42.0)); EXPECT_EQ(fmt::format("{:L}", 42.0), "42");
EXPECT_EQ(" 0x1.0cccccccccccdp+2", fmt::format("{:24a}", 4.2)); EXPECT_EQ(fmt::format("{:24a}", 4.2), " 0x1.0cccccccccccdp+2");
EXPECT_EQ("0x1.0cccccccccccdp+2 ", fmt::format("{:<24a}", 4.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]; 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); 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); safe_sprintf(buffer, "%A", -42.0);
EXPECT_EQ(buffer, fmt::format("{:A}", -42.0)); EXPECT_EQ(fmt::format("{:A}", -42.0), buffer);
EXPECT_EQ("9223372036854775808.000000", EXPECT_EQ(fmt::format("{:f}", 9223372036854775807.0),
fmt::format("{:f}", 9223372036854775807.0)); "9223372036854775808.000000");
} }
TEST(format_test, precision_rounding) { TEST(format_test, precision_rounding) {
@ -1363,6 +1460,9 @@ TEST(format_test, format_char) {
<< format_str; << format_str;
} }
EXPECT_EQ(fmt::format("{:02X}", n), fmt::format("{:02X}", 'x')); 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) { TEST(format_test, format_volatile_char) {
@ -1393,7 +1493,11 @@ TEST(format_test, format_pointer) {
EXPECT_EQ("0x0", fmt::format("{0}", static_cast<void*>(nullptr))); EXPECT_EQ("0x0", fmt::format("{0}", static_cast<void*>(nullptr)));
EXPECT_EQ("0x1234", fmt::format("{0}", reinterpret_cast<void*>(0x1234))); EXPECT_EQ("0x1234", fmt::format("{0}", reinterpret_cast<void*>(0x1234)));
EXPECT_EQ("0x1234", fmt::format("{0:p}", reinterpret_cast<void*>(0x1234))); EXPECT_EQ("0x1234", fmt::format("{0:p}", reinterpret_cast<void*>(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<void*>(~uintptr_t()))); fmt::format("{0}", reinterpret_cast<void*>(~uintptr_t())));
EXPECT_EQ("0x1234", EXPECT_EQ("0x1234",
fmt::format("{}", fmt::ptr(reinterpret_cast<int*>(0x1234)))); fmt::format("{}", fmt::ptr(reinterpret_cast<int*>(0x1234))));
@ -1409,14 +1513,43 @@ TEST(format_test, format_pointer) {
EXPECT_EQ("0x0", fmt::format("{}", nullptr)); EXPECT_EQ("0x0", fmt::format("{}", nullptr));
} }
TEST(format_test, write_uintptr_fallback) {
// Test that formatting a pointer by converting it to uint128_fallback works.
// This is needed to support systems without uintptr_t.
auto s = std::string();
fmt::detail::write_ptr<char>(
std::back_inserter(s),
fmt::detail::bit_cast<fmt::detail::uint128_fallback>(
reinterpret_cast<void*>(0xface)),
nullptr);
EXPECT_EQ(s, "0xface");
}
enum class color { red, green, blue };
namespace test_ns {
enum class color { red, green, blue };
using fmt::enums::format_as;
} // namespace test_ns
TEST(format_test, format_enum_class) {
EXPECT_EQ(fmt::format("{}", fmt::underlying(color::red)), "0");
EXPECT_EQ(fmt::format("{}", test_ns::color::red), "0");
}
TEST(format_test, format_string) { 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")), EXPECT_THROW((void)fmt::format(fmt::runtime("{:x}"), std::string("test")),
fmt::format_error); fmt::format_error);
} }
TEST(format_test, format_string_view) { TEST(format_test, format_string_view) {
EXPECT_EQ("test", fmt::format("{}", string_view("test"))); EXPECT_EQ("test", fmt::format("{}", string_view("test")));
EXPECT_EQ("\"t\\nst\"", fmt::format("{:?}", string_view("t\nst")));
EXPECT_EQ("", fmt::format("{}", string_view())); EXPECT_EQ("", fmt::format("{}", string_view()));
} }
@ -1623,6 +1756,7 @@ TEST(format_test, group_digits_view) {
} }
enum test_enum { foo, bar }; enum test_enum { foo, bar };
auto format_as(test_enum e) -> int { return e; }
TEST(format_test, join) { TEST(format_test, join) {
using fmt::join; using fmt::join;
@ -1652,9 +1786,15 @@ TEST(format_test, join) {
} }
#ifdef __cpp_lib_byte #ifdef __cpp_lib_byte
TEST(format_test, format_byte) {
using arg_mapper = fmt::detail::arg_mapper<fmt::format_context>;
EXPECT_EQ(arg_mapper().map(std::byte(42)), 42);
EXPECT_EQ(fmt::format("{}", std::byte(42)), "42");
}
TEST(format_test, join_bytes) { TEST(format_test, join_bytes) {
auto v = std::vector<std::byte>{std::byte(1), std::byte(2), std::byte(3)}; auto v = std::vector<std::byte>{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 #endif
@ -1694,20 +1834,21 @@ fmt::string_view to_string_view(string_like) { return "foo"; }
constexpr char with_null[3] = {'{', '}', '\0'}; constexpr char with_null[3] = {'{', '}', '\0'};
constexpr char no_null[2] = {'{', '}'}; constexpr char no_null[2] = {'{', '}'};
static FMT_CONSTEXPR_DECL const char static_with_null[3] = {'{', '}', '\0'}; static constexpr const char static_with_null[3] = {'{', '}', '\0'};
static FMT_CONSTEXPR_DECL const char static_no_null[2] = {'{', '}'}; static constexpr const char static_no_null[2] = {'{', '}'};
TEST(format_test, compile_time_string) { TEST(format_test, compile_time_string) {
EXPECT_EQ("foo", fmt::format(FMT_STRING("foo"))); EXPECT_EQ("foo", fmt::format(FMT_STRING("foo")));
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like())); 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; using namespace fmt::literals;
EXPECT_EQ("foobar", fmt::format(FMT_STRING("{foo}{bar}"), "bar"_a = "bar", EXPECT_EQ("foobar", fmt::format(FMT_STRING("{foo}{bar}"), "bar"_a = "bar",
"foo"_a = "foo")); "foo"_a = "foo"));
EXPECT_EQ("", fmt::format(FMT_STRING(""))); EXPECT_EQ("", fmt::format(FMT_STRING("")));
EXPECT_EQ("", fmt::format(FMT_STRING(""), "arg"_a = 42)); EXPECT_EQ("", fmt::format(FMT_STRING(""), "arg"_a = 42));
EXPECT_EQ("42", fmt::format(FMT_STRING("{answer}"), "answer"_a = Answer()));
#endif #endif
(void)static_with_null; (void)static_with_null;
@ -1719,11 +1860,11 @@ TEST(format_test, compile_time_string) {
(void)with_null; (void)with_null;
(void)no_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(with_null), 42));
EXPECT_EQ("42", fmt::format(FMT_STRING(no_null), 42)); EXPECT_EQ("42", fmt::format(FMT_STRING(no_null), 42));
#endif #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)); EXPECT_EQ("42", fmt::format(FMT_STRING(std::string_view("{}")), 42));
#endif #endif
} }
@ -1739,44 +1880,16 @@ TEST(format_test, custom_format_compile_time_string) {
} }
#if FMT_USE_USER_DEFINED_LITERALS #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) { TEST(format_test, named_arg_udl) {
using namespace fmt::literals;
auto udl_a = fmt::format("{first}{second}{first}{third}", "first"_a = "abra", auto udl_a = fmt::format("{first}{second}{first}{third}", "first"_a = "abra",
"second"_a = "cad", "third"_a = 99); "second"_a = "cad", "third"_a = 99);
EXPECT_EQ( EXPECT_EQ(
fmt::format("{first}{second}{first}{third}", fmt::arg("first", "abra"), fmt::format("{first}{second}{first}{third}", fmt::arg("first", "abra"),
fmt::arg("second", "cad"), fmt::arg("third", 99)), fmt::arg("second", "cad"), fmt::arg("third", 99)),
udl_a); udl_a);
EXPECT_EQ("42", fmt::format("{answer}", "answer"_a = Answer()));
} }
#endif // FMT_USE_USER_DEFINED_LITERALS #endif // FMT_USE_USER_DEFINED_LITERALS
@ -1790,6 +1903,7 @@ TEST(format_test, formatter_not_specialized) {
#if FMT_HAS_FEATURE(cxx_strong_enums) #if FMT_HAS_FEATURE(cxx_strong_enums)
enum big_enum : unsigned long long { big_enum_value = 5000000000ULL }; 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) { TEST(format_test, strong_enum) {
EXPECT_EQ("5000000000", fmt::format("{}", big_enum_value)); EXPECT_EQ("5000000000", fmt::format("{}", big_enum_value));
@ -1871,9 +1985,11 @@ TEST(format_test, to_string) {
EXPECT_EQ(fmt::to_string(reinterpret_cast<void*>(0x1234)), "0x1234"); EXPECT_EQ(fmt::to_string(reinterpret_cast<void*>(0x1234)), "0x1234");
EXPECT_EQ(fmt::to_string(adl_test::fmt::detail::foo()), "foo"); EXPECT_EQ(fmt::to_string(adl_test::fmt::detail::foo()), "foo");
EXPECT_EQ(fmt::to_string(convertible_to_int()), "42"); EXPECT_EQ(fmt::to_string(convertible_to_int()), "42");
EXPECT_EQ(fmt::to_string(foo), "0");
enum foo : unsigned char { zero }; #if FMT_USE_FLOAT128
EXPECT_EQ(fmt::to_string(zero), "0"); EXPECT_EQ(fmt::to_string(__float128(0.5)), "0.5");
#endif
} }
TEST(format_test, output_iterators) { TEST(format_test, output_iterators) {
@ -2038,7 +2154,7 @@ TEST(format_test, format_string_errors) {
EXPECT_ERROR_NOARGS("foo", nullptr); EXPECT_ERROR_NOARGS("foo", nullptr);
EXPECT_ERROR_NOARGS("}", "unmatched '}' in format string"); EXPECT_ERROR_NOARGS("}", "unmatched '}' in format string");
EXPECT_ERROR("{0:s", "unknown format specifier", date); 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. // This causes an detail compiler error in MSVC2017.
EXPECT_ERROR("{:{<}", "invalid fill character '{'", int); EXPECT_ERROR("{:{<}", "invalid fill character '{'", int);
EXPECT_ERROR("{:10000000000}", "number is too big", int); EXPECT_ERROR("{:10000000000}", "number is too big", int);
@ -2070,7 +2186,8 @@ TEST(format_test, format_string_errors) {
# else # else
fmt::print("warning: constexpr is broken in this version of MSVC\n"); fmt::print("warning: constexpr is broken in this version of MSVC\n");
# endif # 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("bar"_a = 42));
EXPECT_ERROR("{foo}", "named argument is not found", EXPECT_ERROR("{foo}", "named argument is not found",
decltype(fmt::arg("foo", 42))); decltype(fmt::arg("foo", 42)));
@ -2116,7 +2233,7 @@ TEST(format_test, char_traits_is_not_ambiguous) {
using namespace std; using namespace std;
auto c = char_traits<char>::char_type(); auto c = char_traits<char>::char_type();
(void)c; (void)c;
#if __cplusplus >= 201103L #if FMT_CPLUSPLUS >= 201103L
auto s = std::string(); auto s = std::string();
auto lval = begin(s); auto lval = begin(s);
(void)lval; (void)lval;

View file

@ -30,8 +30,8 @@ void invoke_fmt(const uint8_t* data, size_t size) {
#if FMT_FUZZ_FORMAT_TO_STRING #if FMT_FUZZ_FORMAT_TO_STRING
std::string message = fmt::format(format_str.get(), *value); std::string message = fmt::format(format_str.get(), *value);
#else #else
fmt::memory_buffer message; auto buf = fmt::memory_buffer();
fmt::format_to(message, format_str.get(), *value); fmt::format_to(std::back_inserter(buf), format_str.get(), *value);
#endif #endif
} catch (std::exception&) { } catch (std::exception&) {
} }

View file

@ -27,8 +27,8 @@ void invoke_fmt(const uint8_t* data, size_t size) {
#if FMT_FUZZ_FORMAT_TO_STRING #if FMT_FUZZ_FORMAT_TO_STRING
std::string message = fmt::format(format_str, item1, item2); std::string message = fmt::format(format_str, item1, item2);
#else #else
fmt::memory_buffer message; auto buf = fmt::memory_buffer();
fmt::format_to(message, format_str, item1, item2); fmt::format_to(std::back_inserter(buf), format_str, item1, item2);
#endif #endif
} }

View file

@ -347,7 +347,7 @@ TEST(output_redirect_test, flush_error_in_ctor) {
TEST(output_redirect_test, dup_error_in_ctor) { TEST(output_redirect_test, dup_error_in_ctor) {
buffered_file f = open_buffered_file(); buffered_file f = open_buffered_file();
int fd = (f.fileno)(); int fd = (f.descriptor)();
file copy = file::dup(fd); file copy = file::dup(fd);
FMT_POSIX(close(fd)); FMT_POSIX(close(fd));
std::unique_ptr<output_redirect> redir{nullptr}; std::unique_ptr<output_redirect> redir{nullptr};

View file

@ -23,7 +23,7 @@ output_redirect::output_redirect(FILE* f) : file_(f) {
write_end.dup2(fd); write_end.dup2(fd);
} }
output_redirect::~output_redirect() FMT_NOEXCEPT { output_redirect::~output_redirect() noexcept {
try { try {
restore(); restore();
} catch (const std::exception& e) { } catch (const std::exception& e) {

View file

@ -83,7 +83,7 @@ class output_redirect {
public: public:
explicit output_redirect(FILE* file); explicit output_redirect(FILE* file);
~output_redirect() FMT_NOEXCEPT; ~output_redirect() noexcept;
output_redirect(const output_redirect&) = delete; output_redirect(const output_redirect&) = delete;
void operator=(const output_redirect&) = delete; void operator=(const output_redirect&) = delete;

View file

@ -18,7 +18,7 @@ else ()
endif () endif ()
# Workaround GTest bug https://github.com/google/googletest/issues/705. # 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) -fno-delete-null-pointer-checks HAVE_FNO_DELETE_NULL_POINTER_CHECKS)
if (HAVE_FNO_DELETE_NULL_POINTER_CHECKS) if (HAVE_FNO_DELETE_NULL_POINTER_CHECKS)
target_compile_options(gtest PUBLIC -fno-delete-null-pointer-checks) target_compile_options(gtest PUBLIC -fno-delete-null-pointer-checks)

View file

@ -36,7 +36,6 @@
#else #else
# define FMT_USE_FCNTL 0 # define FMT_USE_FCNTL 0
#endif #endif
#define FMT_NOEXCEPT noexcept
#if defined(_WIN32) && !defined(__MINGW32__) #if defined(_WIN32) && !defined(__MINGW32__)
# define FMT_POSIX(call) _##call # define FMT_POSIX(call) _##call
#else #else
@ -196,13 +195,6 @@ TEST(module_test, wformat_args) {
EXPECT_TRUE(args.get(0)); EXPECT_TRUE(args.get(0));
} }
TEST(module_test, checked_format_args) {
fmt::basic_format_args args = fmt::make_args_checked<int>("{}", 42);
EXPECT_TRUE(args.get(0));
fmt::basic_format_args wargs = fmt::make_args_checked<int>(L"{}", 42);
EXPECT_TRUE(wargs.get(0));
}
TEST(module_test, dynamic_format_args) { TEST(module_test, dynamic_format_args) {
fmt::dynamic_format_arg_store<fmt::format_context> dyn_store; fmt::dynamic_format_arg_store<fmt::format_context> dyn_store;
dyn_store.push_back(fmt::arg("a42", 42)); dyn_store.push_back(fmt::arg("a42", 42));

View file

@ -14,10 +14,6 @@
#include "gtest-extra.h" #include "gtest-extra.h"
#include "util.h" #include "util.h"
#ifdef fileno
# undef fileno
#endif
using fmt::buffered_file; using fmt::buffered_file;
using testing::HasSubstr; using testing::HasSubstr;
using wstring_view = fmt::basic_string_view<wchar_t>; using wstring_view = fmt::basic_string_view<wchar_t>;
@ -205,7 +201,7 @@ TEST(buffered_file_test, move_assignment) {
TEST(buffered_file_test, move_assignment_closes_file) { TEST(buffered_file_test, move_assignment_closes_file) {
buffered_file bf = open_buffered_file(); buffered_file bf = open_buffered_file();
buffered_file bf2 = open_buffered_file(); buffered_file bf2 = open_buffered_file();
int old_fd = bf2.fileno(); int old_fd = bf2.descriptor();
bf2 = std::move(bf); bf2 = std::move(bf);
EXPECT_TRUE(isclosed(old_fd)); 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) { TEST(buffered_file_test, move_from_temporary_in_assignment_closes_file) {
buffered_file f = open_buffered_file(); buffered_file f = open_buffered_file();
int old_fd = f.fileno(); int old_fd = f.descriptor();
f = open_buffered_file(); f = open_buffered_file();
EXPECT_TRUE(isclosed(old_fd)); EXPECT_TRUE(isclosed(old_fd));
} }
@ -234,7 +230,7 @@ TEST(buffered_file_test, close_file_in_dtor) {
int fd = 0; int fd = 0;
{ {
buffered_file f = open_buffered_file(); buffered_file f = open_buffered_file();
fd = f.fileno(); fd = f.descriptor();
} }
EXPECT_TRUE(isclosed(fd)); 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 // otherwise the system may recycle closed file descriptor when
// redirecting the output in EXPECT_STDERR and the second close // redirecting the output in EXPECT_STDERR and the second close
// will break output redirection. // will break output redirection.
FMT_POSIX(close(f->fileno())); FMT_POSIX(close(f->descriptor()));
SUPPRESS_ASSERT(f.reset(nullptr)); SUPPRESS_ASSERT(f.reset(nullptr));
}, },
system_error_message(EBADF, "cannot close file") + "\n"); 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) { TEST(buffered_file_test, close) {
buffered_file f = open_buffered_file(); buffered_file f = open_buffered_file();
int fd = f.fileno(); int fd = f.descriptor();
f.close(); f.close();
EXPECT_TRUE(f.get() == nullptr); EXPECT_TRUE(f.get() == nullptr);
EXPECT_TRUE(isclosed(fd)); EXPECT_TRUE(isclosed(fd));
@ -265,15 +261,15 @@ TEST(buffered_file_test, close) {
TEST(buffered_file_test, close_error) { TEST(buffered_file_test, close_error) {
buffered_file f = open_buffered_file(); 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_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
EXPECT_TRUE(f.get() == nullptr); EXPECT_TRUE(f.get() == nullptr);
} }
TEST(buffered_file_test, fileno) { TEST(buffered_file_test, descriptor) {
auto f = open_buffered_file(); auto f = open_buffered_file();
EXPECT_TRUE(f.fileno() != -1); EXPECT_TRUE(f.descriptor() != -1);
file copy = file::dup(f.fileno()); file copy = file::dup(f.descriptor());
EXPECT_READ(copy, file_content); EXPECT_READ(copy, file_content);
} }

View file

@ -5,6 +5,8 @@
// //
// For the license information refer to format.h. // For the license information refer to format.h.
#include <fstream>
#include "fmt/format.h" #include "fmt/format.h"
using fmt::runtime; using fmt::runtime;
@ -30,12 +32,12 @@ template <> struct formatter<test> : formatter<int> {
#include "gtest-extra.h" #include "gtest-extra.h"
#include "util.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(); os << d.year() << '-' << d.month() << '-' << d.day();
return os; 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(); os << d.year() << L'-' << d.month() << L'-' << d.day();
return os; return os;
} }
@ -47,11 +49,24 @@ template <typename T> type_with_comma_op operator<<(T&, const date&);
enum streamable_enum {}; enum streamable_enum {};
std::ostream& operator<<(std::ostream& os, streamable_enum) { auto operator<<(std::ostream& os, streamable_enum) -> std::ostream& {
return os << "streamable_enum"; return os << "streamable_enum";
} }
enum unstreamable_enum {}; enum unstreamable_enum {};
auto format_as(unstreamable_enum e) -> int { return e; }
struct empty_test {};
auto operator<<(std::ostream& os, empty_test) -> std::ostream& {
return os << "";
}
namespace fmt {
template <> struct formatter<test_string> : ostream_formatter {};
template <> struct formatter<date> : ostream_formatter {};
template <> struct formatter<streamable_enum> : ostream_formatter {};
template <> struct formatter<empty_test> : ostream_formatter {};
} // namespace fmt
TEST(ostream_test, enum) { TEST(ostream_test, enum) {
EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_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)); 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) { TEST(ostream_test, empty_custom_output) {
EXPECT_EQ("", fmt::format("{}", empty_test())); EXPECT_EQ("", fmt::format("{}", empty_test()));
} }
@ -121,7 +133,7 @@ TEST(ostream_test, write_to_ostream_max_size) {
struct mock_streambuf : std::streambuf { struct mock_streambuf : std::streambuf {
MOCK_METHOD2(xsputn, std::streamsize(const void* s, std::streamsize n)); 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; const void* v = s;
return xsputn(v, n); return xsputn(v, n);
} }
@ -158,15 +170,16 @@ TEST(ostream_test, join_fallback_formatter) {
#if FMT_USE_CONSTEXPR #if FMT_USE_CONSTEXPR
TEST(ostream_test, constexpr_string) { TEST(ostream_test, constexpr_string) {
EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42"))); EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), std::string("42")));
EXPECT_EQ("a string", format(FMT_STRING("{0}"), test_string("a string"))); EXPECT_EQ("a string",
fmt::format(FMT_STRING("{0}"), test_string("a string")));
} }
#endif #endif
namespace fmt_test { namespace fmt_test {
struct abc {}; struct abc {};
template <typename Output> Output& operator<<(Output& out, abc) { template <typename Output> auto operator<<(Output& out, abc) -> Output& {
return out << "abc"; return out << "abc";
} }
} // namespace fmt_test } // namespace fmt_test
@ -174,7 +187,7 @@ template <typename Output> Output& operator<<(Output& out, abc) {
template <typename T> struct test_template {}; template <typename T> struct test_template {};
template <typename T> template <typename T>
std::ostream& operator<<(std::ostream& os, test_template<T>) { auto operator<<(std::ostream& os, test_template<T>) -> std::ostream& {
return os << 1; return os << 1;
} }
@ -184,6 +197,8 @@ template <typename T> struct formatter<test_template<T>> : formatter<int> {
return formatter<int>::format(2, ctx); return formatter<int>::format(2, ctx);
} }
}; };
template <> struct formatter<fmt_test::abc> : ostream_formatter {};
} // namespace fmt } // namespace fmt
TEST(ostream_test, template) { TEST(ostream_test, template) {
@ -214,41 +229,6 @@ TEST(ostream_test, disable_builtin_ostream_operators) {
EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo"))); EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo")));
} }
struct explicitly_convertible_to_string_like {
template <typename String,
typename = typename std::enable_if<std::is_constructible<
String, const char*, size_t>::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<char>() 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 { struct streamable_and_convertible_to_bool {
operator bool() const { return true; } 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"); 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 {}; struct copyfmt_test {};
std::ostream& operator<<(std::ostream& os, 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"; return os << "foo";
} }
namespace fmt {
template <> struct formatter<copyfmt_test> : ostream_formatter {};
} // namespace fmt
TEST(ostream_test, copyfmt) { TEST(ostream_test, copyfmt) {
EXPECT_EQ("foo", fmt::format("{}", copyfmt_test())); EXPECT_EQ("foo", fmt::format("{}", copyfmt_test()));
} }
@ -286,11 +285,15 @@ TEST(ostream_test, range) {
struct abstract { struct abstract {
virtual ~abstract() = default; virtual ~abstract() = default;
virtual void f() = 0; 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; return os;
} }
}; };
namespace fmt {
template <> struct formatter<abstract> : ostream_formatter {};
} // namespace fmt
void format_abstract_compiles(const abstract& a) { void format_abstract_compiles(const abstract& a) {
fmt::format(FMT_COMPILE("{}"), a); fmt::format(FMT_COMPILE("{}"), a);
} }
@ -299,3 +302,21 @@ TEST(ostream_test, is_formattable) {
EXPECT_TRUE(fmt::is_formattable<std::string>()); EXPECT_TRUE(fmt::is_formattable<std::string>());
EXPECT_TRUE(fmt::is_formattable<fmt::detail::std_string_view<char>>()); EXPECT_TRUE(fmt::is_formattable<fmt::detail::std_string_view<char>>());
} }
struct streamable_and_unformattable {};
auto operator<<(std::ostream& os, streamable_and_unformattable)
-> std::ostream& {
return os << "foo";
}
TEST(ostream_test, streamed) {
EXPECT_FALSE(fmt::is_formattable<streamable_and_unformattable>());
EXPECT_EQ(fmt::format("{}", fmt::streamed(streamable_and_unformattable())),
"foo");
}
TEST(ostream_test, closed_ofstream) {
std::ofstream ofs;
fmt::print(ofs, "discard");
}

View file

@ -6,7 +6,7 @@
// For the license information refer to format.h. // For the license information refer to format.h.
// Disable bogus MSVC warnings. // Disable bogus MSVC warnings.
#ifndef _CRT_SECURE_NO_WARNINGS #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS # define _CRT_SECURE_NO_WARNINGS
#endif #endif
@ -438,7 +438,7 @@ TEST(buffered_file_test, fileno_no_retry) {
file::pipe(read_end, write_end); file::pipe(read_end, write_end);
buffered_file f = read_end.fdopen("r"); buffered_file f = read_end.fdopen("r");
fileno_count = 1; 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); EXPECT_EQ(2, fileno_count);
fileno_count = 0; fileno_count = 0;
} }
@ -457,84 +457,3 @@ TEST(scoped_mock, scope) {
} }
EXPECT_EQ(nullptr, test_mock::instance); 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<locale_mock> mock;
auto locale = reinterpret_cast<locale_type>(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<locale_mock> mock;
auto impl = reinterpret_cast<locale_type>(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

View file

@ -11,7 +11,6 @@
#include <climits> #include <climits>
#include <cstring> #include <cstring>
#include "fmt/ostream.h"
#include "fmt/xchar.h" #include "fmt/xchar.h"
#include "gtest-extra.h" #include "gtest-extra.h"
#include "util.h" #include "util.h"
@ -505,6 +504,7 @@ TEST(printf_test, pointer) {
} }
enum test_enum { answer = 42 }; enum test_enum { answer = 42 };
auto format_as(test_enum e) -> int { return e; }
TEST(printf_test, enum) { TEST(printf_test, enum) {
EXPECT_PRINTF("42", "%d", answer); EXPECT_PRINTF("42", "%d", answer);
@ -533,10 +533,6 @@ TEST(printf_test, wide_string) {
EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); 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) { TEST(printf_test, vprintf) {
fmt::format_arg_store<fmt::printf_context, int> as{42}; fmt::format_arg_store<fmt::printf_context, int> as{42};
fmt::basic_format_args<fmt::printf_context> args(as); fmt::basic_format_args<fmt::printf_context> args(as);

View file

@ -21,7 +21,7 @@
# define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY # define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
#endif #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_JOIN
# define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT # define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
#endif #endif
@ -46,11 +46,13 @@ TEST(ranges_test, format_array_of_literals) {
TEST(ranges_test, format_vector) { TEST(ranges_test, format_vector) {
auto v = std::vector<int>{1, 2, 3, 5, 7, 11}; auto v = std::vector<int>{1, 2, 3, 5, 7, 11};
EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]"); EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]");
EXPECT_EQ(fmt::format("{::#x}", v), "[0x1, 0x2, 0x3, 0x5, 0x7, 0xb]");
} }
TEST(ranges_test, format_vector2) { TEST(ranges_test, format_vector2) {
auto v = std::vector<std::vector<int>>{{1, 2}, {3, 5}, {7, 11}}; auto v = std::vector<std::vector<int>>{{1, 2}, {3, 5}, {7, 11}};
EXPECT_EQ(fmt::format("{}", v), "[[1, 2], [3, 5], [7, 11]]"); EXPECT_EQ(fmt::format("{}", v), "[[1, 2], [3, 5], [7, 11]]");
EXPECT_EQ(fmt::format("{:::#x}", v), "[[0x1, 0x2], [0x3, 0x5], [0x7, 0xb]]");
} }
TEST(ranges_test, format_map) { TEST(ranges_test, format_map) {
@ -63,16 +65,42 @@ TEST(ranges_test, format_set) {
"{\"one\", \"two\"}"); "{\"one\", \"two\"}");
} }
namespace adl {
struct box {
int value;
};
auto begin(const box& b) -> const int* { return &b.value; }
auto end(const box& b) -> const int* { return &b.value + 1; }
} // namespace adl
TEST(ranges_test, format_adl_begin_end) {
auto b = adl::box{42};
EXPECT_EQ(fmt::format("{}", b), "[42]");
}
TEST(ranges_test, format_pair) { TEST(ranges_test, format_pair) {
auto p = std::pair<int, float>(42, 1.5f); auto p = std::pair<int, float>(42, 1.5f);
EXPECT_EQ(fmt::format("{}", p), "(42, 1.5)"); EXPECT_EQ(fmt::format("{}", p), "(42, 1.5)");
} }
struct unformattable {};
TEST(ranges_test, format_tuple) { TEST(ranges_test, format_tuple) {
auto t = auto t =
std::tuple<int, float, std::string, char>(42, 1.5f, "this is tuple", 'i'); std::tuple<int, float, std::string, char>(42, 1.5f, "this is tuple", 'i');
EXPECT_EQ(fmt::format("{}", t), "(42, 1.5, \"this is tuple\", 'i')"); EXPECT_EQ(fmt::format("{}", t), "(42, 1.5, \"this is tuple\", 'i')");
EXPECT_EQ(fmt::format("{}", std::tuple<>()), "()"); EXPECT_EQ(fmt::format("{}", std::tuple<>()), "()");
EXPECT_TRUE((fmt::is_formattable<std::tuple<>>::value));
EXPECT_FALSE((fmt::is_formattable<unformattable>::value));
EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable>>::value));
EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable, int>>::value));
EXPECT_FALSE((fmt::is_formattable<std::tuple<int, unformattable>>::value));
EXPECT_FALSE(
(fmt::is_formattable<std::tuple<unformattable, unformattable>>::value));
EXPECT_TRUE((fmt::is_formattable<std::tuple<int, float>>::value));
} }
#ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT #ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
@ -131,8 +159,8 @@ TEST(ranges_test, path_like) {
struct string_like { struct string_like {
const char* begin(); const char* begin();
const char* end(); const char* end();
explicit operator fmt::string_view() const { return "foo"; } operator fmt::string_view() const { return "foo"; }
explicit operator std::string_view() const { return "foo"; } operator std::string_view() const { return "foo"; }
}; };
TEST(ranges_test, format_string_like) { TEST(ranges_test, format_string_like) {
@ -196,14 +224,14 @@ TEST(ranges_test, range) {
} }
enum test_enum { foo }; enum test_enum { foo };
auto format_as(test_enum e) -> int { return e; }
TEST(ranges_test, enum_range) { TEST(ranges_test, enum_range) {
auto v = std::vector<test_enum>{test_enum::foo}; auto v = std::vector<test_enum>{test_enum::foo};
EXPECT_EQ(fmt::format("{}", v), "[0]"); EXPECT_EQ(fmt::format("{}", v), "[0]");
} }
#if !FMT_MSC_VER #if !FMT_MSC_VERSION
struct unformattable {};
TEST(ranges_test, unformattable_range) { TEST(ranges_test, unformattable_range) {
EXPECT_FALSE((fmt::has_formatter<std::vector<unformattable>, EXPECT_FALSE((fmt::has_formatter<std::vector<unformattable>,
@ -296,6 +324,7 @@ static_assert(std::input_iterator<cpp20_only_range::iterator>);
TEST(ranges_test, join_sentinel) { TEST(ranges_test, join_sentinel) {
auto hello = zstring{"hello"}; 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("{::}", hello), "[h, e, l, l, o]");
EXPECT_EQ(fmt::format("{}", fmt::join(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{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]");
EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xc0"}), EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xc0"}),
"[\"\\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\"]"); "[\"foo\"]");
} }
#endif // FMT_USE_STRING_VIEW #endif // FMT_USE_STRING_VIEW
template <typename R> struct fmt_ref_view {
R* r;
auto begin() const -> decltype(r->begin()) { return r->begin(); }
auto end() const -> decltype(r->end()) { return r->end(); }
};
TEST(ranges_test, range_of_range_of_mixed_const) {
std::vector<std::vector<int>> v = {{1, 2, 3}, {4, 5}};
EXPECT_EQ(fmt::format("{}", v), "[[1, 2, 3], [4, 5]]");
fmt_ref_view<decltype(v)> r{&v};
EXPECT_EQ(fmt::format("{}", r), "[[1, 2, 3], [4, 5]]");
}

View file

@ -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 <string>
#include <vector>
#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<std::string>{"abc", "def"};
EXPECT_EQ(fmt::format("path={}, range={}", p, c),
"path=\"foo/bar.txt\", range=[\"abc\", \"def\"]");
#endif
}
TEST(std_test, thread_id) {
EXPECT_FALSE(fmt::format("{}", std::this_thread::get_id()).empty());
}
TEST(std_test, variant) {
#ifdef __cpp_lib_variant
EXPECT_EQ(fmt::format("{}", std::monostate{}), "monostate");
using V0 = std::variant<int, float, std::string, char>;
V0 v0(42);
V0 v1(1.5f);
V0 v2("hello");
V0 v3('i');
EXPECT_EQ(fmt::format("{}", v0), "variant(42)");
EXPECT_EQ(fmt::format("{}", v1), "variant(1.5)");
EXPECT_EQ(fmt::format("{}", v2), "variant(\"hello\")");
EXPECT_EQ(fmt::format("{}", v3), "variant('i')");
struct unformattable {};
EXPECT_FALSE((fmt::is_formattable<unformattable>::value));
EXPECT_FALSE((fmt::is_formattable<std::variant<unformattable>>::value));
EXPECT_FALSE((fmt::is_formattable<std::variant<unformattable, int>>::value));
EXPECT_FALSE((fmt::is_formattable<std::variant<int, unformattable>>::value));
EXPECT_FALSE(
(fmt::is_formattable<std::variant<unformattable, unformattable>>::value));
EXPECT_TRUE((fmt::is_formattable<std::variant<int, float>>::value));
using V1 = std::variant<std::monostate, std::string, std::string>;
V1 v4{};
V1 v5{std::in_place_index<1>, "yes, this is variant"};
EXPECT_EQ(fmt::format("{}", v4), "variant(monostate)");
EXPECT_EQ(fmt::format("{}", v5), "variant(\"yes, this is variant\")");
#endif
}

View file

@ -15,9 +15,6 @@
#ifdef _MSC_VER #ifdef _MSC_VER
# include <crtdbg.h> # include <crtdbg.h>
#else
# define _CrtSetReportFile(a, b)
# define _CrtSetReportMode(a, b)
#endif #endif
int main(int argc, char** argv) { int main(int argc, char** argv) {
@ -28,11 +25,13 @@ int main(int argc, char** argv) {
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
SEM_NOOPENFILEERRORBOX); SEM_NOOPENFILEERRORBOX);
#endif #endif
#ifdef _MSC_VER
// Disable message boxes on assertion failures. // Disable message boxes on assertion failures.
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
#endif
try { try {
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
testing::FLAGS_gtest_death_test_style = "threadsafe"; testing::FLAGS_gtest_death_test_style = "threadsafe";

View file

@ -66,14 +66,16 @@ TYPED_TEST(is_string_test, is_string) {
} }
// std::is_constructible is broken in MSVC until version 2015. // 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 { struct explicitly_convertible_to_wstring_view {
explicit operator fmt::wstring_view() const { return L"foo"; } explicit operator fmt::wstring_view() const { return L"foo"; }
}; };
TEST(xchar_test, format_explicitly_convertible_to_wstring_view) { TEST(xchar_test, format_explicitly_convertible_to_wstring_view) {
EXPECT_EQ(L"foo", // Types explicitly convertible to wstring_view are not formattable by
fmt::format(L"{}", explicitly_convertible_to_wstring_view())); // default because it may introduce ODR violations.
static_assert(
!fmt::is_formattable<explicitly_convertible_to_wstring_view>::value, "");
} }
#endif #endif
@ -96,12 +98,12 @@ TEST(xchar_test, is_formattable) {
} }
TEST(xchar_test, compile_time_string) { 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)); EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42));
#endif #endif
} }
#if __cplusplus > 201103L #if FMT_CPLUSPLUS > 201103L
struct custom_char { struct custom_char {
int value; int value;
custom_char() = default; custom_char() = default;
@ -183,11 +185,6 @@ TEST(format_test, wide_format_to_n) {
} }
#if FMT_USE_USER_DEFINED_LITERALS #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) { TEST(xchar_test, named_arg_udl) {
using namespace fmt::literals; using namespace fmt::literals;
auto udl_a = auto udl_a =
@ -218,7 +215,14 @@ std::wostream& operator<<(std::wostream& os, streamable_enum) {
return os << L"streamable_enum"; return os << L"streamable_enum";
} }
namespace fmt {
template <>
struct formatter<streamable_enum, wchar_t> : basic_ostream_formatter<wchar_t> {
};
} // namespace fmt
enum unstreamable_enum {}; enum unstreamable_enum {};
auto format_as(unstreamable_enum e) -> int { return e; }
TEST(xchar_test, enum) { TEST(xchar_test, enum) {
EXPECT_EQ(L"streamable_enum", fmt::format(L"{}", streamable_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); 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<int>(s_.size()); }
private:
std::wstring s_;
};
fmt::basic_string_view<wchar_t> to_string_view(const QString& s) FMT_NOEXCEPT {
return {s.utf16(), static_cast<size_t>(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) { TEST(xchar_test, chrono) {
auto tm = std::tm(); auto tm = std::tm();
tm.tm_year = 116; tm.tm_year = 116;
@ -322,9 +304,22 @@ TEST(xchar_test, color) {
} }
TEST(xchar_test, ostream) { TEST(xchar_test, ostream) {
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 409
std::wostringstream wos; std::wostringstream wos;
fmt::print(wos, L"Don't {}!", L"panic"); 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<std::wstring, int>{{L"one", 1}, {L"t\"wo", 2}};
EXPECT_EQ(fmt::format(L"{}", m), L"{\"one\": 1, \"t\\\"wo\": 2}");
}
TEST(xchar_test, escape_string) {
using vec = std::vector<std::wstring>;
EXPECT_EQ(fmt::format(L"{}", vec{L"\n\r\t\"\\"}), L"[\"\\n\\r\\t\\\"\\\\\"]");
EXPECT_EQ(fmt::format(L"{}", vec{L"понедельник"}), L"[\"понедельник\"]");
} }
TEST(xchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); } TEST(xchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); }
@ -360,10 +355,11 @@ template <typename Char> struct small_grouping : std::numpunct<Char> {
TEST(locale_test, localized_double) { TEST(locale_test, localized_double) {
auto loc = std::locale(std::locale(), new numpunct<char>()); auto loc = std::locale(std::locale(), new numpunct<char>());
EXPECT_EQ("1?23", fmt::format(loc, "{:L}", 1.23)); EXPECT_EQ(fmt::format(loc, "{:L}", 1.23), "1?23");
EXPECT_EQ("1?230000", fmt::format(loc, "{:Lf}", 1.23)); EXPECT_EQ(fmt::format(loc, "{:Lf}", 1.23), "1?230000");
EXPECT_EQ("1~234?5", fmt::format(loc, "{:L}", 1234.5)); EXPECT_EQ(fmt::format(loc, "{:L}", 1234.5), "1~234?5");
EXPECT_EQ("12~000", fmt::format(loc, "{:L}", 12000.0)); EXPECT_EQ(fmt::format(loc, "{:L}", 12000.0), "12~000");
EXPECT_EQ(fmt::format(loc, "{:8L}", 1230.0), " 1~230");
} }
TEST(locale_test, format) { TEST(locale_test, format) {

View file

@ -384,7 +384,7 @@ void A32EmitX64::EmitA32GetCpsr(A32EmitContext& ctx, IR::Inst* inst) {
code.pdep(result, result, tmp); code.pdep(result, result, tmp);
} else { } else {
code.mov(result, dword[r15 + offsetof(A32JitState, upper_location_descriptor)]); 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.and_(result, 0x00000220);
code.mov(tmp, dword[r15 + offsetof(A32JitState, cpsr_ge)]); 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) { static void EmitGetFlag(BlockOfCode& code, A32EmitContext& ctx, IR::Inst* inst, size_t flag_bit) {
const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32(); const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
code.mov(result, dword[r15 + offsetof(A32JitState, cpsr_nzcv)]); 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); 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<int>(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) { void A32EmitX64::EmitA32GetCFlag(A32EmitContext& ctx, IR::Inst* inst) {
EmitGetFlag(code, ctx, inst, NZCV::x64_c_flag_bit); 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) { void A32EmitX64::EmitA32OrQFlag(A32EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
if (args[0].IsImmediate()) { if (args[0].IsImmediate()) {

View file

@ -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}); 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); Optimization::PolyfillPass(ir_block, polyfill_options);
if (conf.HasOptimization(OptimizationFlag::GetSetElimination) && !conf.check_halt_on_memory_access) { 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); Optimization::DeadCodeElimination(ir_block);
} }
if (conf.HasOptimization(OptimizationFlag::ConstProp)) { if (conf.HasOptimization(OptimizationFlag::ConstProp)) {

View file

@ -134,6 +134,34 @@ void EmitX64::EmitGetLowerFromOp(EmitContext&, IR::Inst*) {
ASSERT_MSG(false, "should never happen"); 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) { void EmitX64::EmitGetNZCVFromOp(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(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); 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) { void EmitX64::EmitNZCVFromPackedFlags(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);

View file

@ -3829,29 +3829,29 @@ void EmitX64::EmitVectorSignedSaturatedDoublingMultiply16(EmitContext& ctx, IR::
ctx.EraseInstruction(lower_inst); 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) { 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.reg_alloc.DefineValue(upper_inst, upper_result);
ctx.EraseInstruction(upper_inst); ctx.EraseInstruction(upper_inst);
} }
@ -3880,23 +3880,23 @@ void EmitX64::EmitVectorSignedSaturatedDoublingMultiply32(EmitContext& ctx, IR::
code.vpaddq(odds, odds, odds); code.vpaddq(odds, odds, odds);
code.vpaddq(even, even, even); 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) { 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.reg_alloc.DefineValue(upper_inst, upper_result);
ctx.EraseInstruction(upper_inst); ctx.EraseInstruction(upper_inst);
} }
@ -3955,15 +3955,15 @@ void EmitX64::EmitVectorSignedSaturatedDoublingMultiply32(EmitContext& ctx, IR::
code.por(lower_result, x); code.por(lower_result, x);
code.psubd(upper_result, sign_correction); 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) { 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.reg_alloc.DefineValue(upper_inst, upper_result);
ctx.EraseInstruction(upper_inst); ctx.EraseInstruction(upper_inst);
} }

View file

@ -166,22 +166,6 @@ IR::U1 IREmitter::GetCFlag() {
return Inst<IR::U1>(Opcode::A32GetCFlag); return Inst<IR::U1>(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) { void IREmitter::OrQFlag(const IR::U1& value) {
Inst(Opcode::A32OrQFlag, value); Inst(Opcode::A32OrQFlag, value);
} }
@ -198,6 +182,18 @@ void IREmitter::SetGEFlagsCompressed(const IR::U32& value) {
Inst(Opcode::A32SetGEFlagsCompressed, value); Inst(Opcode::A32SetGEFlagsCompressed, value);
} }
IR::NZCV IREmitter::NZFrom(const IR::Value& value) {
return Inst<IR::NZCV>(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() { void IREmitter::DataSynchronizationBarrier() {
Inst(Opcode::A32DataSynchronizationBarrier); Inst(Opcode::A32DataSynchronizationBarrier);
} }

View file

@ -62,15 +62,15 @@ public:
void SetCheckBit(const IR::U1& value); void SetCheckBit(const IR::U1& value);
IR::U1 GetOverflowFrom(const IR::Value& value); IR::U1 GetOverflowFrom(const IR::Value& value);
IR::U1 GetCFlag(); 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); void OrQFlag(const IR::U1& value);
IR::U32 GetGEFlags(); IR::U32 GetGEFlags();
void SetGEFlags(const IR::U32& value); void SetGEFlags(const IR::U32& value);
void SetGEFlagsCompressed(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 DataSynchronizationBarrier();
void DataMemoryBarrier(); void DataMemoryBarrier();
void InstructionSynchronizationBarrier(); void InstructionSynchronizationBarrier();

View file

@ -5,20 +5,17 @@
#include "dynarmic/frontend/A32/a32_location_descriptor.h" #include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include <ostream>
#include <fmt/format.h> #include <fmt/format.h>
namespace Dynarmic::A32 { namespace Dynarmic::A32 {
std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor) { std::string ToString(const LocationDescriptor& descriptor) {
o << fmt::format("{{{:08x},{},{},{:08x}{}}}", return fmt::format("{{{:08x},{},{},{:08x}{}}}",
descriptor.PC(), descriptor.PC(),
descriptor.TFlag() ? "T" : "!T", descriptor.TFlag() ? "T" : "!T",
descriptor.EFlag() ? "E" : "!E", descriptor.EFlag() ? "E" : "!E",
descriptor.FPSCR().Value(), descriptor.FPSCR().Value(),
descriptor.SingleStepping() ? ",step" : ""); descriptor.SingleStepping() ? ",step" : "");
return o;
} }
} // namespace Dynarmic::A32 } // namespace Dynarmic::A32

View file

@ -6,9 +6,10 @@
#pragma once #pragma once
#include <functional> #include <functional>
#include <iosfwd> #include <string>
#include <tuple> #include <tuple>
#include <fmt/format.h>
#include <mcl/stdint.hpp> #include <mcl/stdint.hpp>
#include "dynarmic/frontend/A32/FPSCR.h" #include "dynarmic/frontend/A32/FPSCR.h"
@ -131,10 +132,9 @@ private:
/** /**
* Provides a string representation of a LocationDescriptor. * Provides a string representation of a LocationDescriptor.
* *
* @param o Output stream
* @param descriptor The descriptor to get a string representation of * @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 } // namespace Dynarmic::A32
@ -152,3 +152,11 @@ struct hash<Dynarmic::A32::LocationDescriptor> {
} }
}; };
} // namespace std } // namespace std
template<>
struct fmt::formatter<Dynarmic::A32::LocationDescriptor> : fmt::formatter<std::string> {
template<typename FormatContext>
auto format(Dynarmic::A32::LocationDescriptor descriptor, FormatContext& ctx) const {
return formatter<std::string>::format(Dynarmic::A32::ToString(descriptor), ctx);
}
};

View file

@ -57,24 +57,4 @@ std::string RegListToString(RegList reg_list) {
return ret; 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 } // namespace Dynarmic::A32

View file

@ -5,10 +5,10 @@
#pragma once #pragma once
#include <iosfwd>
#include <string> #include <string>
#include <utility> #include <utility>
#include <fmt/format.h>
#include <mcl/assert.hpp> #include <mcl/assert.hpp>
#include <mcl/stdint.hpp> #include <mcl/stdint.hpp>
@ -72,11 +72,6 @@ const char* ExtRegToString(ExtReg reg);
const char* CoprocRegToString(CoprocReg reg); const char* CoprocRegToString(CoprocReg reg);
std::string RegListToString(RegList reg_list); 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) { constexpr bool IsSingleExtReg(ExtReg reg) {
return reg >= ExtReg::S0 && reg <= ExtReg::S31; return reg >= ExtReg::S0 && reg <= ExtReg::S31;
} }
@ -148,3 +143,35 @@ inline ExtReg ToVector(bool Q, size_t base, bool bit) {
} }
} // namespace Dynarmic::A32 } // namespace Dynarmic::A32
template<>
struct fmt::formatter<Dynarmic::A32::Reg> : fmt::formatter<const char*> {
template<typename FormatContext>
auto format(Dynarmic::A32::Reg reg, FormatContext& ctx) const {
return formatter<const char*>::format(Dynarmic::A32::RegToString(reg), ctx);
}
};
template<>
struct fmt::formatter<Dynarmic::A32::ExtReg> : fmt::formatter<const char*> {
template<typename FormatContext>
auto format(Dynarmic::A32::ExtReg reg, FormatContext& ctx) const {
return formatter<const char*>::format(Dynarmic::A32::ExtRegToString(reg), ctx);
}
};
template<>
struct fmt::formatter<Dynarmic::A32::CoprocReg> : fmt::formatter<const char*> {
template<typename FormatContext>
auto format(Dynarmic::A32::CoprocReg reg, FormatContext& ctx) const {
return formatter<const char*>::format(Dynarmic::A32::CoprocRegToString(reg), ctx);
}
};
template<>
struct fmt::formatter<Dynarmic::A32::RegList> : fmt::formatter<std::string> {
template<typename FormatContext>
auto format(Dynarmic::A32::RegList reg_list, FormatContext& ctx) const {
return formatter<std::string>::format(Dynarmic::A32::RegListToString(reg_list), ctx);
}
};

View file

@ -181,9 +181,7 @@ bool TranslatorVisitor::arm_AND_imm(Cond cond, bool S, Reg n, Reg d, int rotate,
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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; const auto result = shifted.result;
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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 imm_carry = ArmExpandImm_C(rotate, imm8, ir.GetCFlag());
const auto result = ir.Eor(ir.GetRegister(n), ir.Imm32(imm_carry.imm32)); const auto result = ir.Eor(ir.GetRegister(n), ir.Imm32(imm_carry.imm32));
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
return true; 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 shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, carry_in);
const auto result = ir.Eor(ir.GetRegister(n), shifted.result); const auto result = ir.Eor(ir.GetRegister(n), shifted.result);
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
return true; 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 shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
const auto result = ir.Eor(ir.GetRegister(n), shifted.result); const auto result = ir.Eor(ir.GetRegister(n), shifted.result);
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
return true; 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 imm_carry = ArmExpandImm_C(rotate, imm8, ir.GetCFlag());
const auto result = ir.And(ir.GetRegister(n), ir.Imm32(imm_carry.imm32)); const auto result = ir.And(ir.GetRegister(n), ir.Imm32(imm_carry.imm32));
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
return true; 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 shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, carry_in);
const auto result = ir.And(ir.GetRegister(n), shifted.result); const auto result = ir.And(ir.GetRegister(n), shifted.result);
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
return true; 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 shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
const auto result = ir.And(ir.GetRegister(n), shifted.result); const auto result = ir.And(ir.GetRegister(n), shifted.result);
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
return true; return true;
} }

View file

@ -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)); const auto result = ir.Add(ir.Mul(ir.GetRegister(n), ir.GetRegister(m)), ir.GetRegister(a));
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZ(ir.NZFrom(result));
ir.SetZFlag(ir.IsZero(result));
} }
return true; 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)); const auto result = ir.Mul(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZ(ir.NZFrom(result));
ir.SetZFlag(ir.IsZero(result));
} }
return true; 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(dLo, lo);
ir.SetRegister(dHi, hi); ir.SetRegister(dHi, hi);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi)); ir.SetCpsrNZ(ir.NZFrom(result));
ir.SetZFlag(ir.IsZero(result));
} }
return true; 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(dLo, lo);
ir.SetRegister(dHi, hi); ir.SetRegister(dHi, hi);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi)); ir.SetCpsrNZ(ir.NZFrom(result));
ir.SetZFlag(ir.IsZero(result));
} }
return true; 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(dLo, lo);
ir.SetRegister(dHi, hi); ir.SetRegister(dHi, hi);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi)); ir.SetCpsrNZ(ir.NZFrom(result));
ir.SetZFlag(ir.IsZero(result));
} }
return true; 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(dLo, lo);
ir.SetRegister(dHi, hi); ir.SetRegister(dHi, hi);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi)); ir.SetCpsrNZ(ir.NZFrom(result));
ir.SetZFlag(ir.IsZero(result));
} }
return true; return true;

View file

@ -22,9 +22,7 @@ bool TranslatorVisitor::thumb16_LSL_imm(Imm<5> imm5, Reg m, Reg d) {
ir.SetRegister(d, result.result); ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) { if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetCpsrNZC(ir.NZFrom(result.result), result.carry);
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
} }
return true; return true;
} }
@ -37,9 +35,7 @@ bool TranslatorVisitor::thumb16_LSR_imm(Imm<5> imm5, Reg m, Reg d) {
ir.SetRegister(d, result.result); ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) { if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetCpsrNZC(ir.NZFrom(result.result), result.carry);
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
} }
return true; return true;
} }
@ -52,9 +48,7 @@ bool TranslatorVisitor::thumb16_ASR_imm(Imm<5> imm5, Reg m, Reg d) {
ir.SetRegister(d, result.result); ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) { if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetCpsrNZC(ir.NZFrom(result.result), result.carry);
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
} }
return true; return true;
} }
@ -117,8 +111,7 @@ bool TranslatorVisitor::thumb16_MOV_imm(Reg d, Imm<8> imm8) {
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (!ir.current_location.IT().IsInITBlock()) { if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZ(ir.NZFrom(result));
ir.SetZFlag(ir.IsZero(result));
} }
return true; return true;
} }
@ -171,8 +164,7 @@ bool TranslatorVisitor::thumb16_AND_reg(Reg m, Reg d_n) {
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (!ir.current_location.IT().IsInITBlock()) { if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZ(ir.NZFrom(result));
ir.SetZFlag(ir.IsZero(result));
} }
return true; return true;
} }
@ -186,8 +178,7 @@ bool TranslatorVisitor::thumb16_EOR_reg(Reg m, Reg d_n) {
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (!ir.current_location.IT().IsInITBlock()) { if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZ(ir.NZFrom(result));
ir.SetZFlag(ir.IsZero(result));
} }
return true; return true;
} }
@ -202,9 +193,7 @@ bool TranslatorVisitor::thumb16_LSL_reg(Reg m, Reg d_n) {
ir.SetRegister(d, result_carry.result); ir.SetRegister(d, result_carry.result);
if (!ir.current_location.IT().IsInITBlock()) { if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result_carry.result)); ir.SetCpsrNZC(ir.NZFrom(result_carry.result), result_carry.carry);
ir.SetZFlag(ir.IsZero(result_carry.result));
ir.SetCFlag(result_carry.carry);
} }
return true; return true;
} }
@ -219,9 +208,7 @@ bool TranslatorVisitor::thumb16_LSR_reg(Reg m, Reg d_n) {
ir.SetRegister(d, result.result); ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) { if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetCpsrNZC(ir.NZFrom(result.result), result.carry);
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
} }
return true; return true;
} }
@ -236,9 +223,7 @@ bool TranslatorVisitor::thumb16_ASR_reg(Reg m, Reg d_n) {
ir.SetRegister(d, result.result); ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) { if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetCpsrNZC(ir.NZFrom(result.result), result.carry);
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
} }
return true; return true;
} }
@ -283,9 +268,7 @@ bool TranslatorVisitor::thumb16_ROR_reg(Reg m, Reg d_n) {
ir.SetRegister(d, result.result); ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) { if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetCpsrNZC(ir.NZFrom(result.result), result.carry);
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
} }
return true; return true;
} }
@ -293,8 +276,7 @@ bool TranslatorVisitor::thumb16_ROR_reg(Reg m, Reg d_n) {
// TST <Rn>, <Rm> // TST <Rn>, <Rm>
bool TranslatorVisitor::thumb16_TST_reg(Reg m, Reg n) { bool TranslatorVisitor::thumb16_TST_reg(Reg m, Reg n) {
const auto result = ir.And(ir.GetRegister(n), ir.GetRegister(m)); const auto result = ir.And(ir.GetRegister(n), ir.GetRegister(m));
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZ(ir.NZFrom(result));
ir.SetZFlag(ir.IsZero(result));
return true; return true;
} }
@ -332,8 +314,7 @@ bool TranslatorVisitor::thumb16_ORR_reg(Reg m, Reg d_n) {
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (!ir.current_location.IT().IsInITBlock()) { if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZ(ir.NZFrom(result));
ir.SetZFlag(ir.IsZero(result));
} }
return true; return true;
} }
@ -347,8 +328,7 @@ bool TranslatorVisitor::thumb16_MUL_reg(Reg n, Reg d_m) {
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (!ir.current_location.IT().IsInITBlock()) { if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZ(ir.NZFrom(result));
ir.SetZFlag(ir.IsZero(result));
} }
return true; return true;
} }
@ -362,8 +342,7 @@ bool TranslatorVisitor::thumb16_BIC_reg(Reg m, Reg d_n) {
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (!ir.current_location.IT().IsInITBlock()) { if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZ(ir.NZFrom(result));
ir.SetZFlag(ir.IsZero(result));
} }
return true; return true;
} }
@ -375,8 +354,7 @@ bool TranslatorVisitor::thumb16_MVN_reg(Reg m, Reg d) {
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (!ir.current_location.IT().IsInITBlock()) { if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZ(ir.NZFrom(result));
ir.SetZFlag(ir.IsZero(result));
} }
return true; return true;
} }

View file

@ -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 imm_carry = ThumbExpandImm_C(i, imm3, imm8, ir.GetCFlag());
const auto result = ir.And(ir.GetRegister(n), ir.Imm32(imm_carry.imm32)); const auto result = ir.And(ir.GetRegister(n), ir.Imm32(imm_carry.imm32));
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
} }
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
} }
return true; 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 imm_carry = ThumbExpandImm_C(i, imm3, imm8, ir.GetCFlag());
const auto result = ir.Eor(ir.GetRegister(n), ir.Imm32(imm_carry.imm32)); const auto result = ir.Eor(ir.GetRegister(n), ir.Imm32(imm_carry.imm32));
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
return true; 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); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), imm_carry.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
} }
return true; return true;
} }

View file

@ -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); const auto result_carry = (v.ir.*shift_fn)(v.ir.GetRegister(m), shift_s, apsr_c);
if (S) { if (S) {
v.ir.SetNFlag(v.ir.MostSignificantBit(result_carry.result)); v.ir.SetCpsrNZC(v.ir.NZFrom(result_carry.result), result_carry.carry);
v.ir.SetZFlag(v.ir.IsZero(result_carry.result));
v.ir.SetCFlag(result_carry.carry);
} }
v.ir.SetRegister(d, result_carry.result); v.ir.SetRegister(d, result_carry.result);

View file

@ -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 shifted = EmitImmShift(ir.GetRegister(m), type, imm3, imm2, ir.GetCFlag());
const auto result = ir.And(ir.GetRegister(n), shifted.result); const auto result = ir.And(ir.GetRegister(n), shifted.result);
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
return true; 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); const auto result = ir.And(ir.GetRegister(n), shifted.result);
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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); const auto result = ir.AndNot(ir.GetRegister(n), shifted.result);
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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; const auto result = shifted.result;
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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); const auto result = ir.Or(ir.GetRegister(n), shifted.result);
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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); const auto result = ir.Not(shifted.result);
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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)); const auto result = ir.Or(ir.GetRegister(n), ir.Not(shifted.result));
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; 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 shifted = EmitImmShift(ir.GetRegister(m), type, imm3, imm2, ir.GetCFlag());
const auto result = ir.Eor(ir.GetRegister(n), shifted.result); const auto result = ir.Eor(ir.GetRegister(n), shifted.result);
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
return true; 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); const auto result = ir.Eor(ir.GetRegister(n), shifted.result);
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetCpsrNZC(ir.NZFrom(result), shifted.carry);
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
} }
return true; return true;
} }

View file

@ -31,7 +31,7 @@ enum class ThumbInstSize {
}; };
bool IsThumb16(u16 first_part) { bool IsThumb16(u16 first_part) {
return (first_part & 0xF800) < 0xE800; return first_part < 0xE800;
} }
bool IsUnconditionalInstruction(bool is_thumb_16, u32 instruction) { bool IsUnconditionalInstruction(bool is_thumb_16, u32 instruction) {

View file

@ -5,15 +5,12 @@
#include "dynarmic/frontend/A64/a64_location_descriptor.h" #include "dynarmic/frontend/A64/a64_location_descriptor.h"
#include <ostream>
#include <fmt/format.h> #include <fmt/format.h>
namespace Dynarmic::A64 { namespace Dynarmic::A64 {
std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor) { std::string ToString(const LocationDescriptor& descriptor) {
o << fmt::format("{{{}, {}{}}}", descriptor.PC(), descriptor.FPCR().Value(), descriptor.SingleStepping() ? ", step" : ""); return fmt::format("{{{}, {}{}}}", descriptor.PC(), descriptor.FPCR().Value(), descriptor.SingleStepping() ? ", step" : "");
return o;
} }
} // namespace Dynarmic::A64 } // namespace Dynarmic::A64

View file

@ -6,9 +6,10 @@
#pragma once #pragma once
#include <functional> #include <functional>
#include <iosfwd> #include <string>
#include <tuple> #include <tuple>
#include <fmt/format.h>
#include <mcl/bit/bit_field.hpp> #include <mcl/bit/bit_field.hpp>
#include <mcl/stdint.hpp> #include <mcl/stdint.hpp>
@ -84,10 +85,9 @@ private:
/** /**
* Provides a string representation of a LocationDescriptor. * Provides a string representation of a LocationDescriptor.
* *
* @param o Output stream
* @param descriptor The descriptor to get a string representation of * @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 } // namespace Dynarmic::A64
@ -105,3 +105,11 @@ struct hash<Dynarmic::A64::LocationDescriptor> {
} }
}; };
} // namespace std } // namespace std
template<>
struct fmt::formatter<Dynarmic::A64::LocationDescriptor> : fmt::formatter<std::string> {
template<typename FormatContext>
auto format(Dynarmic::A64::LocationDescriptor descriptor, FormatContext& ctx) const {
return formatter<std::string>::format(Dynarmic::A64::ToString(descriptor), ctx);
}
};

View file

@ -30,14 +30,4 @@ std::string VecToString(Vec vec) {
return fmt::format("v{}", static_cast<size_t>(vec)); return fmt::format("v{}", static_cast<size_t>(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 } // namespace Dynarmic::A64

View file

@ -5,9 +5,9 @@
#pragma once #pragma once
#include <iosfwd>
#include <string> #include <string>
#include <fmt/format.h>
#include <mcl/assert.hpp> #include <mcl/assert.hpp>
#include <mcl/stdint.hpp> #include <mcl/stdint.hpp>
@ -101,9 +101,6 @@ const char* CondToString(Cond cond);
std::string RegToString(Reg reg); std::string RegToString(Reg reg);
std::string VecToString(Vec vec); 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) { constexpr size_t RegNumber(Reg reg) {
return static_cast<size_t>(reg); return static_cast<size_t>(reg);
} }
@ -127,3 +124,19 @@ inline Vec operator+(Vec vec, size_t number) {
} }
} // namespace Dynarmic::A64 } // namespace Dynarmic::A64
template<>
struct fmt::formatter<Dynarmic::A64::Reg> : fmt::formatter<std::string> {
template<typename FormatContext>
auto format(Dynarmic::A64::Reg reg, FormatContext& ctx) const {
return formatter<std::string>::format(Dynarmic::A64::RegToString(reg), ctx);
}
};
template<>
struct fmt::formatter<Dynarmic::A64::Vec> : fmt::formatter<std::string> {
template<typename FormatContext>
auto format(Dynarmic::A64::Vec vec, FormatContext& ctx) const {
return formatter<std::string>::format(Dynarmic::A64::VecToString(vec), ctx);
}
};

View file

@ -135,6 +135,10 @@ U32U64 IREmitter::ConditionalSelect(Cond cond, const U32U64& a, const U32U64& b)
} }
} }
U1 IREmitter::GetCFlagFromNZCV(const NZCV& nzcv) {
return Inst<U1>(Opcode::GetCFlagFromNZCV, nzcv);
}
NZCV IREmitter::NZCVFromPackedFlags(const U32& a) { NZCV IREmitter::NZCVFromPackedFlags(const U32& a) {
return Inst<NZCV>(Opcode::NZCVFromPackedFlags, a); return Inst<NZCV>(Opcode::NZCVFromPackedFlags, a);
} }

View file

@ -101,6 +101,7 @@ public:
NZCV ConditionalSelect(Cond cond, const NZCV& a, const NZCV& b); NZCV ConditionalSelect(Cond cond, const NZCV& a, const NZCV& b);
U32U64 ConditionalSelect(Cond cond, const U32U64& a, const U32U64& b); U32U64 ConditionalSelect(Cond cond, const U32U64& a, const U32U64& b);
U1 GetCFlagFromNZCV(const NZCV& nzcv);
NZCV NZCVFromPackedFlags(const U32& a); NZCV NZCVFromPackedFlags(const U32& a);
// This pseudo-instruction may only be added to instructions that support it. // This pseudo-instruction may only be added to instructions that support it.
NZCV NZCVFrom(const Value& value); NZCV NZCVFrom(const Value& value);
@ -393,14 +394,24 @@ public:
void SetTerm(const Terminal& terminal); 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}; 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; 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: protected:
IR::Block::iterator insertion_point; IR::Block::iterator insertion_point;

View file

@ -5,15 +5,12 @@
#include "dynarmic/ir/location_descriptor.h" #include "dynarmic/ir/location_descriptor.h"
#include <ostream>
#include <fmt/format.h> #include <fmt/format.h>
namespace Dynarmic::IR { namespace Dynarmic::IR {
std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor) { std::string ToString(const LocationDescriptor& descriptor) {
o << fmt::format("{{{:016x}}}", descriptor.Value()); return fmt::format("{{{:016x}}}", descriptor.Value());
return o;
} }
} // namespace Dynarmic::IR } // namespace Dynarmic::IR

View file

@ -6,8 +6,9 @@
#pragma once #pragma once
#include <functional> #include <functional>
#include <iosfwd> #include <string>
#include <fmt/format.h>
#include <mcl/stdint.hpp> #include <mcl/stdint.hpp>
namespace Dynarmic::IR { namespace Dynarmic::IR {
@ -31,7 +32,7 @@ private:
u64 value; 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 { inline bool operator<(const LocationDescriptor& x, const LocationDescriptor& y) noexcept {
return x.Value() < y.Value(); return x.Value() < y.Value();
@ -53,3 +54,11 @@ struct hash<Dynarmic::IR::LocationDescriptor> {
} }
}; };
} // namespace std } // namespace std
template<>
struct fmt::formatter<Dynarmic::IR::LocationDescriptor> : fmt::formatter<std::string> {
template<typename FormatContext>
auto format(Dynarmic::IR::LocationDescriptor descriptor, FormatContext& ctx) const {
return formatter<std::string>::format(ToString(descriptor), ctx);
}
};

View file

@ -7,7 +7,6 @@
#include <algorithm> #include <algorithm>
#include <fmt/ostream.h>
#include <mcl/assert.hpp> #include <mcl/assert.hpp>
#include "dynarmic/ir/opcodes.h" #include "dynarmic/ir/opcodes.h"
@ -176,10 +175,8 @@ bool Inst::WritesToCPSR() const {
case Opcode::A32SetCpsrNZCVRaw: case Opcode::A32SetCpsrNZCVRaw:
case Opcode::A32SetCpsrNZCV: case Opcode::A32SetCpsrNZCV:
case Opcode::A32SetCpsrNZCVQ: case Opcode::A32SetCpsrNZCVQ:
case Opcode::A32SetNFlag: case Opcode::A32SetCpsrNZ:
case Opcode::A32SetZFlag: case Opcode::A32SetCpsrNZC:
case Opcode::A32SetCFlag:
case Opcode::A32SetVFlag:
case Opcode::A32OrQFlag: case Opcode::A32OrQFlag:
case Opcode::A32SetGEFlags: case Opcode::A32SetGEFlags:
case Opcode::A32SetGEFlagsCompressed: case Opcode::A32SetGEFlagsCompressed:
@ -546,8 +543,12 @@ bool Inst::IsAPseudoOperation() const {
case Opcode::GetOverflowFromOp: case Opcode::GetOverflowFromOp:
case Opcode::GetGEFromOp: case Opcode::GetGEFromOp:
case Opcode::GetNZCVFromOp: case Opcode::GetNZCVFromOp:
case Opcode::GetNZFromOp:
case Opcode::GetUpperFromOp: case Opcode::GetUpperFromOp:
case Opcode::GetLowerFromOp: case Opcode::GetLowerFromOp:
case Opcode::MostSignificantBit:
case Opcode::IsZero32:
case Opcode::IsZero64:
return true; return true;
default: default:
@ -583,40 +584,19 @@ bool Inst::AreAllArgsImmediates() const {
} }
bool Inst::HasAssociatedPseudoOperation() const { bool Inst::HasAssociatedPseudoOperation() const {
return carry_inst return next_pseudoop && !IsAPseudoOperation();
|| overflow_inst
|| ge_inst
|| nzcv_inst
|| upper_inst
|| lower_inst;
} }
Inst* Inst::GetAssociatedPseudoOperation(Opcode opcode) { Inst* Inst::GetAssociatedPseudoOperation(Opcode opcode) {
// This is faster than doing a search through the block. Inst* pseudoop = next_pseudoop;
switch (opcode) { while (pseudoop) {
case Opcode::GetCarryFromOp: if (pseudoop->GetOpcode() == opcode) {
ASSERT(!carry_inst || carry_inst->GetOpcode() == Opcode::GetCarryFromOp); ASSERT(pseudoop->GetArg(0).GetInst() == this);
return carry_inst; return pseudoop;
case Opcode::GetOverflowFromOp: }
ASSERT(!overflow_inst || overflow_inst->GetOpcode() == Opcode::GetOverflowFromOp); pseudoop = pseudoop->next_pseudoop;
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;
} }
return nullptr;
ASSERT_FALSE("Not a valid pseudo-operation");
} }
Type Inst::GetType() const { Type Inst::GetType() const {
@ -679,67 +659,31 @@ void Inst::ReplaceUsesWith(Value replacement) {
void Inst::Use(const Value& value) { void Inst::Use(const Value& value) {
value.GetInst()->use_count++; value.GetInst()->use_count++;
switch (op) { if (IsAPseudoOperation()) {
case Opcode::GetCarryFromOp: if (op == Opcode::GetNZCVFromOp) {
ASSERT_MSG(!value.GetInst()->carry_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()->carry_inst = this; }
break;
case Opcode::GetOverflowFromOp: Inst* insert_point = value.GetInst();
ASSERT_MSG(!value.GetInst()->overflow_inst, "Only one of each type of pseudo-op allowed"); while (insert_point->next_pseudoop) {
value.GetInst()->overflow_inst = this; insert_point = insert_point->next_pseudoop;
break; DEBUG_ASSERT(insert_point->GetArg(0).GetInst() == value.GetInst());
case Opcode::GetGEFromOp: }
ASSERT_MSG(!value.GetInst()->ge_inst, "Only one of each type of pseudo-op allowed"); insert_point->next_pseudoop = this;
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;
} }
} }
void Inst::UndoUse(const Value& value) { void Inst::UndoUse(const Value& value) {
value.GetInst()->use_count--; value.GetInst()->use_count--;
switch (op) { if (IsAPseudoOperation()) {
case Opcode::GetCarryFromOp: Inst* insert_point = value.GetInst();
ASSERT(value.GetInst()->carry_inst->GetOpcode() == Opcode::GetCarryFromOp); while (insert_point->next_pseudoop != this) {
value.GetInst()->carry_inst = nullptr; insert_point = insert_point->next_pseudoop;
break; DEBUG_ASSERT(insert_point->GetArg(0).GetInst() == value.GetInst());
case Opcode::GetOverflowFromOp: }
ASSERT(value.GetInst()->overflow_inst->GetOpcode() == Opcode::GetOverflowFromOp); insert_point->next_pseudoop = next_pseudoop;
value.GetInst()->overflow_inst = nullptr; next_pseudoop = 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;
} }
} }

View file

@ -149,18 +149,8 @@ private:
size_t use_count = 0; size_t use_count = 0;
std::array<Value, max_arg_count> args; std::array<Value, max_arg_count> args;
// Pointers to related pseudooperations: // Linked list of pseudooperations associated with this instruction.
// Since not all combinations are possible, we use a union to save space Inst* next_pseudoop = nullptr;
union {
Inst* carry_inst = nullptr;
Inst* ge_inst;
Inst* upper_inst;
};
Inst* overflow_inst = nullptr;
union {
Inst* nzcv_inst = nullptr;
Inst* lower_inst;
};
}; };
} // namespace Dynarmic::IR } // namespace Dynarmic::IR

View file

@ -73,8 +73,4 @@ std::string GetNameOf(Opcode op) {
return OpcodeInfo::opcode_info.at(static_cast<size_t>(op)).name; return OpcodeInfo::opcode_info.at(static_cast<size_t>(op)).name;
} }
std::ostream& operator<<(std::ostream& o, Opcode opcode) {
return o << GetNameOf(opcode);
}
} // namespace Dynarmic::IR } // namespace Dynarmic::IR

View file

@ -5,9 +5,9 @@
#pragma once #pragma once
#include <iosfwd>
#include <string> #include <string>
#include <fmt/format.h>
#include <mcl/stdint.hpp> #include <mcl/stdint.hpp>
namespace Dynarmic::IR { namespace Dynarmic::IR {
@ -43,6 +43,12 @@ Type GetArgTypeOf(Opcode op, size_t arg_index);
/// Get the name of an opcode. /// Get the name of an opcode.
std::string GetNameOf(Opcode op); std::string GetNameOf(Opcode op);
std::ostream& operator<<(std::ostream& o, Opcode opcode);
} // namespace Dynarmic::IR } // namespace Dynarmic::IR
template<>
struct fmt::formatter<Dynarmic::IR::Opcode> : fmt::formatter<std::string> {
template<typename FormatContext>
auto format(Dynarmic::IR::Opcode op, FormatContext& ctx) const {
return formatter<std::string>::format(Dynarmic::IR::GetNameOf(op), ctx);
}
};

View file

@ -22,11 +22,9 @@ A32OPC(SetCpsr, Void, U32
A32OPC(SetCpsrNZCV, Void, NZCV ) A32OPC(SetCpsrNZCV, Void, NZCV )
A32OPC(SetCpsrNZCVRaw, Void, U32 ) A32OPC(SetCpsrNZCVRaw, Void, U32 )
A32OPC(SetCpsrNZCVQ, Void, U32 ) A32OPC(SetCpsrNZCVQ, Void, U32 )
A32OPC(SetNFlag, Void, U1 ) A32OPC(SetCpsrNZ, Void, NZCV )
A32OPC(SetZFlag, Void, U1 ) A32OPC(SetCpsrNZC, Void, NZCV, U1 )
A32OPC(GetCFlag, U1, ) A32OPC(GetCFlag, U1, )
A32OPC(SetCFlag, Void, U1 )
A32OPC(SetVFlag, Void, U1 )
A32OPC(OrQFlag, Void, U1 ) A32OPC(OrQFlag, Void, U1 )
A32OPC(GetGEFlags, U32, ) A32OPC(GetGEFlags, U32, )
A32OPC(SetGEFlags, Void, U32 ) A32OPC(SetGEFlags, Void, U32 )
@ -90,9 +88,11 @@ OPCODE(GetCarryFromOp, U1, Opaq
OPCODE(GetOverflowFromOp, U1, Opaque ) OPCODE(GetOverflowFromOp, U1, Opaque )
OPCODE(GetGEFromOp, U32, Opaque ) OPCODE(GetGEFromOp, U32, Opaque )
OPCODE(GetNZCVFromOp, NZCV, Opaque ) OPCODE(GetNZCVFromOp, NZCV, Opaque )
OPCODE(GetNZFromOp, NZCV, Opaque )
OPCODE(GetUpperFromOp, U128, Opaque ) OPCODE(GetUpperFromOp, U128, Opaque )
OPCODE(GetLowerFromOp, U128, Opaque ) OPCODE(GetLowerFromOp, U128, Opaque )
OPCODE(GetCFlagFromNZCV, U1, NZCV )
OPCODE(NZCVFromPackedFlags, NZCV, U32 ) OPCODE(NZCVFromPackedFlags, NZCV, U32 )
// Calculations // Calculations

View file

@ -13,13 +13,6 @@ namespace Dynarmic::Optimization {
void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) { void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
for (auto& inst : block) { for (auto& inst : block) {
switch (inst.GetOpcode()) { 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: { case IR::Opcode::A32ReadMemory8: {
if (!inst.AreAllArgsImmediates()) { if (!inst.AreAllArgsImmediates()) {
break; break;

View file

@ -8,6 +8,7 @@
#include <mcl/assert.hpp> #include <mcl/assert.hpp>
#include <mcl/stdint.hpp> #include <mcl/stdint.hpp>
#include "dynarmic/frontend/A32/a32_ir_emitter.h"
#include "dynarmic/frontend/A32/a32_types.h" #include "dynarmic/frontend/A32/a32_types.h"
#include "dynarmic/ir/basic_block.h" #include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/opcodes.h" #include "dynarmic/ir/opcodes.h"
@ -16,7 +17,7 @@
namespace Dynarmic::Optimization { namespace Dynarmic::Optimization {
void A32GetSetElimination(IR::Block& block) { void A32GetSetElimination(IR::Block& block, A32GetSetEliminationOptions opt) {
using Iterator = IR::Block::iterator; using Iterator = IR::Block::iterator;
struct RegisterInfo { struct RegisterInfo {
IR::Value register_value; IR::Value register_value;
@ -29,24 +30,34 @@ void A32GetSetElimination(IR::Block& block) {
std::array<RegisterInfo, 32> ext_reg_vector_double_info; std::array<RegisterInfo, 32> ext_reg_vector_double_info;
std::array<RegisterInfo, 16> ext_reg_vector_quad_info; std::array<RegisterInfo, 16> ext_reg_vector_quad_info;
struct CpsrInfo { struct CpsrInfo {
RegisterInfo n; RegisterInfo nz;
RegisterInfo z;
RegisterInfo c; RegisterInfo c;
RegisterInfo v; RegisterInfo nzc;
RegisterInfo nzcv;
RegisterInfo ge; RegisterInfo ge;
} cpsr_info; } 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) { if (info.set_instruction_present) {
info.set_instruction_present = false;
info.last_set_instruction->Invalidate(); info.last_set_instruction->Invalidate();
block.Instructions().erase(info.last_set_instruction); 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.register_value = value;
info.set_instruction_present = true; info.set_instruction_present = true;
info.last_set_instruction = set_inst; 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) { const auto do_get = [](RegisterInfo& info, Iterator get_inst) {
if (info.register_value.IsEmpty()) { if (info.register_value.IsEmpty()) {
info.register_value = IR::Value(&*get_inst); info.register_value = IR::Value(&*get_inst);
@ -55,6 +66,9 @@ void A32GetSetElimination(IR::Block& block) {
get_inst->ReplaceUsesWith(info.register_value); 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) { for (auto inst = block.begin(); inst != block.end(); ++inst) {
switch (inst->GetOpcode()) { switch (inst->GetOpcode()) {
case IR::Opcode::A32SetRegister: { case IR::Opcode::A32SetRegister: {
@ -167,24 +181,66 @@ void A32GetSetElimination(IR::Block& block) {
} }
break; 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: { 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); do_get(cpsr_info.c, inst);
// ensure source is not deleted
cpsr_info.nzc = {};
cpsr_info.nzcv = {};
break; break;
} }
case IR::Opcode::A32SetVFlag: { case IR::Opcode::A32SetCpsrNZCV:
do_set(cpsr_info.v, inst->GetArg(0), inst); 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; break;
} }
case IR::Opcode::A32SetGEFlags: { case IR::Opcode::A32SetGEFlags: {

View file

@ -26,7 +26,7 @@ void A64CallbackConfigPass(IR::Block& block, const A64::UserConfig& conf) {
if (op == A64::DataCacheOperation::ZeroByVA) { if (op == A64::DataCacheOperation::ZeroByVA) {
A64::IREmitter ir{block}; A64::IREmitter ir{block};
ir.current_location = A64::LocationDescriptor{IR::LocationDescriptor{inst.GetArg(0).GetU64()}}; ir.current_location = A64::LocationDescriptor{IR::LocationDescriptor{inst.GetArg(0).GetU64()}};
ir.SetInsertionPoint(&inst); ir.SetInsertionPointBefore(&inst);
size_t bytes = 4 << static_cast<size_t>(conf.dczid_el0 & 0b1111); size_t bytes = 4 << static_cast<size_t>(conf.dczid_el0 & 0b1111);
IR::U64 addr{inst.GetArg(2)}; IR::U64 addr{inst.GetArg(2)};

View file

@ -26,9 +26,14 @@ struct PolyfillOptions {
bool operator==(const PolyfillOptions&) const = default; 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 PolyfillPass(IR::Block& block, const PolyfillOptions& opt);
void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb); 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 A64CallbackConfigPass(IR::Block& block, const A64::UserConfig& conf);
void A64GetSetElimination(IR::Block& block); void A64GetSetElimination(IR::Block& block);
void A64MergeInterpretBlocksPass(IR::Block& block, A64::UserCallbacks* cb); void A64MergeInterpretBlocksPass(IR::Block& block, A64::UserCallbacks* cb);

View file

@ -148,7 +148,7 @@ void PolyfillPass(IR::Block& block, const PolyfillOptions& polyfill) {
IR::IREmitter ir{block}; IR::IREmitter ir{block};
for (auto& inst : block) { for (auto& inst : block) {
ir.SetInsertionPoint(&inst); ir.SetInsertionPointBefore(&inst);
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
case IR::Opcode::SHA256MessageSchedule0: case IR::Opcode::SHA256MessageSchedule0:

View file

@ -43,8 +43,4 @@ bool AreTypesCompatible(Type t1, Type t2) {
return t1 == t2 || t1 == Type::Opaque || t2 == Type::Opaque; return t1 == t2 || t1 == Type::Opaque || t2 == Type::Opaque;
} }
std::ostream& operator<<(std::ostream& o, Type type) {
return o << GetNameOf(type);
}
} // namespace Dynarmic::IR } // namespace Dynarmic::IR

View file

@ -5,9 +5,9 @@
#pragma once #pragma once
#include <iosfwd>
#include <string> #include <string>
#include <fmt/format.h>
#include <mcl/stdint.hpp> #include <mcl/stdint.hpp>
namespace Dynarmic::IR { namespace Dynarmic::IR {
@ -49,6 +49,12 @@ std::string GetNameOf(Type type);
/// @returns true if t1 and t2 are compatible types /// @returns true if t1 and t2 are compatible types
bool AreTypesCompatible(Type t1, Type t2); bool AreTypesCompatible(Type t1, Type t2);
std::ostream& operator<<(std::ostream& o, Type type);
} // namespace Dynarmic::IR } // namespace Dynarmic::IR
template<>
struct fmt::formatter<Dynarmic::IR::Type> : fmt::formatter<std::string> {
template<typename FormatContext>
auto format(Dynarmic::IR::Type type, FormatContext& ctx) const {
return formatter<std::string>::format(Dynarmic::IR::GetNameOf(type), ctx);
}
};

View file

@ -79,6 +79,12 @@ Value::Value(AccType value)
inner.imm_acctype = value; inner.imm_acctype = value;
} }
Value Value::EmptyNZCVImmediateMarker() {
Value result{};
result.type = Type::NZCVFlags;
return result;
}
bool Value::IsIdentity() const { bool Value::IsIdentity() const {
if (type == Type::Opaque) if (type == Type::Opaque)
return inner.inst->GetOpcode() == Opcode::Identity; return inner.inst->GetOpcode() == Opcode::Identity;

View file

@ -53,6 +53,8 @@ public:
explicit Value(Cond value); explicit Value(Cond value);
explicit Value(AccType value); explicit Value(AccType value);
static Value EmptyNZCVImmediateMarker();
bool IsIdentity() const; bool IsIdentity() const;
bool IsEmpty() const; bool IsEmpty() const;
bool IsImmediate() const; bool IsImmediate() const;

Some files were not shown because too many files have changed in this diff Show more