From f1625751c4f406b7b7fd303764372cb4f76c6a76 Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Wed, 5 Oct 2022 19:04:42 -0700 Subject: [PATCH] Externals: Update fmt to 9.1.0 --- Externals/fmt/CMakeLists.txt | 8 +- Externals/fmt/ChangeLog.rst | 519 ++++++- Externals/fmt/README.rst | 10 +- Externals/fmt/fmt.vcxproj | 2 +- Externals/fmt/include/fmt/chrono.h | 54 +- Externals/fmt/include/fmt/color.h | 61 +- Externals/fmt/include/fmt/compile.h | 33 +- Externals/fmt/include/fmt/core.h | 611 ++++---- Externals/fmt/include/fmt/format-inl.h | 917 +----------- Externals/fmt/include/fmt/format.h | 1305 +++++++++++++---- Externals/fmt/include/fmt/locale.h | 2 - Externals/fmt/include/fmt/os.h | 5 +- Externals/fmt/include/fmt/ostream.h | 134 +- Externals/fmt/include/fmt/printf.h | 31 +- Externals/fmt/include/fmt/ranges.h | 316 ++-- Externals/fmt/include/fmt/std.h | 171 +++ Externals/fmt/include/fmt/xchar.h | 45 +- Externals/fmt/src/format.cc | 90 +- Externals/fmt/src/os.cc | 13 +- Externals/fmt/support/appveyor-build.py | 43 - Externals/fmt/support/appveyor.yml | 31 - Externals/fmt/support/bazel/.bazelversion | 2 +- Externals/fmt/support/bazel/BUILD.bazel | 7 +- .../fmt/support/cmake/fmt-config.cmake.in | 5 +- Externals/fmt/support/manage.py | 6 + 25 files changed, 2505 insertions(+), 1916 deletions(-) delete mode 100755 Externals/fmt/include/fmt/locale.h create mode 100644 Externals/fmt/include/fmt/std.h delete mode 100755 Externals/fmt/support/appveyor-build.py delete mode 100755 Externals/fmt/support/appveyor.yml diff --git a/Externals/fmt/CMakeLists.txt b/Externals/fmt/CMakeLists.txt index f6d8bd50db..4ea516f85b 100755 --- a/Externals/fmt/CMakeLists.txt +++ b/Externals/fmt/CMakeLists.txt @@ -218,7 +218,7 @@ endfunction() # Define the fmt library, its includes and the needed defines. add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h - format-inl.h locale.h os.h ostream.h printf.h ranges.h + format-inl.h os.h ostream.h printf.h ranges.h std.h xchar.h) if (FMT_MODULE) set(FMT_SOURCES src/fmt.cc) @@ -263,12 +263,6 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug") endif () if (BUILD_SHARED_LIBS) - if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" AND - NOT EMSCRIPTEN) - # Fix rpmlint warning: - # unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6. - target_link_libraries(fmt -Wl,--as-needed) - endif () target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED) endif () if (FMT_SAFE_DURATION_CAST) diff --git a/Externals/fmt/ChangeLog.rst b/Externals/fmt/ChangeLog.rst index 653002b9ba..4ebc5c7330 100755 --- a/Externals/fmt/ChangeLog.rst +++ b/Externals/fmt/ChangeLog.rst @@ -1,3 +1,515 @@ +9.1.0 - 2022-08-27 +------------------ + +* ``fmt::formatted_size`` now works at compile time + (`#3026 `_). For example + (`godbolt `__): + + .. code:: c++ + + #include + + int main() { + using namespace fmt::literals; + constexpr size_t n = fmt::formatted_size("{}"_cf, 42); + fmt::print("{}\n", n); // prints 2 + } + + Thanks `@marksantaniello (Mark Santaniello) + `_. + +* Fixed handling of invalid UTF-8 + (`#3038 `_, + `#3044 `_, + `#3056 `_). + Thanks `@phprus (Vladislav Shchapov) `_ and + `@skeeto (Christopher Wellons) `_. + +* Improved Unicode support in ``ostream`` overloads of ``print`` + (`#2994 `_, + `#3001 `_, + `#3025 `_). + Thanks `@dimztimz (Dimitrij Mijoski) `_. + +* Fixed handling of the sign specifier in localized formatting on systems with + 32-bit ``wchar_t`` (`#3041 `_). + +* Added support for wide streams to ``fmt::streamed`` + (`#2994 `_). + Thanks `@phprus (Vladislav Shchapov) `_. + +* Added the ``n`` specifier that disables the output of delimiters when + formatting ranges (`#2981 `_, + `#2983 `_). + For example (`godbolt `__): + + .. code:: c++ + + #include + #include + + int main() { + auto v = std::vector{1, 2, 3}; + fmt::print("{:n}\n", v); // prints 1, 2, 3 + } + + Thanks `@BRevzin (Barry Revzin) `_. + +* Worked around problematic ``std::string_view`` constructors introduced in + C++23 (`#3030 `_, + `#3050 `_). + Thanks `@strega-nil-ms (nicole mazzuca) `_. + +* Improve handling (exclusion) of recursive ranges + (`#2968 `_, + `#2974 `_). + Thanks `@Dani-Hub (Daniel Krügler) `_. + +* Improved error reporting in format string compilation + (`#3055 `_). + +* Improved the implementation of + `Dragonbox `_, the algorithm used for + the default floating-point formatting + (`#2984 `_). + Thanks `@jk-jeon (Junekey Jeon) `_. + +* Fixed issues with floating-point formatting on exotic platforms. + +* Improved the implementation of chrono formatting + (`#3010 `_). + Thanks `@phprus (Vladislav Shchapov) `_. + +* Improved documentation + (`#2966 `_, + `#3009 `_, + `#3020 `_, + `#3037 `_). + Thanks `@mwinterb `_, + `@jcelerier (Jean-Michaël Celerier) `_ + and `@remiburtin (Rémi Burtin) `_. + +* Improved build configuration + (`#2991 `_, + `#2995 `_, + `#3004 `_, + `#3007 `_, + `#3040 `_). + Thanks `@dimztimz (Dimitrij Mijoski) `_ and + `@hwhsu1231 (Haowei Hsu) `_. + +* Fixed various warnings and compilation issues + (`#2969 `_, + `#2971 `_, + `#2975 `_, + `#2982 `_, + `#2985 `_, + `#2988 `_, + `#3000 `_, + `#3006 `_, + `#3014 `_, + `#3015 `_, + `#3021 `_, + `#3023 `_, + `#3024 `_, + `#3029 `_, + `#3043 `_, + `#3052 `_, + `#3053 `_, + `#3054 `_). + Thanks `@h-friederich (Hannes Friederich) `_, + `@dimztimz (Dimitrij Mijoski) `_, + `@olupton (Olli Lupton) `_, + `@bernhardmgruber (Bernhard Manfred Gruber) + `_, + `@phprus (Vladislav Shchapov) `_. + +9.0.0 - 2022-07-04 +------------------ + +* Switched to the internal floating point formatter for all decimal presentation + formats. In particular this results in consistent rounding on all platforms + and removing the ``s[n]printf`` fallback for decimal FP formatting. + +* Compile-time floating point formatting no longer requires the header-only + mode. For example (`godbolt `__): + + .. code:: c++ + + #include + #include + + consteval auto compile_time_dtoa(double value) -> std::array { + auto result = std::array(); + fmt::format_to(result.data(), FMT_COMPILE("{}"), value); + return result; + } + + constexpr auto answer = compile_time_dtoa(0.42); + + works with the default settings. + +* Improved the implementation of + `Dragonbox `_, the algorithm used for + the default floating-point formatting + (`#2713 `_, + `#2750 `_). + Thanks `@jk-jeon (Junekey Jeon) `_. + +* Made ``fmt::to_string`` work with ``__float128``. This uses the internal + FP formatter and works even on system without ``__float128`` support in + ``[s]printf``. + +* Disabled automatic ``std::ostream`` insertion operator (``operator<<``) + discovery when ``fmt/ostream.h`` is included to prevent ODR violations. + You can get the old behavior by defining ``FMT_DEPRECATED_OSTREAM`` but this + will be removed in the next major release. Use ``fmt::streamed`` or + ``fmt::ostream_formatter`` to enable formatting via ``std::ostream`` instead. + +* Added ``fmt::ostream_formatter`` that can be used to write ``formatter`` + specializations that perform formatting via ``std::ostream``. + For example (`godbolt `__): + + .. code:: c++ + + #include + + struct date { + int year, month, day; + + friend std::ostream& operator<<(std::ostream& os, const date& d) { + return os << d.year << '-' << d.month << '-' << d.day; + } + }; + + template <> struct fmt::formatter : ostream_formatter {}; + + std::string s = fmt::format("The date is {}", date{2012, 12, 9}); + // s == "The date is 2012-12-9" + +* Added the ``fmt::streamed`` function that takes an object and formats it + via ``std::ostream``. + For example (`godbolt `__): + + .. code:: c++ + + #include + #include + + int main() { + fmt::print("Current thread id: {}\n", + fmt::streamed(std::this_thread::get_id())); + } + + Note that ``fmt/std.h`` provides a ``formatter`` specialization for + ``std::thread::id`` so you don't need to format it via ``std::ostream``. + +* Deprecated implicit conversions of unscoped enums to integers for consistency + with scoped enums. + +* Added an argument-dependent lookup based ``format_as`` extension API to + simplify formatting of enums. + +* Added experimental ``std::variant`` formatting support + (`#2941 `_). + For example (`godbolt `__): + + .. code:: c++ + + #include + #include + + int main() { + auto v = std::variant(42); + fmt::print("{}\n", v); + } + + prints:: + + variant(42) + + Thanks `@jehelset `_. + +* Added experimental ``std::filesystem::path`` formatting support + (`#2865 `_, + `#2902 `_, + `#2917 `_, + `#2918 `_). + For example (`godbolt `__): + + .. code:: c++ + + #include + #include + + int main() { + fmt::print("There is no place like {}.", std::filesystem::path("/home")); + } + + prints:: + + There is no place like "/home". + + Thanks `@phprus (Vladislav Shchapov) `_. + +* Added a ``std::thread::id`` formatter to ``fmt/std.h``. + For example (`godbolt `__): + + .. code:: c++ + + #include + #include + + int main() { + fmt::print("Current thread id: {}\n", std::this_thread::get_id()); + } + +* Added ``fmt::styled`` that applies a text style to an individual argument + (`#2793 `_). + For example (`godbolt `__): + + .. code:: c++ + + #include + #include + + int main() { + auto now = std::chrono::system_clock::now(); + fmt::print( + "[{}] {}: {}\n", + fmt::styled(now, fmt::emphasis::bold), + fmt::styled("error", fg(fmt::color::red)), + "something went wrong"); + } + + prints + + .. image:: https://user-images.githubusercontent.com/576385/ + 175071215-12809244-dab0-4005-96d8-7cd911c964d5.png + + Thanks `@rbrugo (Riccardo Brugo) `_. + +* Made ``fmt::print`` overload for text styles correctly handle UTF-8 + (`#2681 `_, + `#2701 `_). + Thanks `@AlexGuteniev (Alex Guteniev) `_. + +* Fixed Unicode handling when writing to an ostream. + +* Added support for nested specifiers to range formatting + (`#2673 `_). + For example (`godbolt `__): + + .. code:: c++ + + #include + #include + + int main() { + fmt::print("{::#x}\n", std::vector{10, 20, 30}); + } + + prints ``[0xa, 0x14, 0x1e]``. + + Thanks `@BRevzin (Barry Revzin) `_. + +* Implemented escaping of wide strings in ranges + (`#2904 `_). + Thanks `@phprus (Vladislav Shchapov) `_. + +* Added support for ranges with ``begin`` / ``end`` found via the + argument-dependent lookup + (`#2807 `_). + Thanks `@rbrugo (Riccardo Brugo) `_. + +* Fixed formatting of certain kinds of ranges of ranges + (`#2787 `_). + Thanks `@BRevzin (Barry Revzin) `_. + +* Fixed handling of maps with element types other than ``std::pair`` + (`#2944 `_). + Thanks `@BrukerJWD (Jonathan W) `_. + +* Made tuple formatter enabled only if elements are formattable + (`#2939 `_, + `#2940 `_). + Thanks `@jehelset `_. + +* Made ``fmt::join`` compatible with format string compilation + (`#2719 `_, + `#2720 `_). + Thanks `@phprus (Vladislav Shchapov) `_. + +* Made compile-time checks work with named arguments of custom types and + ``std::ostream`` ``print`` overloads + (`#2816 `_, + `#2817 `_, + `#2819 `_). + Thanks `@timsong-cpp `_. + +* Removed ``make_args_checked`` because it is no longer needed for compile-time + checks (`#2760 `_). + Thanks `@phprus (Vladislav Shchapov) `_. + +* Removed the following deprecated APIs: ``_format``, ``arg_join``, + the ``format_to`` overload that takes a memory buffer, + ``[v]fprintf`` that takes an ``ostream``. + +* Removed the deprecated implicit conversion of ``[const] signed char*`` and + ``[const] unsigned char*`` to C strings. + +* Removed the deprecated ``fmt/locale.h``. + +* Replaced the deprecated ``fileno()`` with ``descriptor()`` in + ``buffered_file``. + +* Moved ``to_string_view`` to the ``detail`` namespace since it's an + implementation detail. + +* Made access mode of a created file consistent with ``fopen`` by setting + ``S_IWGRP`` and ``S_IWOTH`` + (`#2733 `_). + Thanks `@arogge (Andreas Rogge) `_. + +* Removed a redundant buffer resize when formatting to ``std::ostream`` + (`#2842 `_, + `#2843 `_). + Thanks `@jcelerier (Jean-Michaël Celerier) `_. + +* Made precision computation for strings consistent with width + (`#2888 `_). + +* Fixed handling of locale separators in floating point formatting + (`#2830 `_). + +* Made sign specifiers work with ``__int128_t`` + (`#2773 `_). + +* Improved support for systems such as CHERI with extra data stored in pointers + (`#2932 `_). + Thanks `@davidchisnall (David Chisnall) `_. + +* Improved documentation + (`#2706 `_, + `#2712 `_, + `#2789 `_, + `#2803 `_, + `#2805 `_, + `#2815 `_, + `#2924 `_). + Thanks `@BRevzin (Barry Revzin) `_, + `@Pokechu22 `_, + `@setoye (Alta) `_, + `@rtobar `_, + `@rbrugo (Riccardo Brugo) `_, + `@anoonD (cre) `_, + `@leha-bot (Alex) `_. + +* Improved build configuration + (`#2766 `_, + `#2772 `_, + `#2836 `_, + `#2852 `_, + `#2907 `_, + `#2913 `_, + `#2914 `_). + Thanks `@kambala-decapitator (Andrey Filipenkov) + `_, + `@mattiasljungstrom (Mattias Ljungström) + `_, + `@kieselnb (Nick Kiesel) `_, + `@nathannaveen `_, + `@Vertexwahn `_. + +* Fixed various warnings and compilation issues + (`#2408 `_, + `#2507 `_, + `#2697 `_, + `#2715 `_, + `#2717 `_, + `#2722 `_, + `#2724 `_, + `#2725 `_, + `#2726 `_, + `#2728 `_, + `#2732 `_, + `#2738 `_, + `#2742 `_, + `#2744 `_, + `#2745 `_, + `#2746 `_, + `#2754 `_, + `#2755 `_, + `#2757 `_, + `#2758 `_, + `#2761 `_, + `#2762 `_, + `#2763 `_, + `#2765 `_, + `#2769 `_, + `#2770 `_, + `#2771 `_, + `#2777 `_, + `#2779 `_, + `#2782 `_, + `#2783 `_, + `#2794 `_, + `#2796 `_, + `#2797 `_, + `#2801 `_, + `#2802 `_, + `#2808 `_, + `#2818 `_, + `#2819 `_, + `#2829 `_, + `#2835 `_, + `#2848 `_, + `#2860 `_, + `#2861 `_, + `#2882 `_, + `#2886 `_, + `#2891 `_, + `#2892 `_, + `#2895 `_, + `#2896 `_, + `#2903 `_, + `#2906 `_, + `#2908 `_, + `#2909 `_, + `#2920 `_, + `#2922 `_, + `#2927 `_, + `#2929 `_, + `#2936 `_, + `#2937 `_, + `#2938 `_, + `#2951 `_, + `#2954 `_, + `#2957 `_, + `#2958 `_, + `#2960 `_). + Thanks `@matrackif `_ + `@Tobi823 (Tobias Hellmann) `_, + `@ivan-volnov (Ivan Volnov) `_, + `@VasiliPupkin256 `_, + `@federico-busato (Federico) `_, + `@barcharcraz (Charlie Barto) `_, + `@jk-jeon (Junekey Jeon) `_, + `@HazardyKnusperkeks (Björn Schäpers) + `_, + `@dalboris (Boris Dalstein) `_, + `@seanm (Sean McBride) `_, + `@gsjaardema (Greg Sjaardema) `_, + `@timsong-cpp `_, + `@seanm (Sean McBride) `_, + `@frithrah `_, + `@chronoxor (Ivan Shynkarenka) `_, + `@Agga `_, + `@madmaxoft (Mattes D) `_, + `@JurajX (Juraj) `_, + `@phprus (Vladislav Shchapov) `_, + `@Dani-Hub (Daniel Krügler) `_. + 8.1.1 - 2022-01-06 ------------------ @@ -148,6 +660,10 @@ [" aan"] +* Added an experimental ``?`` specifier for escaping strings. + (`#2674 `_). + Thanks `@BRevzin (Barry Revzin) `_. + * Switched to JSON-like representation of maps and sets for consistency with Python's ``str.format``. For example (`godbolt `__): @@ -367,7 +883,8 @@ `@alexezeder (Alexey Ochapov) `_, `@andrewcorrigan (Andrew Corrigan) `_, `@lucpelletier `_, - `@HazardyKnusperkeks (Björn Schäpers) `_. + `@HazardyKnusperkeks (Björn Schäpers) + `_. 8.0.1 - 2021-07-02 ------------------ diff --git a/Externals/fmt/README.rst b/Externals/fmt/README.rst index feb4ab5e7d..cc6d7c41ad 100755 --- a/Externals/fmt/README.rst +++ b/Externals/fmt/README.rst @@ -12,9 +12,6 @@ .. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows -.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v?svg=true - :target: https://ci.appveyor.com/project/vitaut/fmt - .. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg :alt: fmt is continuously fuzzed at oss-fuzz :target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\ @@ -33,6 +30,8 @@ help victims of the war in Ukraine: https://www.stopputin.net/. `Documentation `__ +`Cheat Sheets `__ + Q&A: ask questions on `StackOverflow with the tag fmt `_. @@ -342,9 +341,12 @@ Projects using this library * `Folly `_: Facebook open-source library +* `GemRB `_: a portable open-source implementation of + Bioware’s Infinity Engine + * `Grand Mountain Adventure `_: - A beautiful open-world ski & snowboarding game + a beautiful open-world ski & snowboarding game * `HarpyWar/pvpgn `_: Player vs Player Gaming Network with tweaks diff --git a/Externals/fmt/fmt.vcxproj b/Externals/fmt/fmt.vcxproj index 3f0259177c..b7015f0558 100644 --- a/Externals/fmt/fmt.vcxproj +++ b/Externals/fmt/fmt.vcxproj @@ -32,11 +32,11 @@ - + diff --git a/Externals/fmt/include/fmt/chrono.h b/Externals/fmt/include/fmt/chrono.h index 7872fb4bde..b112f76e99 100755 --- a/Externals/fmt/include/fmt/chrono.h +++ b/Externals/fmt/include/fmt/chrono.h @@ -203,7 +203,7 @@ To safe_duration_cast(std::chrono::duration from, } const auto min1 = (std::numeric_limits::min)() / Factor::num; - if (count < min1) { + if (!std::is_unsigned::value && count < min1) { ec = 1; return {}; } @@ -345,7 +345,7 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) if (detail::is_utf8() && loc != get_classic_locale()) { // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and // gcc-4. -#if FMT_MSC_VER != 0 || \ +#if FMT_MSC_VERSION != 0 || \ (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 // and newer. @@ -469,7 +469,7 @@ inline std::tm localtime(std::time_t time) { bool fallback(int res) { return res == 0; } -#if !FMT_MSC_VER +#if !FMT_MSC_VERSION bool fallback(detail::null<>) { using namespace fmt::detail; std::tm* tm = std::localtime(&time_); @@ -515,7 +515,7 @@ inline std::tm gmtime(std::time_t time) { bool fallback(int res) { return res == 0; } -#if !FMT_MSC_VER +#if !FMT_MSC_VERSION bool fallback(detail::null<>) { std::tm* tm = std::gmtime(&time_); if (tm) tm_ = *tm; @@ -1396,7 +1396,8 @@ inline bool isfinite(T) { // Converts value to Int and checks that it's in the range [0, upper). template ::value)> inline Int to_nonnegative_int(T value, Int upper) { - FMT_ASSERT(value >= 0 && to_unsigned(value) <= to_unsigned(upper), + FMT_ASSERT(std::is_unsigned::value || + (value >= 0 && to_unsigned(value) <= to_unsigned(upper)), "invalid value"); (void)upper; return static_cast(value); @@ -1466,8 +1467,7 @@ inline std::chrono::duration get_milliseconds( // C++20 spec. If more than 18 fractional digits are required then returns 6 for // microseconds precision. template ::max() / 10)> + bool Enabled = (N < 19) && (Num <= max_value() / 10)> struct count_fractional_digits { static constexpr int value = Num % Den == 0 ? N : count_fractional_digits::value; @@ -1777,7 +1777,7 @@ struct chrono_formatter { format_to(std::back_inserter(buf), runtime("{:.{}f}"), std::fmod(val * static_cast(Period::num) / static_cast(Period::den), - 60), + static_cast(60)), num_fractional_digits); if (negative) *out++ = '-'; if (buf.size() < 2 || buf[1] == '.') *out++ = '0'; @@ -2002,13 +2002,9 @@ template struct formatter, Char> : formatter { FMT_CONSTEXPR formatter() { - this->do_parse(default_specs, - default_specs + sizeof(default_specs) / sizeof(Char)); - } - - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return this->do_parse(ctx.begin(), ctx.end(), true); + basic_string_view default_specs = + detail::string_literal{}; + this->do_parse(default_specs.begin(), default_specs.end()); } template @@ -2016,15 +2012,8 @@ struct formatter, FormatContext& ctx) const -> decltype(ctx.out()) { return formatter::format(localtime(val), ctx); } - - static constexpr const Char default_specs[] = {'%', 'F', ' ', '%', 'T'}; }; -template -constexpr const Char - formatter, - Char>::default_specs[]; - template struct formatter { private: enum class spec { @@ -2036,13 +2025,18 @@ template struct formatter { basic_string_view specs; protected: - template - FMT_CONSTEXPR auto do_parse(It begin, It end, bool with_default = false) - -> It { + template FMT_CONSTEXPR auto do_parse(It begin, It end) -> It { if (begin != end && *begin == ':') ++begin; end = detail::parse_chrono_format(begin, end, detail::tm_format_checker()); - if (!with_default || end != begin) - specs = {begin, detail::to_unsigned(end - begin)}; + // Replace default spec only if the new spec is not empty. + if (end != begin) specs = {begin, detail::to_unsigned(end - begin)}; + return end; + } + + public: + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto end = this->do_parse(ctx.begin(), ctx.end()); // basic_string_view<>::compare isn't constexpr before C++17. if (specs.size() == 2 && specs[0] == Char('%')) { if (specs[1] == Char('F')) @@ -2053,12 +2047,6 @@ template struct formatter { return end; } - public: - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return this->do_parse(ctx.begin(), ctx.end()); - } - template auto format(const std::tm& tm, FormatContext& ctx) const -> decltype(ctx.out()) { diff --git a/Externals/fmt/include/fmt/color.h b/Externals/fmt/include/fmt/color.h index 762809ca71..4c163277ef 100755 --- a/Externals/fmt/include/fmt/color.h +++ b/Externals/fmt/include/fmt/color.h @@ -10,13 +10,6 @@ #include "format.h" -// __declspec(deprecated) is broken in some MSVC versions. -#if FMT_MSC_VER -# define FMT_DEPRECATED_NONMSVC -#else -# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED -#endif - FMT_BEGIN_NAMESPACE FMT_MODULE_EXPORT_BEGIN @@ -270,16 +263,6 @@ class text_style { return lhs |= rhs; } - FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=( - const text_style& rhs) { - return and_assign(rhs); - } - - FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style - operator&(text_style lhs, const text_style& rhs) { - return lhs.and_assign(rhs); - } - FMT_CONSTEXPR bool has_foreground() const noexcept { return set_foreground_color; } @@ -315,36 +298,9 @@ class text_style { } } - // DEPRECATED! - FMT_CONSTEXPR text_style& and_assign(const text_style& rhs) { - if (!set_foreground_color) { - set_foreground_color = rhs.set_foreground_color; - foreground_color = rhs.foreground_color; - } else if (rhs.set_foreground_color) { - if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) - FMT_THROW(format_error("can't AND a terminal color")); - foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; - } + friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept; - if (!set_background_color) { - set_background_color = rhs.set_background_color; - background_color = rhs.background_color; - } else if (rhs.set_background_color) { - if (!background_color.is_rgb || !rhs.background_color.is_rgb) - FMT_THROW(format_error("can't AND a terminal color")); - background_color.value.rgb_color &= rhs.background_color.value.rgb_color; - } - - ems = static_cast(static_cast(ems) & - static_cast(rhs.ems)); - return *this; - } - - friend FMT_CONSTEXPR_DECL text_style - fg(detail::color_type foreground) noexcept; - - friend FMT_CONSTEXPR_DECL text_style - bg(detail::color_type background) noexcept; + friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept; detail::color_type foreground_color; detail::color_type background_color; @@ -527,7 +483,7 @@ template > void vprint(std::FILE* f, const text_style& ts, const S& format, basic_format_args>> args) { basic_memory_buffer buf; - detail::vformat_to(buf, ts, to_string_view(format), args); + detail::vformat_to(buf, ts, detail::to_string_view(format), args); if (detail::is_utf8()) { detail::print(f, basic_string_view(buf.begin(), buf.size())); } else { @@ -577,7 +533,7 @@ inline std::basic_string vformat( const text_style& ts, const S& format_str, basic_format_args>> args) { basic_memory_buffer buf; - detail::vformat_to(buf, ts, to_string_view(format_str), args); + detail::vformat_to(buf, ts, detail::to_string_view(format_str), args); return fmt::to_string(buf); } @@ -596,7 +552,7 @@ inline std::basic_string vformat( template > inline std::basic_string format(const text_style& ts, const S& format_str, const Args&... args) { - return fmt::vformat(ts, to_string_view(format_str), + return fmt::vformat(ts, detail::to_string_view(format_str), fmt::make_format_args>(args...)); } @@ -631,7 +587,7 @@ template typename std::enable_if::type { - return vformat_to(out, ts, to_string_view(format_str), + return vformat_to(out, ts, detail::to_string_view(format_str), fmt::make_format_args>>(args...)); } @@ -678,8 +634,9 @@ struct formatter, Char> : formatter { **Example**:: - fmt::print("Elapsed time: {s:.2f} seconds", - fmt::styled(1.23, fmt::fg(fmt::color::green) | fmt::bg(fmt::color::blue))); + fmt::print("Elapsed time: {0:.2f} seconds", + fmt::styled(1.23, fmt::fg(fmt::color::green) | + fmt::bg(fmt::color::blue))); \endrst */ template diff --git a/Externals/fmt/include/fmt/compile.h b/Externals/fmt/include/fmt/compile.h index 296ef42c69..933668c41c 100644 --- a/Externals/fmt/include/fmt/compile.h +++ b/Externals/fmt/include/fmt/compile.h @@ -14,8 +14,8 @@ FMT_BEGIN_NAMESPACE namespace detail { template -inline counting_iterator copy_str(InputIt begin, InputIt end, - counting_iterator it) { +FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end, + counting_iterator it) { return it + (end - begin); } @@ -123,7 +123,7 @@ struct is_compiled_string : std::is_base_of {}; # define FMT_COMPILE(s) FMT_STRING(s) #endif -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS template Str> struct udl_compiled_string : compiled_string { @@ -337,10 +337,11 @@ template constexpr parse_specs_result parse_specs(basic_string_view str, size_t pos, int next_arg_id) { str.remove_prefix(pos); - auto ctx = basic_format_parse_context(str, {}, next_arg_id); + auto ctx = compile_parse_context(str, max_value(), nullptr, {}, + next_arg_id); auto f = formatter(); auto end = f.parse(ctx); - return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1, + return {f, pos + fmt::detail::to_unsigned(end - str.data()), next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; } @@ -396,13 +397,20 @@ constexpr auto parse_replacement_field_then_tail(S format_str) { return parse_tail( field::type, ARG_INDEX>(), format_str); - } else if constexpr (c == ':') { + } else if constexpr (c != ':') { + FMT_THROW(format_error("expected ':'")); + } else { constexpr auto result = parse_specs::type>( str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); - return parse_tail( - spec_field::type, ARG_INDEX>{ - result.fmt}, - format_str); + if constexpr (result.end >= str.size() || str[result.end] != '}') { + FMT_THROW(format_error("expected '}'")); + return 0; + } else { + return parse_tail( + spec_field::type, ARG_INDEX>{ + result.fmt}, + format_str); + } } } @@ -567,7 +575,8 @@ format_to_n_result format_to_n(OutputIt out, size_t n, template ::value)> -size_t formatted_size(const S& format_str, const Args&... args) { +FMT_CONSTEXPR20 size_t formatted_size(const S& format_str, + const Args&... args) { return fmt::format_to(detail::counting_iterator(), format_str, args...) .count(); } @@ -586,7 +595,7 @@ void print(const S& format_str, const Args&... args) { print(stdout, format_str, args...); } -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS inline namespace literals { template constexpr auto operator""_cf() { using char_t = remove_cvref_t; diff --git a/Externals/fmt/include/fmt/core.h b/Externals/fmt/include/fmt/core.h index 3905a00197..f6a37af9e3 100755 --- a/Externals/fmt/include/fmt/core.h +++ b/Externals/fmt/include/fmt/core.h @@ -17,7 +17,7 @@ #include // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 80102 +#define FMT_VERSION 90100 #if defined(__clang__) && !defined(__ibmxl__) # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) @@ -49,36 +49,29 @@ # define FMT_ICC_VERSION 0 #endif -#ifdef __NVCOMPILER -# define FMT_NVCOMPILER_VERSION \ - (__NVCOMPILER_MAJOR__ * 100 + __NVCOMPILER_MINOR__) -#else -# define FMT_NVCOMPILER_VERSION 0 -#endif - -#ifdef __NVCC__ -# define FMT_NVCC __NVCC__ -#else -# define FMT_NVCC 0 -#endif - #ifdef _MSC_VER -# define FMT_MSC_VER _MSC_VER +# define FMT_MSC_VERSION _MSC_VER # define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) #else -# define FMT_MSC_VER 0 +# define FMT_MSC_VERSION 0 # define FMT_MSC_WARNING(...) #endif +#ifdef _MSVC_LANG +# define FMT_CPLUSPLUS _MSVC_LANG +#else +# define FMT_CPLUSPLUS __cplusplus +#endif + #ifdef __has_feature # define FMT_HAS_FEATURE(x) __has_feature(x) #else # define FMT_HAS_FEATURE(x) 0 #endif -#if defined(__has_include) && \ - (!defined(__INTELLISENSE__) || FMT_MSC_VER > 1900) && \ - (!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600) +#if (defined(__has_include) || FMT_ICC_VERSION >= 1600 || \ + FMT_MSC_VERSION > 1900) && \ + !defined(__INTELLISENSE__) # define FMT_HAS_INCLUDE(x) __has_include(x) #else # define FMT_HAS_INCLUDE(x) 0 @@ -90,12 +83,6 @@ # define FMT_HAS_CPP_ATTRIBUTE(x) 0 #endif -#ifdef _MSVC_LANG -# define FMT_CPLUSPLUS _MSVC_LANG -#else -# define FMT_CPLUSPLUS __cplusplus -#endif - #define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) @@ -105,37 +92,38 @@ // Check if relaxed C++14 constexpr is supported. // GCC doesn't allow throw in constexpr until version 6 (bug 67371). #ifndef FMT_USE_CONSTEXPR -# define FMT_USE_CONSTEXPR \ - (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1912 || \ - (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ - !FMT_NVCC && !FMT_ICC_VERSION +# if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \ + (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \ + !FMT_ICC_VERSION && !defined(__NVCC__) +# define FMT_USE_CONSTEXPR 1 +# else +# define FMT_USE_CONSTEXPR 0 +# endif #endif #if FMT_USE_CONSTEXPR # define FMT_CONSTEXPR constexpr -# define FMT_CONSTEXPR_DECL constexpr #else # define FMT_CONSTEXPR -# define FMT_CONSTEXPR_DECL #endif -#if ((__cplusplus >= 202002L) && \ +#if ((FMT_CPLUSPLUS >= 202002L) && \ (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \ - (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002) + (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002) # define FMT_CONSTEXPR20 constexpr #else # define FMT_CONSTEXPR20 #endif -// Check if constexpr std::char_traits<>::compare,length is supported. +// Check if constexpr std::char_traits<>::{compare,length} are supported. #if defined(__GLIBCXX__) -# if __cplusplus >= 201703L && defined(_GLIBCXX_RELEASE) && \ +# if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \ _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE. # define FMT_CONSTEXPR_CHAR_TRAITS constexpr # endif -#elif defined(_LIBCPP_VERSION) && __cplusplus >= 201703L && \ +#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \ _LIBCPP_VERSION >= 4000 # define FMT_CONSTEXPR_CHAR_TRAITS constexpr -#elif FMT_MSC_VER >= 1914 && _MSVC_LANG >= 201703L +#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L # define FMT_CONSTEXPR_CHAR_TRAITS constexpr #endif #ifndef FMT_CONSTEXPR_CHAR_TRAITS @@ -145,35 +133,43 @@ // Check if exceptions are disabled. #ifndef FMT_EXCEPTIONS # if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ - FMT_MSC_VER && !_HAS_EXCEPTIONS + (FMT_MSC_VERSION && !_HAS_EXCEPTIONS) # define FMT_EXCEPTIONS 0 # else # define FMT_EXCEPTIONS 1 # endif #endif +#ifndef FMT_DEPRECATED +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900 +# define FMT_DEPRECATED [[deprecated]] +# else +# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) +# define FMT_DEPRECATED __attribute__((deprecated)) +# elif FMT_MSC_VERSION +# define FMT_DEPRECATED __declspec(deprecated) +# else +# define FMT_DEPRECATED /* deprecated */ +# endif +# endif +#endif + // [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code // warnings. -#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \ - !FMT_NVCC +#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \ + !defined(__NVCC__) # define FMT_NORETURN [[noreturn]] #else # define FMT_NORETURN #endif -#if __cplusplus == 201103L || __cplusplus == 201402L -# if defined(__INTEL_COMPILER) || defined(__PGI) -# define FMT_FALLTHROUGH -# elif defined(__clang__) -# define FMT_FALLTHROUGH [[clang::fallthrough]] -# elif FMT_GCC_VERSION >= 700 && \ - (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) -# define FMT_FALLTHROUGH [[gnu::fallthrough]] -# else -# define FMT_FALLTHROUGH -# endif -#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) +#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) # define FMT_FALLTHROUGH [[fallthrough]] +#elif defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +#elif FMT_GCC_VERSION >= 700 && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] #else # define FMT_FALLTHROUGH #endif @@ -204,31 +200,20 @@ # endif #endif -#ifndef FMT_DEPRECATED -# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 -# define FMT_DEPRECATED [[deprecated]] -# else -# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) -# define FMT_DEPRECATED __attribute__((deprecated)) -# elif FMT_MSC_VER -# define FMT_DEPRECATED __declspec(deprecated) -# else -# define FMT_DEPRECATED /* deprecated */ -# endif -# endif -#endif +// An inline std::forward replacement. +#define FMT_FORWARD(...) static_cast(__VA_ARGS__) #ifdef _MSC_VER # define FMT_UNCHECKED_ITERATOR(It) \ using _Unchecked_type = It // Mark iterator as checked. #else -# define FMT_UNCHECKED_ITERATOR(It) using DummyTypeName = It +# define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It #endif #ifndef FMT_BEGIN_NAMESPACE # define FMT_BEGIN_NAMESPACE \ namespace fmt { \ - inline namespace v8 { + inline namespace v9 { # define FMT_END_NAMESPACE \ } \ } @@ -262,25 +247,24 @@ #endif // libc++ supports string_view in pre-c++17. -#if (FMT_HAS_INCLUDE() && \ - (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ - (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) +#if FMT_HAS_INCLUDE() && \ + (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) # include # define FMT_USE_STRING_VIEW -#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L +#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L # include # define FMT_USE_EXPERIMENTAL_STRING_VIEW #endif #ifndef FMT_UNICODE -# define FMT_UNICODE !FMT_MSC_VER +# define FMT_UNICODE !FMT_MSC_VERSION #endif #ifndef FMT_CONSTEVAL -# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ - __cplusplus > 201703L && !defined(__apple_build_version__)) || \ - (defined(__cpp_consteval) && \ - (!FMT_MSC_VER || _MSC_FULL_VER >= 193030704)) +# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ + FMT_CPLUSPLUS >= 202002L && !defined(__apple_build_version__)) || \ + (defined(__cpp_consteval) && \ + (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704)) // consteval is broken in MSVC before VS2022 and Apple clang 13. # define FMT_CONSTEVAL consteval # define FMT_HAS_CONSTEVAL @@ -289,19 +273,20 @@ # endif #endif -#ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -# if defined(__cpp_nontype_template_args) && \ - ((FMT_GCC_VERSION >= 903 && __cplusplus >= 201709L) || \ - __cpp_nontype_template_args >= 201911L) -# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1 +#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS +# if defined(__cpp_nontype_template_args) && \ + ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \ + __cpp_nontype_template_args >= 201911L) && \ + !defined(__NVCOMPILER) +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 # else -# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0 +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 # endif #endif // Enable minimal optimizations for more compact code in debug mode. FMT_GCC_PRAGMA("GCC push_options") -#if !defined(__OPTIMIZE__) && !FMT_NVCOMPILER_VERSION +#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) FMT_GCC_PRAGMA("GCC optimize(\"Og\")") #endif @@ -325,6 +310,18 @@ template using type_identity_t = typename type_identity::type; template using underlying_t = typename std::underlying_type::type; +template struct disjunction : std::false_type {}; +template struct disjunction

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

: P {}; +template +struct conjunction + : conditional_t, P1> {}; + struct monostate { constexpr monostate() {} }; @@ -340,7 +337,7 @@ struct monostate { FMT_BEGIN_DETAIL_NAMESPACE -// Suppress "unused variable" warnings with the method described in +// Suppresses "unused variable" warnings with the method described in // https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. // (void)var does not work on many Intel compilers. template FMT_CONSTEXPR void ignore_unused(const T&...) {} @@ -355,7 +352,7 @@ constexpr FMT_INLINE auto is_constant_evaluated( #endif } -// A function to suppress "conditional expression is constant" warnings. +// Suppresses "conditional expression is constant" warnings. template constexpr FMT_INLINE auto const_check(T value) -> T { return value; } @@ -365,7 +362,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, #ifndef FMT_ASSERT # ifdef NDEBUG -// FMT_ASSERT is not empty to avoid -Werror=empty-body. +// FMT_ASSERT is not empty to avoid -Wempty-body. # define FMT_ASSERT(condition, message) \ ::fmt::detail::ignore_unused((condition), (message)) # else @@ -387,8 +384,8 @@ template struct std_string_view {}; #ifdef FMT_USE_INT128 // Do nothing. -#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \ - !(FMT_CLANG_VERSION && FMT_MSC_VER) +#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ + !(FMT_CLANG_VERSION && FMT_MSC_VERSION) # define FMT_USE_INT128 1 using int128_opt = __int128_t; // An optional native 128-bit integer. using uint128_opt = __uint128_t; @@ -409,15 +406,14 @@ template auto convert_for_visit(T) -> monostate { return {}; } template FMT_CONSTEXPR auto to_unsigned(Int value) -> typename std::make_unsigned::type { - FMT_ASSERT(value >= 0, "negative value"); + FMT_ASSERT(std::is_unsigned::value || value >= 0, "negative value"); return static_cast::type>(value); } FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5"; constexpr auto is_utf8() -> bool { - // Avoid buggy sign extensions in MSVC's constant evaluation mode. - // https://developercommunity.visualstudio.com/t/C-difference-in-behavior-for-unsigned/1233612 + // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297). using uchar = unsigned char; return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 && uchar(micro[1]) == 0xB5); @@ -527,6 +523,14 @@ using string_view = basic_string_view; template struct is_char : std::false_type {}; template <> struct is_char : std::true_type {}; +FMT_BEGIN_DETAIL_NAMESPACE + +// A base class for compile-time strings. +struct compile_string {}; + +template +struct is_compile_string : std::is_base_of {}; + // Returns a string view of `s`. template ::value)> FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view { @@ -543,33 +547,21 @@ constexpr auto to_string_view(basic_string_view s) return s; } template >::value)> -inline auto to_string_view(detail::std_string_view s) - -> basic_string_view { + FMT_ENABLE_IF(!std::is_empty>::value)> +inline auto to_string_view(std_string_view s) -> basic_string_view { return s; } - -// A base class for compile-time strings. It is defined in the fmt namespace to -// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42). -struct compile_string {}; - -template -struct is_compile_string : std::is_base_of {}; - template ::value)> constexpr auto to_string_view(const S& s) -> basic_string_view { return basic_string_view(s); } - -FMT_BEGIN_DETAIL_NAMESPACE - void to_string_view(...); -using fmt::to_string_view; // Specifies whether S is a string type convertible to fmt::basic_string_view. // It should be a constexpr function but MSVC 2017 fails to compile it in // enable_if and MSVC 2015 fails to compile it as an alias template. +// ADL invocation of to_string_view is DEPRECATED! template struct is_string : std::is_class()))> { }; @@ -580,17 +572,60 @@ template struct char_t_impl::value>> { using type = typename result::value_type; }; -// Reports a compile-time error if S is not a valid format string. -template ::value)> -FMT_INLINE void check_format_string(const S&) { -#ifdef FMT_ENFORCE_COMPILE_STRING - static_assert(is_compile_string::value, - "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " - "FMT_STRING."); -#endif +enum class type { + none_type, + // Integer types should go first, + int_type, + uint_type, + long_long_type, + ulong_long_type, + int128_type, + uint128_type, + bool_type, + char_type, + last_integer_type = char_type, + // followed by floating-point types. + float_type, + double_type, + long_double_type, + last_numeric_type = long_double_type, + cstring_type, + string_type, + pointer_type, + custom_type +}; + +// Maps core type T to the corresponding type enum constant. +template +struct type_constant : std::integral_constant {}; + +#define FMT_TYPE_CONSTANT(Type, constant) \ + template \ + struct type_constant \ + : std::integral_constant {} + +FMT_TYPE_CONSTANT(int, int_type); +FMT_TYPE_CONSTANT(unsigned, uint_type); +FMT_TYPE_CONSTANT(long long, long_long_type); +FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); +FMT_TYPE_CONSTANT(int128_opt, int128_type); +FMT_TYPE_CONSTANT(uint128_opt, uint128_type); +FMT_TYPE_CONSTANT(bool, bool_type); +FMT_TYPE_CONSTANT(Char, char_type); +FMT_TYPE_CONSTANT(float, float_type); +FMT_TYPE_CONSTANT(double, double_type); +FMT_TYPE_CONSTANT(long double, long_double_type); +FMT_TYPE_CONSTANT(const Char*, cstring_type); +FMT_TYPE_CONSTANT(basic_string_view, string_type); +FMT_TYPE_CONSTANT(const void*, pointer_type); + +constexpr bool is_integral_type(type t) { + return t > type::none_type && t <= type::last_integer_type; +} + +constexpr bool is_arithmetic_type(type t) { + return t > type::none_type && t <= type::last_numeric_type; } -template ::value)> -void check_format_string(S); FMT_NORETURN FMT_API void throw_format_error(const char* message); @@ -599,7 +634,9 @@ struct error_handler { constexpr error_handler(const error_handler&) = default; // This function is intentionally not constexpr to give a compile-time error. - FMT_NORETURN FMT_API void on_error(const char* message); + FMT_NORETURN void on_error(const char* message) { + throw_format_error(message); + } }; FMT_END_DETAIL_NAMESPACE @@ -619,6 +656,8 @@ class basic_format_parse_context : private ErrorHandler { basic_string_view format_str_; int next_arg_id_; + FMT_CONSTEXPR void do_check_arg_id(int id); + public: using char_type = Char; using iterator = typename basic_string_view::iterator; @@ -651,25 +690,29 @@ class basic_format_parse_context : private ErrorHandler { the next argument index and switches to the automatic indexing. */ FMT_CONSTEXPR auto next_arg_id() -> int { - // Don't check if the argument id is valid to avoid overhead and because it - // will be checked during formatting anyway. - if (next_arg_id_ >= 0) return next_arg_id_++; - on_error("cannot switch from manual to automatic argument indexing"); - return 0; + if (next_arg_id_ < 0) { + on_error("cannot switch from manual to automatic argument indexing"); + return 0; + } + int id = next_arg_id_++; + do_check_arg_id(id); + return id; } /** Reports an error if using the automatic argument indexing; otherwise switches to the manual indexing. */ - FMT_CONSTEXPR void check_arg_id(int) { - if (next_arg_id_ > 0) + FMT_CONSTEXPR void check_arg_id(int id) { + if (next_arg_id_ > 0) { on_error("cannot switch from automatic to manual argument indexing"); - else - next_arg_id_ = -1; + return; + } + next_arg_id_ = -1; + do_check_arg_id(id); } - FMT_CONSTEXPR void check_arg_id(basic_string_view) {} + FMT_CONSTEXPR void check_dynamic_spec(int arg_id); FMT_CONSTEXPR void on_error(const char* message) { ErrorHandler::on_error(message); @@ -680,6 +723,65 @@ class basic_format_parse_context : private ErrorHandler { using format_parse_context = basic_format_parse_context; +FMT_BEGIN_DETAIL_NAMESPACE +// A parse context with extra data used only in compile-time checks. +template +class compile_parse_context + : public basic_format_parse_context { + private: + int num_args_; + const type* types_; + using base = basic_format_parse_context; + + public: + explicit FMT_CONSTEXPR compile_parse_context( + basic_string_view format_str, int num_args, const type* types, + ErrorHandler eh = {}, int next_arg_id = 0) + : base(format_str, eh, next_arg_id), num_args_(num_args), types_(types) {} + + constexpr auto num_args() const -> int { return num_args_; } + constexpr auto arg_type(int id) const -> type { return types_[id]; } + + FMT_CONSTEXPR auto next_arg_id() -> int { + int id = base::next_arg_id(); + if (id >= num_args_) this->on_error("argument not found"); + return id; + } + + FMT_CONSTEXPR void check_arg_id(int id) { + base::check_arg_id(id); + if (id >= num_args_) this->on_error("argument not found"); + } + using base::check_arg_id; + + FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { + if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) + this->on_error("width/precision is not integer"); + } +}; +FMT_END_DETAIL_NAMESPACE + +template +FMT_CONSTEXPR void +basic_format_parse_context::do_check_arg_id(int id) { + // Argument id is only checked at compile-time during parsing because + // formatting has its own validation. + if (detail::is_constant_evaluated() && FMT_GCC_VERSION >= 1200) { + using context = detail::compile_parse_context; + if (id >= static_cast(this)->num_args()) + on_error("argument not found"); + } +} + +template +FMT_CONSTEXPR void +basic_format_parse_context::check_dynamic_spec(int arg_id) { + if (detail::is_constant_evaluated()) { + using context = detail::compile_parse_context; + static_cast(this)->check_dynamic_spec(arg_id); + } +} + template class basic_format_arg; template class basic_format_args; template class dynamic_format_arg_store; @@ -726,10 +828,10 @@ constexpr auto has_const_formatter() -> bool { template inline auto get_container(std::back_insert_iterator it) -> Container& { - using bi_iterator = std::back_insert_iterator; - struct accessor : bi_iterator { - accessor(bi_iterator iter) : bi_iterator(iter) {} - using bi_iterator::container; + using base = std::back_insert_iterator; + struct accessor : base { + accessor(base b) : base(b) {} + using base::container; }; return *accessor(it).container; } @@ -834,11 +936,11 @@ template class buffer { /** Appends data to the end of the buffer. */ template void append(const U* begin, const U* end); - template FMT_CONSTEXPR auto operator[](I index) -> T& { + template FMT_CONSTEXPR auto operator[](Idx index) -> T& { return ptr_[index]; } - template - FMT_CONSTEXPR auto operator[](I index) const -> const T& { + template + FMT_CONSTEXPR auto operator[](Idx index) const -> const T& { return ptr_[index]; } }; @@ -973,6 +1075,7 @@ class iterator_buffer, : buffer(c.size()), container_(c) {} explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) : iterator_buffer(get_container(out)) {} + auto out() -> std::back_insert_iterator { return std::back_inserter(container_); } @@ -1112,61 +1215,6 @@ constexpr auto count_statically_named_args() -> size_t { return count::value...>(); } -enum class type { - none_type, - // Integer types should go first, - int_type, - uint_type, - long_long_type, - ulong_long_type, - int128_type, - uint128_type, - bool_type, - char_type, - last_integer_type = char_type, - // followed by floating-point types. - float_type, - double_type, - long_double_type, - last_numeric_type = long_double_type, - cstring_type, - string_type, - pointer_type, - custom_type -}; - -// Maps core type T to the corresponding type enum constant. -template -struct type_constant : std::integral_constant {}; - -#define FMT_TYPE_CONSTANT(Type, constant) \ - template \ - struct type_constant \ - : std::integral_constant {} - -FMT_TYPE_CONSTANT(int, int_type); -FMT_TYPE_CONSTANT(unsigned, uint_type); -FMT_TYPE_CONSTANT(long long, long_long_type); -FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); -FMT_TYPE_CONSTANT(int128_opt, int128_type); -FMT_TYPE_CONSTANT(uint128_opt, uint128_type); -FMT_TYPE_CONSTANT(bool, bool_type); -FMT_TYPE_CONSTANT(Char, char_type); -FMT_TYPE_CONSTANT(float, float_type); -FMT_TYPE_CONSTANT(double, double_type); -FMT_TYPE_CONSTANT(long double, long_double_type); -FMT_TYPE_CONSTANT(const Char*, cstring_type); -FMT_TYPE_CONSTANT(basic_string_view, string_type); -FMT_TYPE_CONSTANT(const void*, pointer_type); - -constexpr bool is_integral_type(type t) { - return t > type::none_type && t <= type::last_integer_type; -} - -constexpr bool is_arithmetic_type(type t) { - return t > type::none_type && t <= type::last_numeric_type; -} - struct unformattable {}; struct unformattable_char : unformattable {}; struct unformattable_const : unformattable {}; @@ -1386,26 +1434,6 @@ template struct arg_mapper { return std_string_view(val); } - using cstring_result = conditional_t::value, - const char*, unformattable_pointer>; - - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val) - -> cstring_result { - return map(reinterpret_cast(val)); - } - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val) - -> cstring_result { - return map(reinterpret_cast(val)); - } - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(signed char* val) - -> cstring_result { - return map(reinterpret_cast(val)); - } - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val) - -> cstring_result { - return map(reinterpret_cast(val)); - } - FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; } FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* { return val; @@ -1437,9 +1465,9 @@ template struct arg_mapper { template ::value&& std::is_convertible::value && - !has_formatter::value && + !has_format_as::value && !has_formatter::value && !has_fallback_formatter::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(std::declval().map( static_cast>(val))) { return map(static_cast>(val)); @@ -1458,8 +1486,8 @@ template struct arg_mapper { !std::is_const>::value || has_fallback_formatter::value> {}; -#if (FMT_MSC_VER != 0 && FMT_MSC_VER < 1910) || FMT_ICC_VERSION != 0 || \ - FMT_NVCC != 0 +#if (FMT_MSC_VERSION != 0 && FMT_MSC_VERSION < 1910) || \ + FMT_ICC_VERSION != 0 || defined(__NVCC__) // Workaround a bug in MSVC and Intel (Issue 2746). template FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { return val; @@ -1526,7 +1554,6 @@ class appender : public std::back_insert_iterator> { FMT_UNCHECKED_ITERATOR(appender); auto operator++() noexcept -> appender& { return *this; } - auto operator++(int) noexcept -> appender { return *this; } }; @@ -1641,6 +1668,11 @@ auto copy_str(InputIt begin, InputIt end, appender out) -> appender { return out; } +template +FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt { + return detail::copy_str(rng.begin(), rng.end(), out); +} + #if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 // A workaround for gcc 4.8 to make void_t work in a SFINAE context. template struct void_t_impl { using type = void; }; @@ -1674,7 +1706,7 @@ struct is_contiguous_back_insert_iterator> template <> struct is_contiguous_back_insert_iterator : std::true_type {}; -// A type-erased reference to an std::locale to avoid heavy include. +// A type-erased reference to an std::locale to avoid a heavy include. class locale_ref { private: const void* locale_; // A type-erased pointer to std::locale. @@ -1700,13 +1732,13 @@ constexpr auto encode_types() -> unsigned long long { template FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value { - const auto& arg = arg_mapper().map(std::forward(val)); + const auto& arg = arg_mapper().map(FMT_FORWARD(val)); - [[maybe_unused]] constexpr bool formattable_char = + constexpr bool formattable_char = !std::is_same::value; static_assert(formattable_char, "Mixing character types is disallowed."); - [[maybe_unused]] constexpr bool formattable_const = + constexpr bool formattable_const = !std::is_same::value; static_assert(formattable_const, "Cannot format a const argument."); @@ -1714,12 +1746,12 @@ FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value { // a pointer cast it to "void *" or "const void *". In particular, this // forbids formatting of "[const] volatile char *" which is printed as bool // by iostreams. - [[maybe_unused]] constexpr bool formattable_pointer = + constexpr bool formattable_pointer = !std::is_same::value; static_assert(formattable_pointer, "Formatting of non-void pointers is disallowed."); - [[maybe_unused]] constexpr bool formattable = + constexpr bool formattable = !std::is_same::value; static_assert( formattable, @@ -1867,7 +1899,7 @@ class format_arg_store data_{detail::make_arg< is_packed, Context, detail::mapped_type_constant, Context>::value>( - std::forward(args))...} { + FMT_FORWARD(args))...} { detail::init_named_args(data_.named_args(), 0, 0, args...); } }; @@ -1883,7 +1915,7 @@ class format_arg_store template constexpr auto make_format_args(Args&&... args) -> format_arg_store...> { - return {std::forward(args)...}; + return {FMT_FORWARD(args)...}; } /** @@ -2029,23 +2061,22 @@ template class basic_format_args { // between clang and gcc on ARM (#1919). using format_args = basic_format_args; -// We cannot use enum classes as bit fields because of a gcc bug, -// so we put them in namespaces instead. -// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414. +// We cannot use enum classes as bit fields because of a gcc bug, so we put them +// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414). // Additionally, if an underlying type is specified, older gcc incorrectly warns -// that the type is too small for all the enum values. -// Both these bugs are fixed as of gcc 9.3. +// that the type is too small. Both bugs are fixed in gcc 9.3. #if FMT_GCC_VERSION && FMT_GCC_VERSION < 903 -# define FMT_ENUM_UNDERLYING_TYPE(type) +# define FMT_ENUM_UNDERLYING_TYPE(type) #else -# define FMT_ENUM_UNDERLYING_TYPE(type) : type +# define FMT_ENUM_UNDERLYING_TYPE(type) : type #endif namespace align { -enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char) { none, left, right, center, numeric }; +enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center, + numeric}; } using align_t = align::type; namespace sign { -enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char) { none, minus, plus, space }; +enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space}; } using sign_t = sign::type; @@ -2233,11 +2264,14 @@ class dynamic_specs_handler FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type { context_.check_arg_id(arg_id); + context_.check_dynamic_spec(arg_id); return arg_ref_type(arg_id); } FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type { - return arg_ref_type(context_.next_arg_id()); + int arg_id = context_.next_arg_id(); + context_.check_dynamic_spec(arg_id); + return arg_ref_type(arg_id); } FMT_CONSTEXPR auto make_arg_ref(basic_string_view arg_id) @@ -2263,12 +2297,15 @@ constexpr auto to_ascii(Char c) -> underlying_t { return c; } +FMT_CONSTEXPR inline auto code_point_length_impl(char c) -> int { + return "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4" + [static_cast(c) >> 3]; +} + template FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { if (const_check(sizeof(Char) != 1)) return 1; - auto lengths = - "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"; - int len = lengths[static_cast(*begin) >> 3]; + int len = code_point_length_impl(static_cast(*begin)); // Compute the pointer to the next character early so that the next // iteration can start working on the next character. Neither Clang @@ -2660,21 +2697,21 @@ FMT_CONSTEXPR FMT_INLINE void parse_format_string( return; } struct writer { - FMT_CONSTEXPR void operator()(const Char* pbegin, const Char* pend) { - if (pbegin == pend) return; + FMT_CONSTEXPR void operator()(const Char* from, const Char* to) { + if (from == to) return; for (;;) { const Char* p = nullptr; - if (!find(pbegin, pend, Char('}'), p)) - return handler_.on_text(pbegin, pend); + if (!find(from, to, Char('}'), p)) + return handler_.on_text(from, to); ++p; - if (p == pend || *p != '}') + if (p == to || *p != '}') return handler_.on_error("unmatched '}' in format string"); - handler_.on_text(pbegin, p); - pbegin = p + 1; + handler_.on_text(from, p); + from = p + 1; } } Handler& handler_; - } write{handler}; + } write = {handler}; while (begin != end) { // Doing two passes with memchr (one for '{' and another for '}') is up to // 2.5x faster than the naive one-pass implementation on big format strings. @@ -2689,7 +2726,6 @@ FMT_CONSTEXPR FMT_INLINE void parse_format_string( template ::value> struct strip_named_arg { using type = T; }; - template struct strip_named_arg { using type = remove_cvref_t; }; @@ -2710,36 +2746,6 @@ FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) return f.parse(ctx); } -// A parse context with extra argument id checks. It is only used at compile -// time because adding checks at runtime would introduce substantial overhead -// and would be redundant since argument ids are checked when arguments are -// retrieved anyway. -template -class compile_parse_context - : public basic_format_parse_context { - private: - int num_args_; - using base = basic_format_parse_context; - - public: - explicit FMT_CONSTEXPR compile_parse_context( - basic_string_view format_str, - int num_args = (std::numeric_limits::max)(), ErrorHandler eh = {}) - : base(format_str, eh), num_args_(num_args) {} - - FMT_CONSTEXPR auto next_arg_id() -> int { - int id = base::next_arg_id(); - if (id >= num_args_) this->on_error("argument not found"); - return id; - } - - FMT_CONSTEXPR void check_arg_id(int id) { - base::check_arg_id(id); - if (id >= num_args_) this->on_error("argument not found"); - } - using base::check_arg_id; -}; - template FMT_CONSTEXPR void check_int_type_spec(presentation_type type, ErrorHandler&& eh) { @@ -2827,7 +2833,8 @@ FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs& specs, template FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type, ErrorHandler&& eh = {}) -> bool { - if (type == presentation_type::none || type == presentation_type::string) + if (type == presentation_type::none || type == presentation_type::string || + type == presentation_type::debug) return true; if (type != presentation_type::pointer) eh.on_error("invalid type specifier"); return false; @@ -2901,7 +2908,7 @@ template class specs_checker : public Handler { constexpr int invalid_arg_index = -1; -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS template constexpr auto get_arg_index_by_name(basic_string_view name) -> int { if constexpr (detail::is_statically_named_arg()) { @@ -2916,7 +2923,7 @@ constexpr auto get_arg_index_by_name(basic_string_view name) -> int { template FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS if constexpr (sizeof...(Args) > 0) return get_arg_index_by_name<0, Args...>(name); #endif @@ -2927,20 +2934,28 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { template class format_string_checker { private: + // In the future basic_format_parse_context will replace compile_parse_context + // here and will use is_constant_evaluated and downcasting to access the data + // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1. using parse_context_type = compile_parse_context; - enum { num_args = sizeof...(Args) }; + static constexpr int num_args = sizeof...(Args); // Format specifier parsing function. using parse_func = const Char* (*)(parse_context_type&); parse_context_type context_; - parse_func parse_funcs_[num_args > 0 ? num_args : 1]; + parse_func parse_funcs_[num_args > 0 ? static_cast(num_args) : 1]; + type types_[num_args > 0 ? static_cast(num_args) : 1]; public: explicit FMT_CONSTEXPR format_string_checker( basic_string_view format_str, ErrorHandler eh) - : context_(format_str, num_args, eh), - parse_funcs_{&parse_format_specs...} {} + : context_(format_str, num_args, types_, eh), + parse_funcs_{&parse_format_specs...}, + types_{ + mapped_type_constant>::value...} { + } FMT_CONSTEXPR void on_text(const Char*, const Char*) {} @@ -2949,7 +2964,7 @@ class format_string_checker { return context_.check_arg_id(id), id; } FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS auto index = get_arg_index_by_name(id); if (index == invalid_arg_index) on_error("named argument is not found"); return context_.check_arg_id(index), index; @@ -2974,10 +2989,19 @@ class format_string_checker { } }; +// Reports a compile-time error if S is not a valid format string. +template ::value)> +FMT_INLINE void check_format_string(const S&) { +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert(is_compile_string::value, + "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " + "FMT_STRING."); +#endif +} template ::value), int>> + FMT_ENABLE_IF(is_compile_string::value)> void check_format_string(S format_str) { - FMT_CONSTEXPR auto s = to_string_view(format_str); + FMT_CONSTEXPR auto s = basic_string_view(format_str); using checker = format_string_checker...>; FMT_CONSTEXPR bool invalid_format = @@ -3075,6 +3099,15 @@ struct formatter::value, + enable_if_t<(U == detail::type::string_type || + U == detail::type::cstring_type || + U == detail::type::char_type), + int> = 0> + FMT_CONSTEXPR void set_debug_format() { + specs_.type = presentation_type::debug; + } + template FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const -> decltype(ctx.out()); @@ -3136,10 +3169,8 @@ template class basic_format_string { #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 // Workaround broken conversion on older gcc. -template using format_string = string_view; -template auto runtime(const S& s) -> basic_string_view> { - return s; -} +template using format_string = string_view; +inline auto runtime(string_view s) -> string_view { return s; } #else template using format_string = basic_format_string...>; @@ -3153,9 +3184,7 @@ using format_string = basic_format_string...>; fmt::print(fmt::runtime("{:d}"), "I am not a number"); \endrst */ -template auto runtime(const S& s) -> basic_runtime> { - return {{s}}; -} +inline auto runtime(string_view s) -> basic_runtime { return {{s}}; } #endif FMT_API auto vformat(string_view fmt, format_args args) -> std::string; diff --git a/Externals/fmt/include/fmt/format-inl.h b/Externals/fmt/include/fmt/format-inl.h index feb7815d65..22b1ec8df0 100755 --- a/Externals/fmt/include/fmt/format-inl.h +++ b/Externals/fmt/include/fmt/format-inl.h @@ -80,7 +80,8 @@ FMT_FUNC void report_error(format_func func, int error_code, inline void fwrite_fully(const void* ptr, size_t size, size_t count, FILE* stream) { size_t written = std::fwrite(ptr, size, count, stream); - if (written < count) FMT_THROW(system_error(errno, "cannot write to file")); + if (written < count) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); } #ifndef FMT_STATIC_THOUSANDS_SEPARATOR @@ -116,7 +117,7 @@ template FMT_FUNC Char decimal_point_impl(locale_ref) { #endif } // namespace detail -#if !FMT_MSC_VER +#if !FMT_MSC_VERSION FMT_API FMT_FUNC format_error::~format_error() noexcept = default; #endif @@ -128,640 +129,10 @@ FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str, namespace detail { -template struct basic_impl_data { - // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. - // These are generated by support/compute-powers.py. - static constexpr uint64_t pow10_significands[87] = { - 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, - 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, - 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, - 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, - 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, - 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, - 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, - 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, - 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, - 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, - 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, - 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, - 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, - 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, - 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, - 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, - 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, - 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, - 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, - 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, - 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, - 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, - 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, - 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, - 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, - 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, - 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, - 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, - 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, - }; - -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wnarrowing" -#endif - // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding - // to significands above. - static constexpr int16_t pow10_exponents[87] = { - -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, - -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, - -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, - -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, - -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, - 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, - 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, - 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 -# pragma GCC diagnostic pop -#endif - - static constexpr uint64_t power_of_10_64[20] = { - 1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL), - 10000000000000000000ULL}; -}; - -// This is a struct rather than an alias to avoid shadowing warnings in gcc. -struct impl_data : basic_impl_data<> {}; - -#if __cplusplus < 201703L -template -constexpr uint64_t basic_impl_data::pow10_significands[]; -template constexpr int16_t basic_impl_data::pow10_exponents[]; -template constexpr uint64_t basic_impl_data::power_of_10_64[]; -#endif - -template struct bits { - static FMT_CONSTEXPR_DECL const int value = - static_cast(sizeof(T) * std::numeric_limits::digits); -}; - -// A floating-point number f * pow(2, e) where F is an unsigned type. -template struct basic_fp { - F f; - int e; - - static constexpr const int num_significand_bits = bits::value; - - constexpr basic_fp() : f(0), e(0) {} - constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} - - // Constructs fp from an IEEE754 floating-point number. - template FMT_CONSTEXPR basic_fp(Float n) { assign(n); } - - // Assigns n to this and return true iff predecessor is closer than successor. - template FMT_CONSTEXPR auto assign(Float n) -> bool { - static_assert((std::numeric_limits::is_iec559 && - std::numeric_limits::digits <= 113) || - is_float128::value, - "unsupported FP"); - // Assume Float is in the format [sign][exponent][significand]. - using carrier_uint = typename dragonbox::float_info::carrier_uint; - const auto num_float_significand_bits = - detail::num_significand_bits(); - const auto implicit_bit = carrier_uint(1) << num_float_significand_bits; - const auto significand_mask = implicit_bit - 1; - auto u = bit_cast(n); - f = static_cast(u & significand_mask); - auto biased_e = static_cast((u & exponent_mask()) >> - num_float_significand_bits); - // The predecessor is closer if n is a normalized power of 2 (f == 0) other - // than the smallest normalized number (biased_e > 1). - auto is_predecessor_closer = f == 0 && biased_e > 1; - if (biased_e == 0) - biased_e = 1; // Subnormals use biased exponent 1 (min exponent). - else if (has_implicit_bit()) - f += static_cast(implicit_bit); - e = biased_e - exponent_bias() - num_float_significand_bits; - if (!has_implicit_bit()) ++e; - return is_predecessor_closer; - } -}; - -using fp = basic_fp; - -// Normalizes the value converted from double and multiplied by (1 << SHIFT). -template -FMT_CONSTEXPR basic_fp normalize(basic_fp value) { - // Handle subnormals. - const auto implicit_bit = F(1) << num_significand_bits(); - const auto shifted_implicit_bit = implicit_bit << SHIFT; - while ((value.f & shifted_implicit_bit) == 0) { - value.f <<= 1; - --value.e; - } - // Subtract 1 to account for hidden bit. - const auto offset = - fp::num_significand_bits - num_significand_bits() - SHIFT - 1; - value.f <<= offset; - value.e -= offset; - return value; -} - template inline bool operator==(basic_fp x, basic_fp y) { return x.f == y.f && x.e == y.e; } -// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. -FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { -#if FMT_USE_INT128 - auto product = static_cast<__uint128_t>(lhs) * rhs; - auto f = static_cast(product >> 64); - return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; -#else - // Multiply 32-bit parts of significands. - uint64_t mask = (1ULL << 32) - 1; - uint64_t a = lhs >> 32, b = lhs & mask; - uint64_t c = rhs >> 32, d = rhs & mask; - uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; - // Compute mid 64-bit of result and round. - uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); - return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); -#endif -} - -FMT_CONSTEXPR inline fp operator*(fp x, fp y) { - return {multiply(x.f, y.f), x.e + y.e + 64}; -} - -// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its -// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. -FMT_CONSTEXPR inline fp get_cached_power(int min_exponent, - int& pow10_exponent) { - const int shift = 32; - // log10(2) = 0x0.4d104d427de7fbcc... - const int64_t significand = 0x4d104d427de7fbcc; - int index = static_cast( - ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) + - ((int64_t(1) << shift) - 1)) // ceil - >> 32 // arithmetic shift - ); - // Decimal exponent of the first (smallest) cached power of 10. - const int first_dec_exp = -348; - // Difference between 2 consecutive decimal exponents in cached powers of 10. - const int dec_exp_step = 8; - index = (index - first_dec_exp - 1) / dec_exp_step + 1; - pow10_exponent = first_dec_exp + index * dec_exp_step; - return {impl_data::pow10_significands[index], - impl_data::pow10_exponents[index]}; -} - -class bigint { - private: - // A bigint is stored as an array of bigits (big digits), with bigit at index - // 0 being the least significant one. - using bigit = uint32_t; - using double_bigit = uint64_t; - enum { bigits_capacity = 32 }; - basic_memory_buffer bigits_; - int exp_; - - FMT_CONSTEXPR20 bigit operator[](int index) const { - return bigits_[to_unsigned(index)]; - } - FMT_CONSTEXPR20 bigit& operator[](int index) { - return bigits_[to_unsigned(index)]; - } - - static FMT_CONSTEXPR_DECL const int bigit_bits = bits::value; - - friend struct formatter; - - FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) { - auto result = static_cast((*this)[index]) - other - borrow; - (*this)[index] = static_cast(result); - borrow = static_cast(result >> (bigit_bits * 2 - 1)); - } - - FMT_CONSTEXPR20 void remove_leading_zeros() { - int num_bigits = static_cast(bigits_.size()) - 1; - while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; - bigits_.resize(to_unsigned(num_bigits + 1)); - } - - // Computes *this -= other assuming aligned bigints and *this >= other. - FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) { - FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); - FMT_ASSERT(compare(*this, other) >= 0, ""); - bigit borrow = 0; - int i = other.exp_ - exp_; - for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) - subtract_bigits(i, other.bigits_[j], borrow); - while (borrow > 0) subtract_bigits(i, 0, borrow); - remove_leading_zeros(); - } - - FMT_CONSTEXPR20 void multiply(uint32_t value) { - const double_bigit wide_value = value; - bigit carry = 0; - for (size_t i = 0, n = bigits_.size(); i < n; ++i) { - double_bigit result = bigits_[i] * wide_value + carry; - bigits_[i] = static_cast(result); - carry = static_cast(result >> bigit_bits); - } - if (carry != 0) bigits_.push_back(carry); - } - - template ::value || - std::is_same::value)> - FMT_CONSTEXPR20 void multiply(UInt value) { - using half_uint = - conditional_t::value, uint64_t, uint32_t>; - const int shift = num_bits() - bigit_bits; - const UInt lower = static_cast(value); - const UInt upper = value >> num_bits(); - UInt carry = 0; - for (size_t i = 0, n = bigits_.size(); i < n; ++i) { - UInt result = lower * bigits_[i] + static_cast(carry); - carry = (upper * bigits_[i] << shift) + (result >> bigit_bits) + - (carry >> bigit_bits); - bigits_[i] = static_cast(result); - } - while (carry != 0) { - bigits_.push_back(static_cast(carry)); - carry >>= bigit_bits; - } - } - - template ::value || - std::is_same::value)> - FMT_CONSTEXPR20 void assign(UInt n) { - size_t num_bigits = 0; - do { - bigits_[num_bigits++] = static_cast(n); - n >>= bigit_bits; - } while (n != 0); - bigits_.resize(num_bigits); - exp_ = 0; - } - - public: - FMT_CONSTEXPR20 bigint() : exp_(0) {} - explicit bigint(uint64_t n) { assign(n); } - - bigint(const bigint&) = delete; - void operator=(const bigint&) = delete; - - FMT_CONSTEXPR20 void assign(const bigint& other) { - auto size = other.bigits_.size(); - bigits_.resize(size); - auto data = other.bigits_.data(); - std::copy(data, data + size, make_checked(bigits_.data(), size)); - exp_ = other.exp_; - } - - template FMT_CONSTEXPR20 void operator=(Int n) { - FMT_ASSERT(n > 0, ""); - assign(uint64_or_128_t(n)); - } - - FMT_CONSTEXPR20 int num_bigits() const { - return static_cast(bigits_.size()) + exp_; - } - - FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) { - FMT_ASSERT(shift >= 0, ""); - exp_ += shift / bigit_bits; - shift %= bigit_bits; - if (shift == 0) return *this; - bigit carry = 0; - for (size_t i = 0, n = bigits_.size(); i < n; ++i) { - bigit c = bigits_[i] >> (bigit_bits - shift); - bigits_[i] = (bigits_[i] << shift) + carry; - carry = c; - } - if (carry != 0) bigits_.push_back(carry); - return *this; - } - - template FMT_CONSTEXPR20 bigint& operator*=(Int value) { - FMT_ASSERT(value > 0, ""); - multiply(uint32_or_64_or_128_t(value)); - return *this; - } - - friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) { - int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); - if (num_lhs_bigits != num_rhs_bigits) - return num_lhs_bigits > num_rhs_bigits ? 1 : -1; - int i = static_cast(lhs.bigits_.size()) - 1; - int j = static_cast(rhs.bigits_.size()) - 1; - int end = i - j; - if (end < 0) end = 0; - for (; i >= end; --i, --j) { - bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; - if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; - } - if (i != j) return i > j ? 1 : -1; - return 0; - } - - // Returns compare(lhs1 + lhs2, rhs). - friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2, - const bigint& rhs) { - int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits()); - int num_rhs_bigits = rhs.num_bigits(); - if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; - if (max_lhs_bigits > num_rhs_bigits) return 1; - auto get_bigit = [](const bigint& n, int i) -> bigit { - return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; - }; - double_bigit borrow = 0; - int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_); - for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { - double_bigit sum = - static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); - bigit rhs_bigit = get_bigit(rhs, i); - if (sum > rhs_bigit + borrow) return 1; - borrow = rhs_bigit + borrow - sum; - if (borrow > 1) return -1; - borrow <<= bigit_bits; - } - return borrow != 0 ? -1 : 0; - } - - // Assigns pow(10, exp) to this bigint. - FMT_CONSTEXPR20 void assign_pow10(int exp) { - FMT_ASSERT(exp >= 0, ""); - if (exp == 0) return *this = 1; - // Find the top bit. - int bitmask = 1; - while (exp >= bitmask) bitmask <<= 1; - bitmask >>= 1; - // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by - // repeated squaring and multiplication. - *this = 5; - bitmask >>= 1; - while (bitmask != 0) { - square(); - if ((exp & bitmask) != 0) *this *= 5; - bitmask >>= 1; - } - *this <<= exp; // Multiply by pow(2, exp) by shifting. - } - - FMT_CONSTEXPR20 void square() { - int num_bigits = static_cast(bigits_.size()); - int num_result_bigits = 2 * num_bigits; - basic_memory_buffer n(std::move(bigits_)); - bigits_.resize(to_unsigned(num_result_bigits)); - auto sum = uint128_t(); - for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) { - // Compute bigit at position bigit_index of the result by adding - // cross-product terms n[i] * n[j] such that i + j == bigit_index. - for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { - // Most terms are multiplied twice which can be optimized in the future. - sum += static_cast(n[i]) * n[j]; - } - (*this)[bigit_index] = static_cast(sum); - sum >>= bits::value; // Compute the carry. - } - // Do the same for the top half. - for (int bigit_index = num_bigits; bigit_index < num_result_bigits; - ++bigit_index) { - for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) - sum += static_cast(n[i++]) * n[j--]; - (*this)[bigit_index] = static_cast(sum); - sum >>= bits::value; - } - remove_leading_zeros(); - exp_ *= 2; - } - - // If this bigint has a bigger exponent than other, adds trailing zero to make - // exponents equal. This simplifies some operations such as subtraction. - FMT_CONSTEXPR20 void align(const bigint& other) { - int exp_difference = exp_ - other.exp_; - if (exp_difference <= 0) return; - int num_bigits = static_cast(bigits_.size()); - bigits_.resize(to_unsigned(num_bigits + exp_difference)); - for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) - bigits_[j] = bigits_[i]; - std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); - exp_ -= exp_difference; - } - - // Divides this bignum by divisor, assigning the remainder to this and - // returning the quotient. - FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) { - FMT_ASSERT(this != &divisor, ""); - if (compare(*this, divisor) < 0) return 0; - FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); - align(divisor); - int quotient = 0; - do { - subtract_aligned(divisor); - ++quotient; - } while (compare(*this, divisor) >= 0); - return quotient; - } -}; - -enum class round_direction { unknown, up, down }; - -// Given the divisor (normally a power of 10), the remainder = v % divisor for -// some number v and the error, returns whether v should be rounded up, down, or -// whether the rounding direction can't be determined due to error. -// error should be less than divisor / 2. -FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor, - uint64_t remainder, - uint64_t error) { - FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. - FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. - FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. - // Round down if (remainder + error) * 2 <= divisor. - if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) - return round_direction::down; - // Round up if (remainder - error) * 2 >= divisor. - if (remainder >= error && - remainder - error >= divisor - (remainder - error)) { - return round_direction::up; - } - return round_direction::unknown; -} - -namespace digits { -enum result { - more, // Generate more digits. - done, // Done generating digits. - error // Digit generation cancelled due to an error. -}; -} - -struct gen_digits_handler { - char* buf; - int size; - int precision; - int exp10; - bool fixed; - - FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor, - uint64_t remainder, uint64_t error, - bool integral) { - FMT_ASSERT(remainder < divisor, ""); - buf[size++] = digit; - if (!integral && error >= remainder) return digits::error; - if (size < precision) return digits::more; - if (!integral) { - // Check if error * 2 < divisor with overflow prevention. - // The check is not needed for the integral part because error = 1 - // and divisor > (1 << 32) there. - if (error >= divisor || error >= divisor - error) return digits::error; - } else { - FMT_ASSERT(error == 1 && divisor > 2, ""); - } - auto dir = get_round_direction(divisor, remainder, error); - if (dir != round_direction::up) - return dir == round_direction::down ? digits::done : digits::error; - ++buf[size - 1]; - for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { - buf[i] = '0'; - ++buf[i - 1]; - } - if (buf[0] > '9') { - buf[0] = '1'; - if (fixed) - buf[size++] = '0'; - else - ++exp10; - } - return digits::done; - } -}; - -inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) { - // Adjust fixed precision by exponent because it is relative to decimal - // point. - if (exp10 > 0 && precision > max_value() - exp10) - FMT_THROW(format_error("number is too big")); - precision += exp10; -} - -// Generates output using the Grisu digit-gen algorithm. -// error: the size of the region (lower, upper) outside of which numbers -// definitely do not round to value (Delta in Grisu3). -FMT_INLINE FMT_CONSTEXPR20 digits::result grisu_gen_digits( - fp value, uint64_t error, int& exp, gen_digits_handler& handler) { - const fp one(1ULL << -value.e, value.e); - // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be - // zero because it contains a product of two 64-bit numbers with MSB set (due - // to normalization) - 1, shifted right by at most 60 bits. - auto integral = static_cast(value.f >> -one.e); - FMT_ASSERT(integral != 0, ""); - FMT_ASSERT(integral == value.f >> -one.e, ""); - // The fractional part of scaled value (p2 in Grisu) c = value % one. - uint64_t fractional = value.f & (one.f - 1); - exp = count_digits(integral); // kappa in Grisu. - // Non-fixed formats require at least one digit and no precision adjustment. - if (handler.fixed) { - adjust_precision(handler.precision, exp + handler.exp10); - // Check if precision is satisfied just by leading zeros, e.g. - // format("{:.2f}", 0.001) gives "0.00" without generating any digits. - if (handler.precision <= 0) { - if (handler.precision < 0) return digits::done; - // Divide by 10 to prevent overflow. - uint64_t divisor = impl_data::power_of_10_64[exp - 1] << -one.e; - auto dir = get_round_direction(divisor, value.f / 10, error * 10); - if (dir == round_direction::unknown) return digits::error; - handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0'; - return digits::done; - } - } - // Generate digits for the integral part. This can produce up to 10 digits. - do { - uint32_t digit = 0; - auto divmod_integral = [&](uint32_t divisor) { - digit = integral / divisor; - integral %= divisor; - }; - // This optimization by Milo Yip reduces the number of integer divisions by - // one per iteration. - switch (exp) { - case 10: - divmod_integral(1000000000); - break; - case 9: - divmod_integral(100000000); - break; - case 8: - divmod_integral(10000000); - break; - case 7: - divmod_integral(1000000); - break; - case 6: - divmod_integral(100000); - break; - case 5: - divmod_integral(10000); - break; - case 4: - divmod_integral(1000); - break; - case 3: - divmod_integral(100); - break; - case 2: - divmod_integral(10); - break; - case 1: - digit = integral; - integral = 0; - break; - default: - FMT_ASSERT(false, "invalid number of digits"); - } - --exp; - auto remainder = (static_cast(integral) << -one.e) + fractional; - auto result = handler.on_digit(static_cast('0' + digit), - impl_data::power_of_10_64[exp] << -one.e, - remainder, error, true); - if (result != digits::more) return result; - } while (exp > 0); - // Generate digits for the fractional part. - for (;;) { - fractional *= 10; - error *= 10; - char digit = static_cast('0' + (fractional >> -one.e)); - fractional &= one.f - 1; - --exp; - auto result = handler.on_digit(digit, one.f, fractional, error, false); - if (result != digits::more) return result; - } -} - -inline FMT_CONSTEXPR20 uint128_fallback& uint128_fallback::operator+=( - uint64_t n) noexcept { - if (is_constant_evaluated()) { - lo_ += n; - hi_ += (lo_ < n ? 1 : 0); - return *this; - } -#if FMT_HAS_BUILTIN(__builtin_addcll) - unsigned long long carry; - lo_ = __builtin_addcll(lo_, n, 0, &carry); - hi_ += carry; -#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) - unsigned long long result; - auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result); - lo_ = result; - hi_ += carry; -#elif defined(_MSC_VER) && defined(_M_X64) - auto carry = _addcarry_u64(0, lo_, n, &lo_); - _addcarry_u64(carry, hi_, 0, &hi_); -#else - lo_ += n; - hi_ += (lo_ < n ? 1 : 0); -#endif - return *this; -} - // Compilers should be able to optimize this into the ror instruction. FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept { r &= 31; @@ -1966,7 +1337,7 @@ template decimal_fp to_decimal(T x) noexcept { if (r < deltai) { // Exclude the right endpoint if necessary. - if (r == 0 && z_mul.is_integer && !include_right_endpoint) { + if (r == 0 && (z_mul.is_integer & !include_right_endpoint)) { --ret_value.significand; r = float_info::big_divisor; goto small_divisor_case_label; @@ -1975,26 +1346,11 @@ template decimal_fp to_decimal(T x) noexcept { goto small_divisor_case_label; } else { // r == deltai; compare fractional parts. - const carrier_uint two_fl = two_fc - 1; + const typename cache_accessor::compute_mul_parity_result x_mul = + cache_accessor::compute_mul_parity(two_fc - 1, cache, beta); - if (!include_left_endpoint || - exponent < float_info::case_fc_pm_half_lower_threshold || - exponent > float_info::divisibility_check_by_5_threshold) { - // If the left endpoint is not included, the condition for - // success is z^(f) < delta^(f) (odd parity). - // Otherwise, the inequalities on exponent ensure that - // x is not an integer, so if z^(f) >= delta^(f) (even parity), we in fact - // have strict inequality. - if (!cache_accessor::compute_mul_parity(two_fl, cache, beta).parity) { - goto small_divisor_case_label; - } - } else { - const typename cache_accessor::compute_mul_parity_result x_mul = - cache_accessor::compute_mul_parity(two_fl, cache, beta); - if (!x_mul.parity && !x_mul.is_integer) { - goto small_divisor_case_label; - } - } + if (!(x_mul.parity | (x_mul.is_integer & include_left_endpoint))) + goto small_divisor_case_label; } ret_value.exponent = minus_k + float_info::kappa + 1; @@ -2033,149 +1389,12 @@ small_divisor_case_label: // or equivalently, when y is an integer. if (y_mul.parity != approx_y_parity) --ret_value.significand; - else if (y_mul.is_integer && ret_value.significand % 2 != 0) + else if (y_mul.is_integer & (ret_value.significand % 2 != 0)) --ret_value.significand; return ret_value; } } // namespace dragonbox -// format_dragon flags. -enum dragon { - predecessor_closer = 1, - fixup = 2, // Run fixup to correct exp10 which can be off by one. - fixed = 4, -}; - -// Formats a floating-point number using a variation of the Fixed-Precision -// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White: -// https://fmt.dev/papers/p372-steele.pdf. -FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, - unsigned flags, int num_digits, - buffer& buf, int& exp10) { - bigint numerator; // 2 * R in (FPP)^2. - bigint denominator; // 2 * S in (FPP)^2. - // lower and upper are differences between value and corresponding boundaries. - bigint lower; // (M^- in (FPP)^2). - bigint upper_store; // upper's value if different from lower. - bigint* upper = nullptr; // (M^+ in (FPP)^2). - // Shift numerator and denominator by an extra bit or two (if lower boundary - // is closer) to make lower and upper integers. This eliminates multiplication - // by 2 during later computations. - bool is_predecessor_closer = (flags & dragon::predecessor_closer) != 0; - int shift = is_predecessor_closer ? 2 : 1; - if (value.e >= 0) { - numerator = value.f; - numerator <<= value.e + shift; - lower = 1; - lower <<= value.e; - if (is_predecessor_closer) { - upper_store = 1; - upper_store <<= value.e + 1; - upper = &upper_store; - } - denominator.assign_pow10(exp10); - denominator <<= shift; - } else if (exp10 < 0) { - numerator.assign_pow10(-exp10); - lower.assign(numerator); - if (is_predecessor_closer) { - upper_store.assign(numerator); - upper_store <<= 1; - upper = &upper_store; - } - numerator *= value.f; - numerator <<= shift; - denominator = 1; - denominator <<= shift - value.e; - } else { - numerator = value.f; - numerator <<= shift; - denominator.assign_pow10(exp10); - denominator <<= shift - value.e; - lower = 1; - if (is_predecessor_closer) { - upper_store = 1ULL << 1; - upper = &upper_store; - } - } - bool even = (value.f & 1) == 0; - if (!upper) upper = &lower; - if ((flags & dragon::fixup) != 0) { - if (add_compare(numerator, *upper, denominator) + even <= 0) { - --exp10; - numerator *= 10; - if (num_digits < 0) { - lower *= 10; - if (upper != &lower) *upper *= 10; - } - } - if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1); - } - // Invariant: value == (numerator / denominator) * pow(10, exp10). - if (num_digits < 0) { - // Generate the shortest representation. - num_digits = 0; - char* data = buf.data(); - for (;;) { - int digit = numerator.divmod_assign(denominator); - bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. - // numerator + upper >[=] pow10: - bool high = add_compare(numerator, *upper, denominator) + even > 0; - data[num_digits++] = static_cast('0' + digit); - if (low || high) { - if (!low) { - ++data[num_digits - 1]; - } else if (high) { - int result = add_compare(numerator, numerator, denominator); - // Round half to even. - if (result > 0 || (result == 0 && (digit % 2) != 0)) - ++data[num_digits - 1]; - } - buf.try_resize(to_unsigned(num_digits)); - exp10 -= num_digits - 1; - return; - } - numerator *= 10; - lower *= 10; - if (upper != &lower) *upper *= 10; - } - } - // Generate the given number of digits. - exp10 -= num_digits - 1; - if (num_digits == 0) { - denominator *= 10; - auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; - buf.push_back(digit); - return; - } - buf.try_resize(to_unsigned(num_digits)); - for (int i = 0; i < num_digits - 1; ++i) { - int digit = numerator.divmod_assign(denominator); - buf[i] = static_cast('0' + digit); - numerator *= 10; - } - int digit = numerator.divmod_assign(denominator); - auto result = add_compare(numerator, numerator, denominator); - if (result > 0 || (result == 0 && (digit % 2) != 0)) { - if (digit == 9) { - const auto overflow = '0' + 10; - buf[num_digits - 1] = overflow; - // Propagate the carry. - for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) { - buf[i] = '0'; - ++buf[i - 1]; - } - if (buf[0] == overflow) { - buf[0] = '1'; - ++exp10; - } - return; - } - ++digit; - } - buf[num_digits - 1] = static_cast('0' + digit); -} - #ifdef _MSC_VER FMT_FUNC auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...) -> int { @@ -2186,95 +1405,6 @@ FMT_FUNC auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...) return result; } #endif - -template -FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, - float_specs specs, - buffer& buf) { - // float is passed as double to reduce the number of instantiations. - static_assert(!std::is_same::value, ""); - FMT_ASSERT(value >= 0, "value is negative"); - auto converted_value = convert_float(value); - - const bool fixed = specs.format == float_format::fixed; - if (value <= 0) { // <= instead of == to silence a warning. - if (precision <= 0 || !fixed) { - buf.push_back('0'); - return 0; - } - buf.try_resize(to_unsigned(precision)); - fill_n(buf.data(), precision, '0'); - return -precision; - } - - int exp = 0; - bool use_dragon = true; - unsigned dragon_flags = 0; - if (!is_fast_float()) { - const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10) - using info = dragonbox::float_info; - const auto f = basic_fp(converted_value); - // Compute exp, an approximate power of 10, such that - // 10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1). - // This is based on log10(value) == log2(value) / log2(10) and approximation - // of log2(value) by e + num_fraction_bits idea from double-conversion. - exp = static_cast( - std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10)); - dragon_flags = dragon::fixup; - } else if (!is_constant_evaluated() && precision < 0) { - // Use Dragonbox for the shortest format. - if (specs.binary32) { - auto dec = dragonbox::to_decimal(static_cast(value)); - write(buffer_appender(buf), dec.significand); - return dec.exponent; - } - auto dec = dragonbox::to_decimal(static_cast(value)); - write(buffer_appender(buf), dec.significand); - return dec.exponent; - } else { - // Use Grisu + Dragon4 for the given precision: - // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. - const int min_exp = -60; // alpha in Grisu. - int cached_exp10 = 0; // K in Grisu. - fp normalized = normalize(fp(converted_value)); - const auto cached_pow = get_cached_power( - min_exp - (normalized.e + fp::num_significand_bits), cached_exp10); - normalized = normalized * cached_pow; - gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; - if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error && - !is_constant_evaluated()) { - exp += handler.exp10; - buf.try_resize(to_unsigned(handler.size)); - use_dragon = false; - } else { - exp += handler.size - cached_exp10 - 1; - precision = handler.precision; - } - } - if (use_dragon) { - auto f = basic_fp(); - bool is_predecessor_closer = specs.binary32 - ? f.assign(static_cast(value)) - : f.assign(converted_value); - if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer; - if (fixed) dragon_flags |= dragon::fixed; - // Limit precision to the maximum possible number of significant digits in - // an IEEE754 double because we don't need to generate zeros. - const int max_double_digits = 767; - if (precision > max_double_digits) precision = max_double_digits; - format_dragon(f, dragon_flags, precision, buf, exp); - } - if (!fixed && !specs.showpoint) { - // Remove trailing zeros. - auto num_digits = buf.size(); - while (num_digits > 0 && buf[num_digits - 1] == '0') { - --num_digits; - ++exp; - } - buf.try_resize(num_digits); - } - return exp; -} } // namespace detail template <> struct formatter { @@ -2335,12 +1465,6 @@ FMT_FUNC void report_system_error(int error_code, report_error(format_system_error, error_code, message); } -// DEPRECATED! -// This function is defined here and not inline for ABI compatiblity. -FMT_FUNC void detail::error_handler::on_error(const char* message) { - throw_format_error(message); -} - FMT_FUNC std::string vformat(string_view fmt, format_args args) { // Don't optimize the "{}" case to keep the binary size small and because it // can be better optimized in fmt::format anyway. @@ -2349,17 +1473,13 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) { return to_string(buffer); } -#ifdef _WIN32 namespace detail { +#ifdef _WIN32 using dword = conditional_t; extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // void*, const void*, dword, dword*, void*); -} // namespace detail -#endif -namespace detail { -FMT_FUNC void print(std::FILE* f, string_view text) { -#ifdef _WIN32 +FMT_FUNC bool write_console(std::FILE* f, string_view text) { auto fd = _fileno(f); if (_isatty(fd)) { detail::utf8_to_utf16 u16(string_view(text.data(), text.size())); @@ -2367,11 +1487,20 @@ FMT_FUNC void print(std::FILE* f, string_view text) { if (detail::WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), u16.c_str(), static_cast(u16.size()), &written, nullptr)) { - return; + return true; } - // Fallback to fwrite on failure. It can happen if the output has been - // redirected to NUL. } + // We return false if the file descriptor was not TTY, or it was but + // SetConsoleW failed which can happen if the output has been redirected to + // NUL. In both cases when we return false, we should attempt to do regular + // write via fwrite or std::ostream::write. + return false; +} +#endif + +FMT_FUNC void print(std::FILE* f, string_view text) { +#ifdef _WIN32 + if (write_console(f, text)) return; #endif detail::fwrite_fully(text.data(), 1, text.size(), f); } diff --git a/Externals/fmt/include/fmt/format.h b/Externals/fmt/include/fmt/format.h index d65f14a189..7c607dbd30 100755 --- a/Externals/fmt/include/fmt/format.h +++ b/Externals/fmt/include/fmt/format.h @@ -71,7 +71,7 @@ # define FMT_NOINLINE #endif -#if FMT_MSC_VER +#if FMT_MSC_VERSION # define FMT_MSC_DEFAULT = default #else # define FMT_MSC_DEFAULT @@ -79,7 +79,7 @@ #ifndef FMT_THROW # if FMT_EXCEPTIONS -# if FMT_MSC_VER || FMT_NVCC +# if FMT_MSC_VERSION || defined(__NVCC__) FMT_BEGIN_NAMESPACE namespace detail { template inline void do_throw(const Exception& x) { @@ -118,17 +118,10 @@ FMT_END_NAMESPACE # endif #endif -// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers. -#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC -# define FMT_DEPRECATED_ALIAS -#else -# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED -#endif - #ifndef FMT_USE_USER_DEFINED_LITERALS // EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. # if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ - FMT_MSC_VER >= 1900) && \ + FMT_MSC_VERSION >= 1900) && \ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) # define FMT_USE_USER_DEFINED_LITERALS 1 # else @@ -146,7 +139,7 @@ FMT_END_NAMESPACE // __builtin_clz is broken in clang with Microsoft CodeGen: // https://github.com/fmtlib/fmt/issues/519. -#if !FMT_MSC_VER +#if !FMT_MSC_VERSION # if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION # define FMT_BUILTIN_CLZ(n) __builtin_clz(n) # endif @@ -159,23 +152,24 @@ FMT_END_NAMESPACE // https://github.com/fmtlib/fmt/issues/2510. #ifndef __ICL # if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \ - FMT_NVCOMPILER_VERSION + defined(__NVCOMPILER) # define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) # endif # if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \ - FMT_ICC_VERSION || FMT_NVCOMPILER_VERSION + FMT_ICC_VERSION || defined(__NVCOMPILER) # define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) # endif #endif -#if FMT_MSC_VER +#if FMT_MSC_VERSION # include // _BitScanReverse[64], _BitScanForward[64], _umul128 #endif // Some compilers masquerade as both MSVC and GCC-likes or otherwise support // __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the // MSVC intrinsics if the clz and clzll builtins are not available. -#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(FMT_BUILTIN_CTZLL) +#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) && \ + !defined(FMT_BUILTIN_CTZLL) FMT_BEGIN_NAMESPACE namespace detail { // Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. @@ -245,12 +239,6 @@ inline auto ctzll(uint64_t x) -> int { FMT_END_NAMESPACE #endif -#ifdef FMT_HEADER_ONLY -# define FMT_HEADER_ONLY_CONSTEXPR20 FMT_CONSTEXPR20 -#else -# define FMT_HEADER_ONLY_CONSTEXPR20 -#endif - FMT_BEGIN_NAMESPACE namespace detail { @@ -261,6 +249,18 @@ FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { #endif } +template struct string_literal { + static constexpr CharT value[sizeof...(C)] = {C...}; + constexpr operator basic_string_view() const { + return {value, sizeof...(C)}; + } +}; + +#if FMT_CPLUSPLUS < 201703L +template +constexpr CharT string_literal::value[sizeof...(C)]; +#endif + template class formatbuf : public Streambuf { private: using char_type = typename Streambuf::char_type; @@ -299,7 +299,8 @@ FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { if (is_constant_evaluated()) return std::bit_cast(from); #endif auto to = To(); - std::memcpy(&to, &from, sizeof(to)); + // The cast suppresses a bogus -Wclass-memaccess on GCC. + std::memcpy(static_cast(&to), &from, sizeof(to)); return to; } @@ -378,10 +379,12 @@ class uint128_fallback { } FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback { if (shift == 64) return {0, hi_}; + if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64); return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)}; } FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback { if (shift == 64) return {lo_, 0}; + if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64); return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)}; } FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& { @@ -394,7 +397,31 @@ class uint128_fallback { lo_ = new_lo; hi_ = new_hi; } - FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept; + + FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept { + if (is_constant_evaluated()) { + lo_ += n; + hi_ += (lo_ < n ? 1 : 0); + return *this; + } +#if FMT_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__) + unsigned long long carry; + lo_ = __builtin_addcll(lo_, n, 0, &carry); + hi_ += carry; +#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__) + unsigned long long result; + auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result); + lo_ = result; + hi_ += carry; +#elif defined(_MSC_VER) && defined(_M_X64) + auto carry = _addcarry_u64(0, lo_, n, &lo_); + _addcarry_u64(carry, hi_, 0, &hi_); +#else + lo_ += n; + hi_ += (lo_ < n ? 1 : 0); +#endif + return *this; + } }; using uint128_t = conditional_t; @@ -580,19 +607,23 @@ FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) constexpr const int shiftc[] = {0, 18, 12, 6, 0}; constexpr const int shifte[] = {0, 6, 4, 2, 0}; - int len = code_point_length(s); - const char* next = s + len; + int len = code_point_length_impl(*s); + // Compute the pointer to the next character early so that the next + // iteration can start working on the next character. Neither Clang + // nor GCC figure out this reordering on their own. + const char* next = s + len + !len; + + using uchar = unsigned char; // Assume a four-byte character and load four bytes. Unused bits are // shifted out. - *c = uint32_t(s[0] & masks[len]) << 18; - *c |= uint32_t(s[1] & 0x3f) << 12; - *c |= uint32_t(s[2] & 0x3f) << 6; - *c |= uint32_t(s[3] & 0x3f) << 0; + *c = uint32_t(uchar(s[0]) & masks[len]) << 18; + *c |= uint32_t(uchar(s[1]) & 0x3f) << 12; + *c |= uint32_t(uchar(s[2]) & 0x3f) << 6; + *c |= uint32_t(uchar(s[3]) & 0x3f) << 0; *c >>= shiftc[len]; // Accumulate the various error conditions. - using uchar = unsigned char; *e = (*c < mins[len]) << 6; // non-canonical encoding *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? *e |= (*c > 0x10FFFF) << 8; // out of range? @@ -616,8 +647,8 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { auto error = 0; auto end = utf8_decode(buf_ptr, &cp, &error); bool result = f(error ? invalid_code_point : cp, - string_view(ptr, to_unsigned(end - buf_ptr))); - return result ? end : nullptr; + string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr))); + return result ? (error ? buf_ptr + 1 : end) : nullptr; }; auto p = s.data(); const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. @@ -680,8 +711,8 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) { } inline auto compute_width(basic_string_view s) -> size_t { - return compute_width(basic_string_view( - reinterpret_cast(s.data()), s.size())); + return compute_width( + string_view(reinterpret_cast(s.data()), s.size())); } template @@ -691,9 +722,8 @@ inline auto code_point_index(basic_string_view s, size_t n) -> size_t { } // Calculates the index of the nth code point in a UTF-8 string. -inline auto code_point_index(basic_string_view s, size_t n) - -> size_t { - const char8_type* data = s.data(); +inline auto code_point_index(string_view s, size_t n) -> size_t { + const char* data = s.data(); size_t num_code_points = 0; for (size_t i = 0, size = s.size(); i != size; ++i) { if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i; @@ -701,6 +731,12 @@ inline auto code_point_index(basic_string_view s, size_t n) return s.size(); } +inline auto code_point_index(basic_string_view s, size_t n) + -> size_t { + return code_point_index( + string_view(reinterpret_cast(s.data()), s.size()), n); +} + #ifndef FMT_USE_FLOAT128 # ifdef __SIZEOF_FLOAT128__ # define FMT_USE_FLOAT128 1 @@ -724,6 +760,9 @@ struct is_fast_float : bool_constant::is_iec559 && sizeof(T) <= sizeof(double)> {}; template struct is_fast_float : std::false_type {}; +template +using is_double_double = bool_constant::digits == 106>; + #ifndef FMT_USE_FULL_CACHE_DRAGONBOX # define FMT_USE_FULL_CACHE_DRAGONBOX 0 #endif @@ -899,8 +938,11 @@ struct is_contiguous> : std::true_type { }; namespace detail { +#ifdef _WIN32 +FMT_API bool write_console(std::FILE* f, string_view text); +#endif FMT_API void print(std::FILE*, string_view); -} +} // namespace detail /** A formatting error such as invalid format string. */ FMT_CLASS_API @@ -916,29 +958,8 @@ class FMT_API format_error : public std::runtime_error { ~format_error() noexcept override FMT_MSC_DEFAULT; }; -/** - \rst - Constructs a `~fmt::format_arg_store` object that contains references - to arguments and can be implicitly converted to `~fmt::format_args`. - If ``fmt`` is a compile-time string then `make_args_checked` checks - its validity at compile time. - \endrst - */ -template > -FMT_DEPRECATED FMT_INLINE auto make_args_checked( - const S& fmt, const remove_reference_t&... args) - -> format_arg_store, remove_reference_t...> { - static_assert( - detail::count<( - std::is_base_of>::value && - std::is_reference::value)...>() == 0, - "passing views as lvalues is disallowed"); - detail::check_format_string(fmt); - return {args...}; -} - namespace detail_exported { -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS template struct fixed_string { constexpr fixed_string(const Char (&str)[N]) { detail::copy_str(static_cast(str), @@ -985,11 +1006,12 @@ constexpr auto is_negative(T) -> bool { return false; } -template constexpr auto is_supported_floating_point(T) -> bool { - return (std::is_same() && FMT_USE_FLOAT) || - (std::is_same() && FMT_USE_DOUBLE) || - (std::is_same() && FMT_USE_LONG_DOUBLE) || - is_float128(); +template +FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool { + if (std::is_same()) return FMT_USE_FLOAT; + if (std::is_same()) return FMT_USE_DOUBLE; + if (std::is_same()) return FMT_USE_LONG_DOUBLE; + return true; } // Smallest of uint32_t, uint64_t, uint128_t that is large enough to @@ -1213,7 +1235,7 @@ FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) template >::value)> -inline auto format_decimal(Iterator out, UInt value, int size) +FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size) -> format_decimal_result { // Buffer is large enough to hold all digits (digits10 + 1). Char buffer[digits10() + 1]; @@ -1274,8 +1296,6 @@ template <> struct float_info { static const int small_divisor = 10; static const int min_k = -31; static const int max_k = 46; - static const int divisibility_check_by_5_threshold = 39; - static const int case_fc_pm_half_lower_threshold = -1; static const int shorter_interval_tie_lower_threshold = -35; static const int shorter_interval_tie_upper_threshold = -35; }; @@ -1288,8 +1308,6 @@ template <> struct float_info { static const int small_divisor = 100; static const int min_k = -292; static const int max_k = 326; - static const int divisibility_check_by_5_threshold = 86; - static const int case_fc_pm_half_lower_threshold = -2; static const int shorter_interval_tie_lower_threshold = -77; static const int shorter_interval_tie_upper_threshold = -77; }; @@ -1305,8 +1323,7 @@ struct float_info::digits == 64 || // A double-double floating point number. template -struct float_info::digits >= 106 && - !std::numeric_limits::is_iec559>> { +struct float_info::value>> { using carrier_uint = detail::uint128_t; }; @@ -1337,8 +1354,8 @@ template constexpr int num_significand_bits() { template constexpr auto exponent_mask() -> typename dragonbox::float_info::carrier_uint { - using fmt_uint = typename dragonbox::float_info::carrier_uint; - return ((fmt_uint(1) << dragonbox::float_info::exponent_bits) - 1) + using uint = typename dragonbox::float_info::carrier_uint; + return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) << num_significand_bits(); } template constexpr auto exponent_bias() -> int { @@ -1369,10 +1386,186 @@ FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It { return it; } -template -FMT_HEADER_ONLY_CONSTEXPR20 auto format_float(T value, int precision, - float_specs specs, - buffer& buf) -> int; +// A floating-point number f * pow(2, e) where F is an unsigned type. +template struct basic_fp { + F f; + int e; + + static constexpr const int num_significand_bits = + static_cast(sizeof(F) * num_bits()); + + constexpr basic_fp() : f(0), e(0) {} + constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} + + // Constructs fp from an IEEE754 floating-point number. + template FMT_CONSTEXPR basic_fp(Float n) { assign(n); } + + // Assigns n to this and return true iff predecessor is closer than successor. + template ::value)> + FMT_CONSTEXPR auto assign(Float n) -> bool { + static_assert(std::numeric_limits::digits <= 113, "unsupported FP"); + // Assume Float is in the format [sign][exponent][significand]. + using carrier_uint = typename dragonbox::float_info::carrier_uint; + const auto num_float_significand_bits = + detail::num_significand_bits(); + const auto implicit_bit = carrier_uint(1) << num_float_significand_bits; + const auto significand_mask = implicit_bit - 1; + auto u = bit_cast(n); + f = static_cast(u & significand_mask); + auto biased_e = static_cast((u & exponent_mask()) >> + num_float_significand_bits); + // The predecessor is closer if n is a normalized power of 2 (f == 0) + // other than the smallest normalized number (biased_e > 1). + auto is_predecessor_closer = f == 0 && biased_e > 1; + if (biased_e == 0) + biased_e = 1; // Subnormals use biased exponent 1 (min exponent). + else if (has_implicit_bit()) + f += static_cast(implicit_bit); + e = biased_e - exponent_bias() - num_float_significand_bits; + if (!has_implicit_bit()) ++e; + return is_predecessor_closer; + } + + template ::value)> + FMT_CONSTEXPR auto assign(Float n) -> bool { + static_assert(std::numeric_limits::is_iec559, "unsupported FP"); + return assign(static_cast(n)); + } +}; + +using fp = basic_fp; + +// Normalizes the value converted from double and multiplied by (1 << SHIFT). +template +FMT_CONSTEXPR basic_fp normalize(basic_fp value) { + // Handle subnormals. + const auto implicit_bit = F(1) << num_significand_bits(); + const auto shifted_implicit_bit = implicit_bit << SHIFT; + while ((value.f & shifted_implicit_bit) == 0) { + value.f <<= 1; + --value.e; + } + // Subtract 1 to account for hidden bit. + const auto offset = basic_fp::num_significand_bits - + num_significand_bits() - SHIFT - 1; + value.f <<= offset; + value.e -= offset; + return value; +} + +// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. +FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { +#if FMT_USE_INT128 + auto product = static_cast<__uint128_t>(lhs) * rhs; + auto f = static_cast(product >> 64); + return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; +#else + // Multiply 32-bit parts of significands. + uint64_t mask = (1ULL << 32) - 1; + uint64_t a = lhs >> 32, b = lhs & mask; + uint64_t c = rhs >> 32, d = rhs & mask; + uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; + // Compute mid 64-bit of result and round. + uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); + return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); +#endif +} + +FMT_CONSTEXPR inline fp operator*(fp x, fp y) { + return {multiply(x.f, y.f), x.e + y.e + 64}; +} + +template struct basic_data { + // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. + // These are generated by support/compute-powers.py. + static constexpr uint64_t pow10_significands[87] = { + 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, + 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, + 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, + 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, + 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, + 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, + 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, + 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, + 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, + 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, + 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, + 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, + 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, + 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, + 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, + 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, + 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, + 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, + 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, + 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, + 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, + 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, + 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, + 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, + 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, + 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, + 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, + 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, + 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, + }; + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wnarrowing" +#endif + // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding + // to significands above. + static constexpr int16_t pow10_exponents[87] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, + -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, + -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, + -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, + -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, + 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, + 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, + 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +# pragma GCC diagnostic pop +#endif + + static constexpr uint64_t power_of_10_64[20] = { + 1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; +}; + +#if FMT_CPLUSPLUS < 201703L +template constexpr uint64_t basic_data::pow10_significands[]; +template constexpr int16_t basic_data::pow10_exponents[]; +template constexpr uint64_t basic_data::power_of_10_64[]; +#endif + +// This is a struct rather than an alias to avoid shadowing warnings in gcc. +struct data : basic_data<> {}; + +// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its +// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. +FMT_CONSTEXPR inline fp get_cached_power(int min_exponent, + int& pow10_exponent) { + const int shift = 32; + // log10(2) = 0x0.4d104d427de7fbcc... + const int64_t significand = 0x4d104d427de7fbcc; + int index = static_cast( + ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) + + ((int64_t(1) << shift) - 1)) // ceil + >> 32 // arithmetic shift + ); + // Decimal exponent of the first (smallest) cached power of 10. + const int first_dec_exp = -348; + // Difference between 2 consecutive decimal exponents in cached powers of 10. + const int dec_exp_step = 8; + index = (index - first_dec_exp - 1) / dec_exp_step + 1; + pow10_exponent = first_dec_exp + index * dec_exp_step; + // Using *(x + index) instead of x[index] avoids an issue with some compilers + // using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode). + return {*(data::pow10_significands + index), + *(data::pow10_exponents + index)}; +} #ifndef _MSC_VER # define FMT_SNPRINTF snprintf @@ -1381,34 +1574,26 @@ FMT_API auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...) -> int; # define FMT_SNPRINTF fmt_snprintf #endif // _MSC_VER -// Formats a floating-point number with snprintf. +// Formats a floating-point number with snprintf using the hexfloat format. template auto snprintf_float(T value, int precision, float_specs specs, buffer& buf) -> int { // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); + FMT_ASSERT(specs.format == float_format::hex, ""); static_assert(!std::is_same::value, ""); - // Subtract 1 to account for the difference in precision since we use %e for - // both general and exponent format. - if (specs.format == float_format::general || - specs.format == float_format::exp) { - precision = (precision >= 0 ? precision : 6) - 1; - } - // Build the format string. char format[7]; // The longest format is "%#.*Le". char* format_ptr = format; *format_ptr++ = '%'; - if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#'; + if (specs.showpoint) *format_ptr++ = '#'; if (precision >= 0) { *format_ptr++ = '.'; *format_ptr++ = '*'; } if (std::is_same()) *format_ptr++ = 'L'; - *format_ptr++ = specs.format != float_format::hex - ? (specs.format == float_format::fixed ? 'f' : 'e') - : (specs.upper ? 'A' : 'a'); + *format_ptr++ = specs.upper ? 'A' : 'a'; *format_ptr = '\0'; // Format using snprintf. @@ -1430,55 +1615,11 @@ auto snprintf_float(T value, int precision, float_specs specs, } auto size = to_unsigned(result); // Size equal to capacity means that the last character was truncated. - if (size >= capacity) { - buf.try_reserve(size + offset + 1); // Add 1 for the terminating '\0'. - continue; - } - auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; - if (specs.format == float_format::fixed) { - if (precision == 0) { - buf.try_resize(size); - return 0; - } - // Find and remove the decimal point. - auto end = begin + size, p = end; - do { - --p; - } while (is_digit(*p)); - int fraction_size = static_cast(end - p - 1); - std::memmove(p, p + 1, to_unsigned(fraction_size)); - buf.try_resize(size - 1); - return -fraction_size; - } - if (specs.format == float_format::hex) { + if (size < capacity) { buf.try_resize(size + offset); return 0; } - // Find and parse the exponent. - auto end = begin + size, exp_pos = end; - do { - --exp_pos; - } while (*exp_pos != 'e'); - char sign = exp_pos[1]; - FMT_ASSERT(sign == '+' || sign == '-', ""); - int exp = 0; - auto p = exp_pos + 2; // Skip 'e' and sign. - do { - FMT_ASSERT(is_digit(*p), ""); - exp = exp * 10 + (*p++ - '0'); - } while (p != end); - if (sign == '-') exp = -exp; - int fraction_size = 0; - if (exp_pos != begin + 1) { - // Remove trailing zeros. - auto fraction_end = exp_pos - 1; - while (*fraction_end == '0') --fraction_end; - // Move the fractional part left to get rid of the decimal point. - fraction_size = static_cast(fraction_end - begin - 1); - std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size)); - } - buf.try_resize(to_unsigned(fraction_size) + offset + 1); - return exp - fraction_size; + buf.try_reserve(size + offset + 1); // Add 1 for the terminating '\0'. } } @@ -1583,7 +1724,7 @@ auto find_escape(const Char* begin, const Char* end) -> find_escape_result { for (; begin != end; ++begin) { uint32_t cp = static_cast>(*begin); - if (sizeof(Char) == 1 && cp >= 0x80) continue; + if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue; if (needs_escape(cp)) return {begin, begin + 1, cp}; } return {begin, nullptr, 0}; @@ -1604,18 +1745,18 @@ inline auto find_escape(const char* begin, const char* end) return result; } -#define FMT_STRING_IMPL(s, base, explicit) \ - [] { \ - /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ - /* Use a macro-like name to avoid shadowing warnings. */ \ - struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \ - using char_type = fmt::remove_cvref_t; \ - FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ - operator fmt::basic_string_view() const { \ - return fmt::detail_exported::compile_string_to_view(s); \ - } \ - }; \ - return FMT_COMPILE_STRING(); \ +#define FMT_STRING_IMPL(s, base, explicit) \ + [] { \ + /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ + /* Use a macro-like name to avoid shadowing warnings. */ \ + struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \ + using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t; \ + FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ + operator fmt::basic_string_view() const { \ + return fmt::detail_exported::compile_string_to_view(s); \ + } \ + }; \ + return FMT_COMPILE_STRING(); \ }() /** @@ -1628,12 +1769,16 @@ inline auto find_escape(const char* begin, const char* end) std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); \endrst */ -#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string, ) +#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, ) -template -auto write_escaped_string(OutputIt out, basic_string_view str) - -> OutputIt { - return copy_str(str.data(), str.data() + str.size(), out); +template +auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { + *out++ = static_cast('\\'); + *out++ = static_cast(prefix); + Char buf[width]; + fill_n(buf, width, static_cast('0')); + format_uint<4>(buf, cp, width); + return copy_str(buf, buf + width, out); } template @@ -1642,40 +1787,40 @@ auto write_escaped_cp(OutputIt out, const find_escape_result& escape) auto c = static_cast(escape.cp); switch (escape.cp) { case '\n': - *out++ = '\\'; - c = 'n'; + *out++ = static_cast('\\'); + c = static_cast('n'); break; case '\r': - *out++ = '\\'; - c = 'r'; + *out++ = static_cast('\\'); + c = static_cast('r'); break; case '\t': - *out++ = '\\'; - c = 't'; + *out++ = static_cast('\\'); + c = static_cast('t'); break; case '"': FMT_FALLTHROUGH; case '\'': FMT_FALLTHROUGH; case '\\': - *out++ = '\\'; + *out++ = static_cast('\\'); break; default: if (is_utf8()) { if (escape.cp < 0x100) { - return format_to(out, FMT_STRING("\\x{:02x}"), escape.cp); + return write_codepoint<2, Char>(out, 'x', escape.cp); } if (escape.cp < 0x10000) { - return format_to(out, FMT_STRING("\\u{:04x}"), escape.cp); + return write_codepoint<4, Char>(out, 'u', escape.cp); } if (escape.cp < 0x110000) { - return format_to(out, FMT_STRING("\\U{:08x}"), escape.cp); + return write_codepoint<8, Char>(out, 'U', escape.cp); } } - for (char escape_char : basic_string_view( + for (Char escape_char : basic_string_view( escape.begin, to_unsigned(escape.end - escape.begin))) { - out = format_to(out, FMT_STRING("\\x{:02x}"), - static_cast>(escape_char)); + out = write_codepoint<2, Char>(out, 'x', + static_cast(escape_char) & 0xFF); } return out; } @@ -1683,38 +1828,33 @@ auto write_escaped_cp(OutputIt out, const find_escape_result& escape) return out; } -template -auto write_escaped_string(OutputIt out, basic_string_view str) +template +auto write_escaped_string(OutputIt out, basic_string_view str) -> OutputIt { - *out++ = '"'; + *out++ = static_cast('"'); auto begin = str.begin(), end = str.end(); do { auto escape = find_escape(begin, end); - out = copy_str(begin, escape.begin, out); + out = copy_str(begin, escape.begin, out); begin = escape.end; if (!begin) break; - out = write_escaped_cp(out, escape); + out = write_escaped_cp(out, escape); } while (begin != end); - *out++ = '"'; + *out++ = static_cast('"'); return out; } template auto write_escaped_char(OutputIt out, Char v) -> OutputIt { - *out++ = v; - return out; -} - -template -auto write_escaped_char(OutputIt out, char v) -> OutputIt { - *out++ = '\''; - if ((needs_escape(static_cast(v)) && v != '"') || v == '\'') { + *out++ = static_cast('\''); + if ((needs_escape(static_cast(v)) && v != static_cast('"')) || + v == static_cast('\'')) { out = write_escaped_cp( - out, find_escape_result{&v, &v + 1, static_cast(v)}); + out, find_escape_result{&v, &v + 1, static_cast(v)}); } else { *out++ = v; } - *out++ = '\''; + *out++ = static_cast('\''); return out; } @@ -1862,7 +2002,10 @@ auto write_int_localized(OutputIt out, UInt value, unsigned prefix, grouping.count_separators(num_digits)); return write_padded( out, specs, size, size, [&](reserve_iterator it) { - if (prefix != 0) *it++ = static_cast(prefix); + if (prefix != 0) { + char sign = static_cast(prefix); + *it++ = static_cast(sign); + } return grouping.apply(it, string_view(digits, to_unsigned(num_digits))); }); } @@ -2004,29 +2147,30 @@ class counting_iterator { FMT_UNCHECKED_ITERATOR(counting_iterator); struct value_type { - template void operator=(const T&) {} + template FMT_CONSTEXPR void operator=(const T&) {} }; - counting_iterator() : count_(0) {} + FMT_CONSTEXPR counting_iterator() : count_(0) {} - size_t count() const { return count_; } + FMT_CONSTEXPR size_t count() const { return count_; } - counting_iterator& operator++() { + FMT_CONSTEXPR counting_iterator& operator++() { ++count_; return *this; } - counting_iterator operator++(int) { + FMT_CONSTEXPR counting_iterator operator++(int) { auto it = *this; ++*this; return it; } - friend counting_iterator operator+(counting_iterator it, difference_type n) { + FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it, + difference_type n) { it.count_ += static_cast(n); return it; } - value_type operator*() const { return {}; } + FMT_CONSTEXPR value_type operator*() const { return {}; } }; template @@ -2067,6 +2211,28 @@ FMT_CONSTEXPR auto write(OutputIt out, const Char* s, : write_ptr(out, bit_cast(s), &specs); } +template ::value && + !std::is_same::value && + !std::is_same::value)> +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { + auto abs_value = static_cast>(value); + bool negative = is_negative(value); + // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. + if (negative) abs_value = ~abs_value + 1; + int num_digits = count_digits(abs_value); + auto size = (negative ? 1 : 0) + static_cast(num_digits); + auto it = reserve(out, size); + if (auto ptr = to_pointer(it, size)) { + if (negative) *ptr++ = static_cast('-'); + format_decimal(ptr, abs_value, num_digits); + return out; + } + if (negative) *it++ = static_cast('-'); + it = format_decimal(it, abs_value, num_digits).end; + return base_iterator(out, it); +} + template FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, basic_format_specs specs, @@ -2093,12 +2259,12 @@ struct big_decimal_fp { int exponent; }; -constexpr auto get_significand_size(const big_decimal_fp& fp) -> int { - return fp.significand_size; +constexpr auto get_significand_size(const big_decimal_fp& f) -> int { + return f.significand_size; } template -inline auto get_significand_size(const dragonbox::decimal_fp& fp) -> int { - return count_digits(fp.significand); +inline auto get_significand_size(const dragonbox::decimal_fp& f) -> int { + return count_digits(f.significand); } template @@ -2192,12 +2358,12 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, template > -FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp, +FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, const basic_format_specs& specs, float_specs fspecs, locale_ref loc) -> OutputIt { - auto significand = fp.significand; - int significand_size = get_significand_size(fp); + auto significand = f.significand; + int significand_size = get_significand_size(f); const Char zero = static_cast('0'); auto sign = fspecs.sign; size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); @@ -2206,7 +2372,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp, Char decimal_point = fspecs.locale ? detail::decimal_point(loc) : static_cast('.'); - int output_exp = fp.exponent + significand_size - 1; + int output_exp = f.exponent + significand_size - 1; auto use_exp_format = [=]() { if (fspecs.format == float_format::exp) return true; if (fspecs.format != float_format::general) return false; @@ -2244,10 +2410,10 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp, : base_iterator(out, write(reserve(out, size))); } - int exp = fp.exponent + significand_size; - if (fp.exponent >= 0) { + int exp = f.exponent + significand_size; + if (f.exponent >= 0) { // 1234e5 -> 123400000[.0+] - size += to_unsigned(fp.exponent); + size += to_unsigned(f.exponent); int num_zeros = fspecs.precision - exp; abort_fuzzing_if(num_zeros > 5000); if (fspecs.showpoint) { @@ -2260,7 +2426,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp, return write_padded(out, specs, size, [&](iterator it) { if (sign) *it++ = detail::sign(sign); it = write_significand(it, significand, significand_size, - fp.exponent, grouping); + f.exponent, grouping); if (!fspecs.showpoint) return it; *it++ = decimal_point; return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; @@ -2311,16 +2477,16 @@ template class fallback_digit_grouping { }; template -FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& fp, +FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, const basic_format_specs& specs, float_specs fspecs, locale_ref loc) -> OutputIt { if (is_constant_evaluated()) { return do_write_float>(out, fp, specs, fspecs, + fallback_digit_grouping>(out, f, specs, fspecs, loc); } else { - return do_write_float(out, fp, specs, fspecs, loc); + return do_write_float(out, f, specs, fspecs, loc); } } @@ -2338,12 +2504,16 @@ struct has_isfinite> template ::value&& has_isfinite::value)> FMT_CONSTEXPR20 bool isfinite(T value) { - if (is_constant_evaluated()) return !isnan(value - value); + constexpr T inf = T(std::numeric_limits::infinity()); + if (is_constant_evaluated()) + return !detail::isnan(value) && value != inf && value != -inf; return std::isfinite(value); } template ::value)> -constexpr bool isfinite(T value) { - return value - value == 0; // std::isfinite doesn't support __float128. +FMT_CONSTEXPR bool isfinite(T value) { + T inf = T(std::numeric_limits::infinity()); + // std::isfinite doesn't support __float128. + return !detail::isnan(value) && value != inf && value != -inf; } template ::value)> @@ -2359,6 +2529,659 @@ FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { return std::signbit(static_cast(value)); } +enum class round_direction { unknown, up, down }; + +// Given the divisor (normally a power of 10), the remainder = v % divisor for +// some number v and the error, returns whether v should be rounded up, down, or +// whether the rounding direction can't be determined due to error. +// error should be less than divisor / 2. +FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor, + uint64_t remainder, + uint64_t error) { + FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. + FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. + FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. + // Round down if (remainder + error) * 2 <= divisor. + if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) + return round_direction::down; + // Round up if (remainder - error) * 2 >= divisor. + if (remainder >= error && + remainder - error >= divisor - (remainder - error)) { + return round_direction::up; + } + return round_direction::unknown; +} + +namespace digits { +enum result { + more, // Generate more digits. + done, // Done generating digits. + error // Digit generation cancelled due to an error. +}; +} + +struct gen_digits_handler { + char* buf; + int size; + int precision; + int exp10; + bool fixed; + + FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor, + uint64_t remainder, uint64_t error, + bool integral) { + FMT_ASSERT(remainder < divisor, ""); + buf[size++] = digit; + if (!integral && error >= remainder) return digits::error; + if (size < precision) return digits::more; + if (!integral) { + // Check if error * 2 < divisor with overflow prevention. + // The check is not needed for the integral part because error = 1 + // and divisor > (1 << 32) there. + if (error >= divisor || error >= divisor - error) return digits::error; + } else { + FMT_ASSERT(error == 1 && divisor > 2, ""); + } + auto dir = get_round_direction(divisor, remainder, error); + if (dir != round_direction::up) + return dir == round_direction::down ? digits::done : digits::error; + ++buf[size - 1]; + for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] > '9') { + buf[0] = '1'; + if (fixed) + buf[size++] = '0'; + else + ++exp10; + } + return digits::done; + } +}; + +inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) { + // Adjust fixed precision by exponent because it is relative to decimal + // point. + if (exp10 > 0 && precision > max_value() - exp10) + FMT_THROW(format_error("number is too big")); + precision += exp10; +} + +// Generates output using the Grisu digit-gen algorithm. +// error: the size of the region (lower, upper) outside of which numbers +// definitely do not round to value (Delta in Grisu3). +FMT_INLINE FMT_CONSTEXPR20 auto grisu_gen_digits(fp value, uint64_t error, + int& exp, + gen_digits_handler& handler) + -> digits::result { + const fp one(1ULL << -value.e, value.e); + // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be + // zero because it contains a product of two 64-bit numbers with MSB set (due + // to normalization) - 1, shifted right by at most 60 bits. + auto integral = static_cast(value.f >> -one.e); + FMT_ASSERT(integral != 0, ""); + FMT_ASSERT(integral == value.f >> -one.e, ""); + // The fractional part of scaled value (p2 in Grisu) c = value % one. + uint64_t fractional = value.f & (one.f - 1); + exp = count_digits(integral); // kappa in Grisu. + // Non-fixed formats require at least one digit and no precision adjustment. + if (handler.fixed) { + adjust_precision(handler.precision, exp + handler.exp10); + // Check if precision is satisfied just by leading zeros, e.g. + // format("{:.2f}", 0.001) gives "0.00" without generating any digits. + if (handler.precision <= 0) { + if (handler.precision < 0) return digits::done; + // Divide by 10 to prevent overflow. + uint64_t divisor = data::power_of_10_64[exp - 1] << -one.e; + auto dir = get_round_direction(divisor, value.f / 10, error * 10); + if (dir == round_direction::unknown) return digits::error; + handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0'; + return digits::done; + } + } + // Generate digits for the integral part. This can produce up to 10 digits. + do { + uint32_t digit = 0; + auto divmod_integral = [&](uint32_t divisor) { + digit = integral / divisor; + integral %= divisor; + }; + // This optimization by Milo Yip reduces the number of integer divisions by + // one per iteration. + switch (exp) { + case 10: + divmod_integral(1000000000); + break; + case 9: + divmod_integral(100000000); + break; + case 8: + divmod_integral(10000000); + break; + case 7: + divmod_integral(1000000); + break; + case 6: + divmod_integral(100000); + break; + case 5: + divmod_integral(10000); + break; + case 4: + divmod_integral(1000); + break; + case 3: + divmod_integral(100); + break; + case 2: + divmod_integral(10); + break; + case 1: + digit = integral; + integral = 0; + break; + default: + FMT_ASSERT(false, "invalid number of digits"); + } + --exp; + auto remainder = (static_cast(integral) << -one.e) + fractional; + auto result = handler.on_digit(static_cast('0' + digit), + data::power_of_10_64[exp] << -one.e, + remainder, error, true); + if (result != digits::more) return result; + } while (exp > 0); + // Generate digits for the fractional part. + for (;;) { + fractional *= 10; + error *= 10; + char digit = static_cast('0' + (fractional >> -one.e)); + fractional &= one.f - 1; + --exp; + auto result = handler.on_digit(digit, one.f, fractional, error, false); + if (result != digits::more) return result; + } +} + +class bigint { + private: + // A bigint is stored as an array of bigits (big digits), with bigit at index + // 0 being the least significant one. + using bigit = uint32_t; + using double_bigit = uint64_t; + enum { bigits_capacity = 32 }; + basic_memory_buffer bigits_; + int exp_; + + FMT_CONSTEXPR20 bigit operator[](int index) const { + return bigits_[to_unsigned(index)]; + } + FMT_CONSTEXPR20 bigit& operator[](int index) { + return bigits_[to_unsigned(index)]; + } + + static constexpr const int bigit_bits = num_bits(); + + friend struct formatter; + + FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) { + auto result = static_cast((*this)[index]) - other - borrow; + (*this)[index] = static_cast(result); + borrow = static_cast(result >> (bigit_bits * 2 - 1)); + } + + FMT_CONSTEXPR20 void remove_leading_zeros() { + int num_bigits = static_cast(bigits_.size()) - 1; + while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; + bigits_.resize(to_unsigned(num_bigits + 1)); + } + + // Computes *this -= other assuming aligned bigints and *this >= other. + FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) { + FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); + FMT_ASSERT(compare(*this, other) >= 0, ""); + bigit borrow = 0; + int i = other.exp_ - exp_; + for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) + subtract_bigits(i, other.bigits_[j], borrow); + while (borrow > 0) subtract_bigits(i, 0, borrow); + remove_leading_zeros(); + } + + FMT_CONSTEXPR20 void multiply(uint32_t value) { + const double_bigit wide_value = value; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + double_bigit result = bigits_[i] * wide_value + carry; + bigits_[i] = static_cast(result); + carry = static_cast(result >> bigit_bits); + } + if (carry != 0) bigits_.push_back(carry); + } + + template ::value || + std::is_same::value)> + FMT_CONSTEXPR20 void multiply(UInt value) { + using half_uint = + conditional_t::value, uint64_t, uint32_t>; + const int shift = num_bits() - bigit_bits; + const UInt lower = static_cast(value); + const UInt upper = value >> num_bits(); + UInt carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + UInt result = lower * bigits_[i] + static_cast(carry); + carry = (upper * bigits_[i] << shift) + (result >> bigit_bits) + + (carry >> bigit_bits); + bigits_[i] = static_cast(result); + } + while (carry != 0) { + bigits_.push_back(static_cast(carry)); + carry >>= bigit_bits; + } + } + + template ::value || + std::is_same::value)> + FMT_CONSTEXPR20 void assign(UInt n) { + size_t num_bigits = 0; + do { + bigits_[num_bigits++] = static_cast(n); + n >>= bigit_bits; + } while (n != 0); + bigits_.resize(num_bigits); + exp_ = 0; + } + + public: + FMT_CONSTEXPR20 bigint() : exp_(0) {} + explicit bigint(uint64_t n) { assign(n); } + + bigint(const bigint&) = delete; + void operator=(const bigint&) = delete; + + FMT_CONSTEXPR20 void assign(const bigint& other) { + auto size = other.bigits_.size(); + bigits_.resize(size); + auto data = other.bigits_.data(); + std::copy(data, data + size, make_checked(bigits_.data(), size)); + exp_ = other.exp_; + } + + template FMT_CONSTEXPR20 void operator=(Int n) { + FMT_ASSERT(n > 0, ""); + assign(uint64_or_128_t(n)); + } + + FMT_CONSTEXPR20 int num_bigits() const { + return static_cast(bigits_.size()) + exp_; + } + + FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) { + FMT_ASSERT(shift >= 0, ""); + exp_ += shift / bigit_bits; + shift %= bigit_bits; + if (shift == 0) return *this; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + bigit c = bigits_[i] >> (bigit_bits - shift); + bigits_[i] = (bigits_[i] << shift) + carry; + carry = c; + } + if (carry != 0) bigits_.push_back(carry); + return *this; + } + + template FMT_CONSTEXPR20 bigint& operator*=(Int value) { + FMT_ASSERT(value > 0, ""); + multiply(uint32_or_64_or_128_t(value)); + return *this; + } + + friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) { + int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); + if (num_lhs_bigits != num_rhs_bigits) + return num_lhs_bigits > num_rhs_bigits ? 1 : -1; + int i = static_cast(lhs.bigits_.size()) - 1; + int j = static_cast(rhs.bigits_.size()) - 1; + int end = i - j; + if (end < 0) end = 0; + for (; i >= end; --i, --j) { + bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; + if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; + } + if (i != j) return i > j ? 1 : -1; + return 0; + } + + // Returns compare(lhs1 + lhs2, rhs). + friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2, + const bigint& rhs) { + auto minimum = [](int a, int b) { return a < b ? a : b; }; + auto maximum = [](int a, int b) { return a > b ? a : b; }; + int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits()); + int num_rhs_bigits = rhs.num_bigits(); + if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; + if (max_lhs_bigits > num_rhs_bigits) return 1; + auto get_bigit = [](const bigint& n, int i) -> bigit { + return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; + }; + double_bigit borrow = 0; + int min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_); + for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { + double_bigit sum = + static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); + bigit rhs_bigit = get_bigit(rhs, i); + if (sum > rhs_bigit + borrow) return 1; + borrow = rhs_bigit + borrow - sum; + if (borrow > 1) return -1; + borrow <<= bigit_bits; + } + return borrow != 0 ? -1 : 0; + } + + // Assigns pow(10, exp) to this bigint. + FMT_CONSTEXPR20 void assign_pow10(int exp) { + FMT_ASSERT(exp >= 0, ""); + if (exp == 0) return *this = 1; + // Find the top bit. + int bitmask = 1; + while (exp >= bitmask) bitmask <<= 1; + bitmask >>= 1; + // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by + // repeated squaring and multiplication. + *this = 5; + bitmask >>= 1; + while (bitmask != 0) { + square(); + if ((exp & bitmask) != 0) *this *= 5; + bitmask >>= 1; + } + *this <<= exp; // Multiply by pow(2, exp) by shifting. + } + + FMT_CONSTEXPR20 void square() { + int num_bigits = static_cast(bigits_.size()); + int num_result_bigits = 2 * num_bigits; + basic_memory_buffer n(std::move(bigits_)); + bigits_.resize(to_unsigned(num_result_bigits)); + auto sum = uint128_t(); + for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) { + // Compute bigit at position bigit_index of the result by adding + // cross-product terms n[i] * n[j] such that i + j == bigit_index. + for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { + // Most terms are multiplied twice which can be optimized in the future. + sum += static_cast(n[i]) * n[j]; + } + (*this)[bigit_index] = static_cast(sum); + sum >>= num_bits(); // Compute the carry. + } + // Do the same for the top half. + for (int bigit_index = num_bigits; bigit_index < num_result_bigits; + ++bigit_index) { + for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) + sum += static_cast(n[i++]) * n[j--]; + (*this)[bigit_index] = static_cast(sum); + sum >>= num_bits(); + } + remove_leading_zeros(); + exp_ *= 2; + } + + // If this bigint has a bigger exponent than other, adds trailing zero to make + // exponents equal. This simplifies some operations such as subtraction. + FMT_CONSTEXPR20 void align(const bigint& other) { + int exp_difference = exp_ - other.exp_; + if (exp_difference <= 0) return; + int num_bigits = static_cast(bigits_.size()); + bigits_.resize(to_unsigned(num_bigits + exp_difference)); + for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) + bigits_[j] = bigits_[i]; + std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); + exp_ -= exp_difference; + } + + // Divides this bignum by divisor, assigning the remainder to this and + // returning the quotient. + FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) { + FMT_ASSERT(this != &divisor, ""); + if (compare(*this, divisor) < 0) return 0; + FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); + align(divisor); + int quotient = 0; + do { + subtract_aligned(divisor); + ++quotient; + } while (compare(*this, divisor) >= 0); + return quotient; + } +}; + +// format_dragon flags. +enum dragon { + predecessor_closer = 1, + fixup = 2, // Run fixup to correct exp10 which can be off by one. + fixed = 4, +}; + +// Formats a floating-point number using a variation of the Fixed-Precision +// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White: +// https://fmt.dev/papers/p372-steele.pdf. +FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, + unsigned flags, int num_digits, + buffer& buf, int& exp10) { + bigint numerator; // 2 * R in (FPP)^2. + bigint denominator; // 2 * S in (FPP)^2. + // lower and upper are differences between value and corresponding boundaries. + bigint lower; // (M^- in (FPP)^2). + bigint upper_store; // upper's value if different from lower. + bigint* upper = nullptr; // (M^+ in (FPP)^2). + // Shift numerator and denominator by an extra bit or two (if lower boundary + // is closer) to make lower and upper integers. This eliminates multiplication + // by 2 during later computations. + bool is_predecessor_closer = (flags & dragon::predecessor_closer) != 0; + int shift = is_predecessor_closer ? 2 : 1; + if (value.e >= 0) { + numerator = value.f; + numerator <<= value.e + shift; + lower = 1; + lower <<= value.e; + if (is_predecessor_closer) { + upper_store = 1; + upper_store <<= value.e + 1; + upper = &upper_store; + } + denominator.assign_pow10(exp10); + denominator <<= shift; + } else if (exp10 < 0) { + numerator.assign_pow10(-exp10); + lower.assign(numerator); + if (is_predecessor_closer) { + upper_store.assign(numerator); + upper_store <<= 1; + upper = &upper_store; + } + numerator *= value.f; + numerator <<= shift; + denominator = 1; + denominator <<= shift - value.e; + } else { + numerator = value.f; + numerator <<= shift; + denominator.assign_pow10(exp10); + denominator <<= shift - value.e; + lower = 1; + if (is_predecessor_closer) { + upper_store = 1ULL << 1; + upper = &upper_store; + } + } + int even = static_cast((value.f & 1) == 0); + if (!upper) upper = &lower; + if ((flags & dragon::fixup) != 0) { + if (add_compare(numerator, *upper, denominator) + even <= 0) { + --exp10; + numerator *= 10; + if (num_digits < 0) { + lower *= 10; + if (upper != &lower) *upper *= 10; + } + } + if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1); + } + // Invariant: value == (numerator / denominator) * pow(10, exp10). + if (num_digits < 0) { + // Generate the shortest representation. + num_digits = 0; + char* data = buf.data(); + for (;;) { + int digit = numerator.divmod_assign(denominator); + bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. + // numerator + upper >[=] pow10: + bool high = add_compare(numerator, *upper, denominator) + even > 0; + data[num_digits++] = static_cast('0' + digit); + if (low || high) { + if (!low) { + ++data[num_digits - 1]; + } else if (high) { + int result = add_compare(numerator, numerator, denominator); + // Round half to even. + if (result > 0 || (result == 0 && (digit % 2) != 0)) + ++data[num_digits - 1]; + } + buf.try_resize(to_unsigned(num_digits)); + exp10 -= num_digits - 1; + return; + } + numerator *= 10; + lower *= 10; + if (upper != &lower) *upper *= 10; + } + } + // Generate the given number of digits. + exp10 -= num_digits - 1; + if (num_digits == 0) { + denominator *= 10; + auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + buf.push_back(digit); + return; + } + buf.try_resize(to_unsigned(num_digits)); + for (int i = 0; i < num_digits - 1; ++i) { + int digit = numerator.divmod_assign(denominator); + buf[i] = static_cast('0' + digit); + numerator *= 10; + } + int digit = numerator.divmod_assign(denominator); + auto result = add_compare(numerator, numerator, denominator); + if (result > 0 || (result == 0 && (digit % 2) != 0)) { + if (digit == 9) { + const auto overflow = '0' + 10; + buf[num_digits - 1] = overflow; + // Propagate the carry. + for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] == overflow) { + buf[0] = '1'; + ++exp10; + } + return; + } + ++digit; + } + buf[num_digits - 1] = static_cast('0' + digit); +} + +template +FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, + buffer& buf) -> int { + // float is passed as double to reduce the number of instantiations. + static_assert(!std::is_same::value, ""); + FMT_ASSERT(value >= 0, "value is negative"); + auto converted_value = convert_float(value); + + const bool fixed = specs.format == float_format::fixed; + if (value <= 0) { // <= instead of == to silence a warning. + if (precision <= 0 || !fixed) { + buf.push_back('0'); + return 0; + } + buf.try_resize(to_unsigned(precision)); + fill_n(buf.data(), precision, '0'); + return -precision; + } + + int exp = 0; + bool use_dragon = true; + unsigned dragon_flags = 0; + if (!is_fast_float()) { + const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10) + using info = dragonbox::float_info; + const auto f = basic_fp(converted_value); + // Compute exp, an approximate power of 10, such that + // 10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1). + // This is based on log10(value) == log2(value) / log2(10) and approximation + // of log2(value) by e + num_fraction_bits idea from double-conversion. + exp = static_cast( + std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10)); + dragon_flags = dragon::fixup; + } else if (!is_constant_evaluated() && precision < 0) { + // Use Dragonbox for the shortest format. + if (specs.binary32) { + auto dec = dragonbox::to_decimal(static_cast(value)); + write(buffer_appender(buf), dec.significand); + return dec.exponent; + } + auto dec = dragonbox::to_decimal(static_cast(value)); + write(buffer_appender(buf), dec.significand); + return dec.exponent; + } else { + // Use Grisu + Dragon4 for the given precision: + // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. + const int min_exp = -60; // alpha in Grisu. + int cached_exp10 = 0; // K in Grisu. + fp normalized = normalize(fp(converted_value)); + const auto cached_pow = get_cached_power( + min_exp - (normalized.e + fp::num_significand_bits), cached_exp10); + normalized = normalized * cached_pow; + gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; + if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error && + !is_constant_evaluated()) { + exp += handler.exp10; + buf.try_resize(to_unsigned(handler.size)); + use_dragon = false; + } else { + exp += handler.size - cached_exp10 - 1; + precision = handler.precision; + } + } + if (use_dragon) { + auto f = basic_fp(); + bool is_predecessor_closer = specs.binary32 + ? f.assign(static_cast(value)) + : f.assign(converted_value); + if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer; + if (fixed) dragon_flags |= dragon::fixed; + // Limit precision to the maximum possible number of significant digits in + // an IEEE754 double because we don't need to generate zeros. + const int max_double_digits = 767; + if (precision > max_double_digits) precision = max_double_digits; + format_dragon(f, dragon_flags, precision, buf, exp); + } + if (!fixed && !specs.showpoint) { + // Remove trailing zeros. + auto num_digits = buf.size(); + while (num_digits > 0 && buf[num_digits - 1] == '0') { + --num_digits; + ++exp; + } + buf.try_resize(num_digits); + } + return exp; +} + template ::value)> FMT_CONSTEXPR20 auto write(OutputIt out, T value, @@ -2406,8 +3229,8 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value, if (const_check(std::is_same())) fspecs.binary32 = true; int exp = format_float(convert_float(value), precision, fspecs, buffer); fspecs.precision = precision; - auto fp = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; - return write_float(out, fp, specs, fspecs, loc); + auto f = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; + return write_float(out, f, specs, fspecs, loc); } template OutputIt { constexpr auto specs = basic_format_specs(); using floaty = conditional_t::value, double, T>; - using fmt_uint = typename dragonbox::float_info::carrier_uint; - fmt_uint mask = exponent_mask(); - if ((bit_cast(value) & mask) == mask) + using uint = typename dragonbox::float_info::carrier_uint; + uint mask = exponent_mask(); + if ((bit_cast(value) & mask) == mask) return write_nonfinite(out, std::isnan(value), specs, fspecs); auto dec = dragonbox::to_decimal(static_cast(value)); @@ -2462,28 +3285,6 @@ constexpr auto write(OutputIt out, const T& value) -> OutputIt { return write(out, to_string_view(value)); } -template ::value && - !std::is_same::value && - !std::is_same::value)> -FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { - auto abs_value = static_cast>(value); - bool negative = is_negative(value); - // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. - if (negative) abs_value = ~abs_value + 1; - int num_digits = count_digits(abs_value); - auto size = (negative ? 1 : 0) + static_cast(num_digits); - auto it = reserve(out, size); - if (auto ptr = to_pointer(it, size)) { - if (negative) *ptr++ = static_cast('-'); - format_decimal(ptr, abs_value, num_digits); - return out; - } - if (negative) *it++ = static_cast('-'); - it = format_decimal(it, abs_value, num_digits).end; - return base_iterator(out, it); -} - // FMT_ENABLE_IF() condition separated to workaround an MSVC bug. template < typename Char, typename OutputIt, typename T, @@ -2742,7 +3543,7 @@ template struct udl_formatter { } }; -# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +# if FMT_USE_NONTYPE_TEMPLATE_ARGS template Str> struct statically_named_arg : view { @@ -3127,9 +3928,6 @@ struct join_view : detail::view { : begin(b), end(e), sep(s) {} }; -template -using arg_join FMT_DEPRECATED_ALIAS = join_view; - template struct formatter, Char> { private: @@ -3345,12 +4143,6 @@ extern template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; extern template FMT_API auto decimal_point_impl(locale_ref) -> char; extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; -extern template auto format_float(double value, int precision, - float_specs specs, buffer& buf) - -> int; -extern template auto format_float(long double value, int precision, - float_specs specs, - buffer& buf) -> int; #endif // FMT_HEADER_ONLY FMT_END_DETAIL_NAMESPACE @@ -3367,7 +4159,7 @@ inline namespace literals { fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); \endrst */ -# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +# if FMT_USE_NONTYPE_TEMPLATE_ARGS template constexpr auto operator""_a() { using char_t = remove_cvref_t; return detail::udl_arg(); @@ -3377,13 +4169,6 @@ constexpr auto operator"" _a(const char* s, size_t) -> detail::udl_arg { return {s}; } # endif - -// DEPRECATED! -// User-defined literal equivalent of fmt::format. -FMT_DEPRECATED constexpr auto operator"" _format(const char* s, size_t n) - -> detail::udl_formatter { - return {{s, n}}; -} } // namespace literals #endif // FMT_USE_USER_DEFINED_LITERALS @@ -3400,14 +4185,6 @@ inline auto format(const Locale& loc, format_string fmt, T&&... args) return vformat(loc, string_view(fmt), fmt::make_format_args(args...)); } -template -FMT_DEPRECATED auto format_to(basic_memory_buffer& buf, - format_string fmt, T&&... args) - -> appender { - detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...)); - return appender(buf); -} - template ::value&& detail::is_locale::value)> @@ -3430,10 +4207,6 @@ FMT_INLINE auto format_to(OutputIt out, const Locale& loc, FMT_MODULE_EXPORT_END FMT_END_NAMESPACE -#ifdef FMT_DEPRECATED_INCLUDE_XCHAR -# include "xchar.h" -#endif - #ifdef FMT_HEADER_ONLY # define FMT_FUNC inline # include "format-inl.h" diff --git a/Externals/fmt/include/fmt/locale.h b/Externals/fmt/include/fmt/locale.h deleted file mode 100755 index 7571b5261b..0000000000 --- a/Externals/fmt/include/fmt/locale.h +++ /dev/null @@ -1,2 +0,0 @@ -#include "xchar.h" -#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead diff --git a/Externals/fmt/include/fmt/os.h b/Externals/fmt/include/fmt/os.h index d7ba5f4c6e..d82be1125a 100644 --- a/Externals/fmt/include/fmt/os.h +++ b/Externals/fmt/include/fmt/os.h @@ -260,10 +260,7 @@ class buffered_file { // Returns the pointer to a FILE object representing this file. FILE* get() const noexcept { return file_; } - // We place parentheses around fileno to workaround a bug in some versions - // of MinGW that define fileno as a macro. - // DEPRECATED! Rename to descriptor to avoid issues with macros. - FMT_API int(fileno)() const; + FMT_API int descriptor() const; void vprint(string_view format_str, format_args args) { fmt::vprint(file_, format_str, args); diff --git a/Externals/fmt/include/fmt/ostream.h b/Externals/fmt/include/fmt/ostream.h index e228cfc6f9..c3cdd4a61b 100755 --- a/Externals/fmt/include/fmt/ostream.h +++ b/Externals/fmt/include/fmt/ostream.h @@ -10,6 +10,12 @@ #include #include +#if defined(_WIN32) && defined(__GLIBCXX__) +# include +# include +#elif defined(_WIN32) && defined(_LIBCPP_VERSION) +# include <__std_stream> +#endif #include "format.h" @@ -51,41 +57,50 @@ struct is_streamable< (std::is_convertible::value && !std::is_enum::value)>> : std::false_type {}; -template FILE* get_file(std::basic_filebuf&) { - return nullptr; -} - -struct dummy_filebuf { - FILE* _Myfile; -}; -template struct ms_filebuf { - using type = dummy_filebuf; -}; -template struct ms_filebuf { - using type = T; -}; -using filebuf_type = ms_filebuf::type; - -FILE* get_file(filebuf_type& buf); - // Generate a unique explicit instantion in every translation unit using a tag // type in an anonymous namespace. namespace { -struct filebuf_access_tag {}; +struct file_access_tag {}; } // namespace -template -class filebuf_access { - friend FILE* get_file(filebuf_type& buf) { return buf.*file; } +template +class file_access { + friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } }; -template class filebuf_access; -inline bool write(std::filebuf& buf, fmt::string_view data) { - print(get_file(buf), data); - return true; +#if FMT_MSC_VERSION +template class file_access; +auto get_file(std::filebuf&) -> FILE*; +#elif defined(_WIN32) && defined(_LIBCPP_VERSION) +template class file_access, + &std::__stdoutbuf::__file_>; +auto get_file(std::__stdoutbuf&) -> FILE*; +#endif + +inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) { +#if FMT_MSC_VERSION + if (auto* buf = dynamic_cast(os.rdbuf())) + if (FILE* f = get_file(*buf)) return write_console(f, data); +#elif defined(_WIN32) && defined(__GLIBCXX__) + auto* rdbuf = os.rdbuf(); + FILE* c_file; + if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf*>(rdbuf)) + c_file = fbuf->file(); + else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf*>(rdbuf)) + c_file = fbuf->file(); + else + return false; + if (c_file) return write_console(c_file, data); +#elif defined(_WIN32) && defined(_LIBCPP_VERSION) + if (auto* buf = dynamic_cast*>(os.rdbuf())) + if (FILE* f = get_file(*buf)) return write_console(f, data); +#else + ignore_unused(os, data); +#endif + return false; } -inline bool write(std::wfilebuf&, fmt::basic_string_view) { +inline bool write_ostream_unicode(std::wostream&, + fmt::basic_string_view) { return false; } @@ -93,10 +108,6 @@ inline bool write(std::wfilebuf&, fmt::basic_string_view) { // It is a separate function rather than a part of vprint to simplify testing. template void write_buffer(std::basic_ostream& os, buffer& buf) { - if (const_check(FMT_MSC_VER)) { - auto filebuf = dynamic_cast*>(os.rdbuf()); - if (filebuf && write(*filebuf, {buf.data(), buf.size()})) return; - } const Char* buf_data = buf.data(); using unsigned_streamsize = std::make_unsigned::type; unsigned_streamsize size = buf.size(); @@ -120,11 +131,16 @@ void format_value(buffer& buf, const T& value, output << value; output.exceptions(std::ios_base::failbit | std::ios_base::badbit); } + +template struct streamed_view { const T& value; }; + } // namespace detail // Formats an object of type T that has an overloaded ostream operator<<. template struct basic_ostream_formatter : formatter, Char> { + void set_debug_format() = delete; + template auto format(const T& value, basic_format_context& ctx) const -> OutputIt { @@ -137,6 +153,31 @@ struct basic_ostream_formatter : formatter, Char> { using ostream_formatter = basic_ostream_formatter; +template +struct formatter, Char> + : basic_ostream_formatter { + template + auto format(detail::streamed_view view, + basic_format_context& ctx) const -> OutputIt { + return basic_ostream_formatter::format(view.value, ctx); + } +}; + +/** + \rst + Returns a view that formats `value` via an ostream ``operator<<``. + + **Example**:: + + fmt::print("Current thread id: {}\n", + fmt::streamed(std::this_thread::get_id())); + \endrst + */ +template +auto streamed(const T& value) -> detail::streamed_view { + return {value}; +} + namespace detail { // Formats an object of type T that has an overloaded ostream operator<<. @@ -144,24 +185,24 @@ template struct fallback_formatter::value>> : basic_ostream_formatter { using basic_ostream_formatter::format; - // DEPRECATED! - template - auto format(const T& value, basic_printf_context& ctx) const - -> OutputIt { - auto buffer = basic_memory_buffer(); - format_value(buffer, value, ctx.locale()); - return std::copy(buffer.begin(), buffer.end(), ctx.out()); - } }; + +inline void vprint_directly(std::ostream& os, string_view format_str, + format_args args) { + auto buffer = memory_buffer(); + detail::vformat_to(buffer, format_str, args); + detail::write_buffer(os, buffer); +} + } // namespace detail -FMT_MODULE_EXPORT -template +FMT_MODULE_EXPORT template void vprint(std::basic_ostream& os, basic_string_view> format_str, basic_format_args>> args) { auto buffer = basic_memory_buffer(); detail::vformat_to(buffer, format_str, args); + if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return; detail::write_buffer(os, buffer); } @@ -174,10 +215,13 @@ void vprint(std::basic_ostream& os, fmt::print(cerr, "Don't {}!", "panic"); \endrst */ -FMT_MODULE_EXPORT -template +FMT_MODULE_EXPORT template void print(std::ostream& os, format_string fmt, T&&... args) { - vprint(os, fmt, fmt::make_format_args(args...)); + const auto& vargs = fmt::make_format_args(args...); + if (detail::is_utf8()) + vprint(os, fmt, vargs); + else + detail::vprint_directly(os, fmt, vargs); } FMT_MODULE_EXPORT diff --git a/Externals/fmt/include/fmt/printf.h b/Externals/fmt/include/fmt/printf.h index 19d550f6cf..70a592dc26 100755 --- a/Externals/fmt/include/fmt/printf.h +++ b/Externals/fmt/include/fmt/printf.h @@ -10,7 +10,6 @@ #include // std::max #include // std::numeric_limits -#include #include "format.h" @@ -561,7 +560,7 @@ inline auto vsprintf( basic_format_args>> args) -> std::basic_string { basic_memory_buffer buffer; - vprintf(buffer, to_string_view(fmt), args); + vprintf(buffer, detail::to_string_view(fmt), args); return to_string(buffer); } @@ -578,7 +577,8 @@ template ::value, char_t>> inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string { using context = basic_printf_context_t; - return vsprintf(to_string_view(fmt), fmt::make_format_args(args...)); + return vsprintf(detail::to_string_view(fmt), + fmt::make_format_args(args...)); } template > @@ -587,7 +587,7 @@ inline auto vfprintf( basic_format_args>> args) -> int { basic_memory_buffer buffer; - vprintf(buffer, to_string_view(fmt), args); + vprintf(buffer, detail::to_string_view(fmt), args); size_t size = buffer.size(); return std::fwrite(buffer.data(), sizeof(Char), size, f) < size ? -1 @@ -606,7 +606,7 @@ inline auto vfprintf( template > inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { using context = basic_printf_context_t; - return vfprintf(f, to_string_view(fmt), + return vfprintf(f, detail::to_string_view(fmt), fmt::make_format_args(args...)); } @@ -615,7 +615,7 @@ inline auto vprintf( const S& fmt, basic_format_args>> args) -> int { - return vfprintf(stdout, to_string_view(fmt), args); + return vfprintf(stdout, detail::to_string_view(fmt), args); } /** @@ -630,27 +630,10 @@ inline auto vprintf( template ::value)> inline auto printf(const S& fmt, const T&... args) -> int { return vprintf( - to_string_view(fmt), + detail::to_string_view(fmt), fmt::make_format_args>>(args...)); } -template > -FMT_DEPRECATED auto vfprintf( - std::basic_ostream& os, const S& fmt, - basic_format_args>> args) - -> int { - basic_memory_buffer buffer; - vprintf(buffer, to_string_view(fmt), args); - os.write(buffer.data(), static_cast(buffer.size())); - return static_cast(buffer.size()); -} -template > -FMT_DEPRECATED auto fprintf(std::basic_ostream& os, const S& fmt, - const T&... args) -> int { - return vfprintf(os, to_string_view(fmt), - fmt::make_format_args>(args...)); -} - FMT_MODULE_EXPORT_END FMT_END_NAMESPACE diff --git a/Externals/fmt/include/fmt/ranges.h b/Externals/fmt/include/fmt/ranges.h index edff8f96f7..dea7d60dd6 100755 --- a/Externals/fmt/include/fmt/ranges.h +++ b/Externals/fmt/include/fmt/ranges.h @@ -55,7 +55,7 @@ template class is_std_string_like { template static void check(...); public: - static FMT_CONSTEXPR_DECL const bool value = + static constexpr const bool value = is_string::value || std::is_convertible>::value || !std::is_void(nullptr))>::value; @@ -70,9 +70,9 @@ template class is_map { public: #ifdef FMT_FORMAT_MAP_AS_LIST - static FMT_CONSTEXPR_DECL const bool value = false; + static constexpr const bool value = false; #else - static FMT_CONSTEXPR_DECL const bool value = + static constexpr const bool value = !std::is_void(nullptr))>::value; #endif }; @@ -83,9 +83,9 @@ template class is_set { public: #ifdef FMT_FORMAT_SET_AS_LIST - static FMT_CONSTEXPR_DECL const bool value = false; + static constexpr const bool value = false; #else - static FMT_CONSTEXPR_DECL const bool value = + static constexpr const bool value = !std::is_void(nullptr))>::value && !is_map::value; #endif }; @@ -94,7 +94,7 @@ template struct conditional_helper {}; template struct is_range_ : std::false_type {}; -#if !FMT_MSC_VER || FMT_MSC_VER > 1800 +#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800 # define FMT_DECLTYPE_RETURN(val) \ ->decltype(val) { return val; } \ @@ -174,12 +174,12 @@ template class is_tuple_like_ { template static void check(...); public: - static FMT_CONSTEXPR_DECL const bool value = + static constexpr const bool value = !std::is_void(nullptr))>::value; }; // Check for integer_sequence -#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 +#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900 template using integer_sequence = std::integer_sequence; template using index_sequence = std::index_sequence; @@ -202,6 +202,31 @@ template using make_index_sequence = make_integer_sequence; #endif +template +using tuple_index_sequence = make_index_sequence::value>; + +template ::value> +class is_tuple_formattable_ { + public: + static constexpr const bool value = false; +}; +template class is_tuple_formattable_ { + template + static std::true_type check2(index_sequence, + integer_sequence); + static std::false_type check2(...); + template + static decltype(check2( + index_sequence{}, + integer_sequence< + bool, (is_formattable::type, + C>::value)...>{})) check(index_sequence); + + public: + static constexpr const bool value = + decltype(check(tuple_index_sequence{}))::value; +}; + template void for_each(index_sequence, Tuple&& tup, F&& f) noexcept { using std::get; @@ -221,7 +246,7 @@ template void for_each(Tuple&& tup, F&& f) { for_each(indexes, std::forward(tup), std::forward(f)); } -#if FMT_MSC_VER +#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920 // Older MSVC doesn't get the reference type correctly for arrays. template struct range_reference_type_impl { using type = decltype(*detail::range_begin(std::declval())); @@ -244,6 +269,14 @@ using range_reference_type = template using uncvref_type = remove_cvref_t>; +template +using uncvref_first_type = + remove_cvref_t>().first)>; + +template +using uncvref_second_type = remove_cvref_t< + decltype(std::declval>().second)>; + template OutputIt write_delimiter(OutputIt out) { *out++ = ','; *out++ = ' '; @@ -279,25 +312,51 @@ OutputIt write_range_entry(OutputIt out, const Arg& v) { } // namespace detail template struct is_tuple_like { - static FMT_CONSTEXPR_DECL const bool value = + static constexpr const bool value = detail::is_tuple_like_::value && !detail::is_range_::value; }; +template struct is_tuple_formattable { + static constexpr const bool value = + detail::is_tuple_formattable_::value; +}; + template -struct formatter::value>> { +struct formatter::value && + fmt::is_tuple_formattable::value>> { private: + basic_string_view separator_ = detail::string_literal{}; + basic_string_view opening_bracket_ = + detail::string_literal{}; + basic_string_view closing_bracket_ = + detail::string_literal{}; + // C++11 generic lambda for format(). template struct format_each { template void operator()(const T& v) { - if (i > 0) out = detail::write_delimiter(out); + if (i > 0) out = detail::copy_str(separator, out); out = detail::write_range_entry(out, v); ++i; } int i; typename FormatContext::iterator& out; + basic_string_view separator; }; public: + FMT_CONSTEXPR formatter() {} + + FMT_CONSTEXPR void set_separator(basic_string_view sep) { + separator_ = sep; + } + + FMT_CONSTEXPR void set_brackets(basic_string_view open, + basic_string_view close) { + opening_bracket_ = open; + closing_bracket_ = close; + } + template FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { return ctx.begin(); @@ -307,19 +366,18 @@ struct formatter::value>> { auto format(const TupleT& values, FormatContext& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); - *out++ = '('; - detail::for_each(values, format_each{0, out}); - *out++ = ')'; + out = detail::copy_str(opening_bracket_, out); + detail::for_each(values, format_each{0, out, separator_}); + out = detail::copy_str(closing_bracket_, out); return out; } }; template struct is_range { - static FMT_CONSTEXPR_DECL const bool value = + static constexpr const bool value = detail::is_range_::value && !detail::is_std_string_like::value && - !detail::is_map::value && !std::is_convertible>::value && - !std::is_constructible, T>::value; + !std::is_convertible>::value; }; namespace detail { @@ -350,37 +408,88 @@ using range_formatter_type = conditional_t< template using maybe_const_range = conditional_t::value, const R, R>; + +// Workaround a bug in MSVC 2015 and earlier. +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 +template +struct is_formattable_delayed + : disjunction< + is_formattable>, Char>, + has_fallback_formatter>, Char>> {}; +#endif + } // namespace detail -template -struct formatter< - R, Char, - enable_if_t< - fmt::is_range::value -// Workaround a bug in MSVC 2019 and earlier. -#if !FMT_MSC_VER - && - (is_formattable>, - Char>::value || - detail::has_fallback_formatter< - detail::uncvref_type>, Char>::value) -#endif - >> { +template +struct range_formatter; - using range_type = detail::maybe_const_range; - using formatter_type = - detail::range_formatter_type>; - formatter_type underlying_; +template +struct range_formatter< + T, Char, + enable_if_t>, + disjunction, + detail::has_fallback_formatter>>::value>> { + private: + detail::range_formatter_type underlying_; bool custom_specs_ = false; + basic_string_view separator_ = detail::string_literal{}; + basic_string_view opening_bracket_ = + detail::string_literal{}; + basic_string_view closing_bracket_ = + detail::string_literal{}; + + template + FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, int) + -> decltype(u.set_debug_format()) { + u.set_debug_format(); + } + + template + FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {} + + FMT_CONSTEXPR void maybe_set_debug_format() { + maybe_set_debug_format(underlying_, 0); + } + + public: + FMT_CONSTEXPR range_formatter() {} + + FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type& { + return underlying_; + } + + FMT_CONSTEXPR void set_separator(basic_string_view sep) { + separator_ = sep; + } + + FMT_CONSTEXPR void set_brackets(basic_string_view open, + basic_string_view close) { + opening_bracket_ = open; + closing_bracket_ = close; + } template FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { auto it = ctx.begin(); auto end = ctx.end(); - if (it == end || *it == '}') return it; + if (it == end || *it == '}') { + maybe_set_debug_format(); + return it; + } + + if (*it == 'n') { + set_brackets({}, {}); + ++it; + } + + if (*it == '}') { + maybe_set_debug_format(); + return it; + } if (*it != ':') - FMT_THROW(format_error("no top-level range formatters supported")); + FMT_THROW(format_error("no other top-level range formatters supported")); custom_specs_ = true; ++it; @@ -388,73 +497,100 @@ struct formatter< return underlying_.parse(ctx); } - template - auto format(range_type& range, FormatContext& ctx) const - -> decltype(ctx.out()) { -#ifdef FMT_DEPRECATED_BRACED_RANGES - Char prefix = '{'; - Char postfix = '}'; -#else - Char prefix = detail::is_set::value ? '{' : '['; - Char postfix = detail::is_set::value ? '}' : ']'; -#endif + template + auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { detail::range_mapper> mapper; auto out = ctx.out(); - *out++ = prefix; + out = detail::copy_str(opening_bracket_, out); int i = 0; auto it = detail::range_begin(range); auto end = detail::range_end(range); for (; it != end; ++it) { - if (i > 0) out = detail::write_delimiter(out); - if (custom_specs_) { - ctx.advance_to(out); - out = underlying_.format(mapper.map(*it), ctx); - } else { - out = detail::write_range_entry(out, *it); - } + if (i > 0) out = detail::copy_str(separator_, out); + ; + ctx.advance_to(out); + out = underlying_.format(mapper.map(*it), ctx); ++i; } - *out++ = postfix; + out = detail::copy_str(closing_bracket_, out); return out; } }; -template -struct formatter< - T, Char, - enable_if_t::value -// Workaround a bug in MSVC 2019 and earlier. -#if !FMT_MSC_VER - && (is_formattable, Char>::value || - detail::has_fallback_formatter, - Char>::value) -#endif - >> { - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); +enum class range_format { disabled, map, set, sequence, string, debug_string }; + +namespace detail { +template struct range_format_kind_ { + static constexpr auto value = std::is_same, T>::value + ? range_format::disabled + : is_map::value ? range_format::map + : is_set::value ? range_format::set + : range_format::sequence; +}; + +template +struct range_default_formatter; + +template +using range_format_constant = std::integral_constant; + +template +struct range_default_formatter< + K, R, Char, + enable_if_t<(K == range_format::sequence || K == range_format::map || + K == range_format::set)>> { + using range_type = detail::maybe_const_range; + range_formatter, Char> underlying_; + + FMT_CONSTEXPR range_default_formatter() { init(range_format_constant()); } + + FMT_CONSTEXPR void init(range_format_constant) { + underlying_.set_brackets(detail::string_literal{}, + detail::string_literal{}); } - template < - typename FormatContext, typename U, - FMT_ENABLE_IF( - std::is_same::value, - const T, T>>::value)> - auto format(U& map, FormatContext& ctx) const -> decltype(ctx.out()) { - auto out = ctx.out(); - *out++ = '{'; - int i = 0; - for (const auto& item : map) { - if (i > 0) out = detail::write_delimiter(out); - out = detail::write_range_entry(out, item.first); - *out++ = ':'; - *out++ = ' '; - out = detail::write_range_entry(out, item.second); - ++i; - } - *out++ = '}'; - return out; + FMT_CONSTEXPR void init(range_format_constant) { + underlying_.set_brackets(detail::string_literal{}, + detail::string_literal{}); + underlying_.underlying().set_brackets({}, {}); + underlying_.underlying().set_separator( + detail::string_literal{}); } + + FMT_CONSTEXPR void init(range_format_constant) {} + + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return underlying_.parse(ctx); + } + + template + auto format(range_type& range, FormatContext& ctx) const + -> decltype(ctx.out()) { + return underlying_.format(range, ctx); + } +}; +} // namespace detail + +template +struct range_format_kind + : conditional_t< + is_range::value, detail::range_format_kind_, + std::integral_constant> {}; + +template +struct formatter< + R, Char, + enable_if_t::value != + range_format::disabled> +// Workaround a bug in MSVC 2015 and earlier. +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 + , + detail::is_formattable_delayed +#endif + >::value>> + : detail::range_default_formatter::value, R, + Char> { }; template struct tuple_join_view : detail::view { diff --git a/Externals/fmt/include/fmt/std.h b/Externals/fmt/include/fmt/std.h new file mode 100644 index 0000000000..41d2b2838b --- /dev/null +++ b/Externals/fmt/include/fmt/std.h @@ -0,0 +1,171 @@ +// Formatting library for C++ - formatters for standard library types +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_STD_H_ +#define FMT_STD_H_ + +#include +#include +#include + +#include "ostream.h" + +#if FMT_HAS_INCLUDE() +# include +#endif +// Checking FMT_CPLUSPLUS for warning suppression in MSVC. +#if FMT_CPLUSPLUS >= 201703L +# if FMT_HAS_INCLUDE() +# include +# endif +# if FMT_HAS_INCLUDE() +# include +# endif +#endif + +#ifdef __cpp_lib_filesystem +FMT_BEGIN_NAMESPACE + +namespace detail { + +template +void write_escaped_path(basic_memory_buffer& quoted, + const std::filesystem::path& p) { + write_escaped_string(std::back_inserter(quoted), p.string()); +} +# ifdef _WIN32 +template <> +inline void write_escaped_path(basic_memory_buffer& quoted, + const std::filesystem::path& p) { + auto s = p.u8string(); + write_escaped_string( + std::back_inserter(quoted), + string_view(reinterpret_cast(s.c_str()), s.size())); +} +# endif +template <> +inline void write_escaped_path( + basic_memory_buffer& quoted, + const std::filesystem::path& p) { + write_escaped_string( + std::back_inserter(quoted), p.native()); +} + +} // namespace detail + +template +struct formatter + : formatter> { + template + auto format(const std::filesystem::path& p, FormatContext& ctx) const -> + typename FormatContext::iterator { + basic_memory_buffer quoted; + detail::write_escaped_path(quoted, p); + return formatter>::format( + basic_string_view(quoted.data(), quoted.size()), ctx); + } +}; +FMT_END_NAMESPACE +#endif + +FMT_BEGIN_NAMESPACE +template +struct formatter : basic_ostream_formatter {}; +FMT_END_NAMESPACE + +#ifdef __cpp_lib_variant +FMT_BEGIN_NAMESPACE +template struct formatter { + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template + auto format(const std::monostate&, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + out = detail::write(out, "monostate"); + return out; + } +}; + +namespace detail { + +template +using variant_index_sequence = + std::make_index_sequence::value>; + +// variant_size and variant_alternative check. +template +struct is_variant_like_ : std::false_type {}; +template +struct is_variant_like_::value)>> + : std::true_type {}; + +// formattable element check +template class is_variant_formattable_ { + template + static std::conjunction< + is_formattable, C>...> + check(std::index_sequence); + + public: + static constexpr const bool value = + decltype(check(variant_index_sequence{}))::value; +}; + +template +auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt { + if constexpr (is_string::value) + return write_escaped_string(out, detail::to_string_view(v)); + else if constexpr (std::is_same_v) + return write_escaped_char(out, v); + else + return write(out, v); +} + +} // namespace detail + +template struct is_variant_like { + static constexpr const bool value = detail::is_variant_like_::value; +}; + +template struct is_variant_formattable { + static constexpr const bool value = + detail::is_variant_formattable_::value; +}; + +template +struct formatter< + Variant, Char, + std::enable_if_t, is_variant_formattable>>> { + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template + auto format(const Variant& value, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + + out = detail::write(out, "variant("); + std::visit( + [&](const auto& v) { + out = detail::write_variant_alternative(out, v); + }, + value); + *out++ = ')'; + return out; + } +}; +FMT_END_NAMESPACE +#endif + +#endif // FMT_STD_H_ diff --git a/Externals/fmt/include/fmt/xchar.h b/Externals/fmt/include/fmt/xchar.h index 9b57815d08..3b5bc15ca0 100644 --- a/Externals/fmt/include/fmt/xchar.h +++ b/Externals/fmt/include/fmt/xchar.h @@ -9,7 +9,6 @@ #define FMT_XCHAR_H_ #include -#include #include "format.h" @@ -30,9 +29,11 @@ using wmemory_buffer = basic_memory_buffer; #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 // Workaround broken conversion on older gcc. template using wformat_string = wstring_view; +inline auto runtime(wstring_view s) -> wstring_view { return s; } #else template using wformat_string = basic_format_string...>; +inline auto runtime(wstring_view s) -> basic_runtime { return {{s}}; } #endif template <> struct is_char : std::true_type {}; @@ -47,12 +48,7 @@ constexpr format_arg_store make_wformat_args( } inline namespace literals { -constexpr auto operator"" _format(const wchar_t* s, size_t n) - -> detail::udl_formatter { - return {{s, n}}; -} - -#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS constexpr detail::udl_arg operator"" _a(const wchar_t* s, size_t) { return {s}; } @@ -87,12 +83,18 @@ auto vformat(basic_string_view format_str, return to_string(buffer); } +template +auto format(wformat_string fmt, T&&... args) -> std::wstring { + return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...)); +} + // Pass char_t as a default template parameter instead of using // std::basic_string> to reduce the symbol size. template , - FMT_ENABLE_IF(!std::is_same::value)> + FMT_ENABLE_IF(!std::is_same::value && + !std::is_same::value)> auto format(const S& format_str, Args&&... args) -> std::basic_string { - return vformat(to_string_view(format_str), + return vformat(detail::to_string_view(format_str), fmt::make_format_args>(args...)); } @@ -103,7 +105,7 @@ inline auto vformat( const Locale& loc, const S& format_str, basic_format_args>> args) -> std::basic_string { - return detail::vformat(loc, to_string_view(format_str), args); + return detail::vformat(loc, detail::to_string_view(format_str), args); } template ::value)> inline auto format(const Locale& loc, const S& format_str, Args&&... args) -> std::basic_string { - return detail::vformat(loc, to_string_view(format_str), + return detail::vformat(loc, detail::to_string_view(format_str), fmt::make_format_args>(args...)); } @@ -123,7 +125,7 @@ auto vformat_to(OutputIt out, const S& format_str, basic_format_args>> args) -> OutputIt { auto&& buf = detail::get_buffer(out); - detail::vformat_to(buf, to_string_view(format_str), args); + detail::vformat_to(buf, detail::to_string_view(format_str), args); return detail::get_iterator(buf); } @@ -132,20 +134,10 @@ template ::value&& detail::is_exotic_char::value)> inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { - return vformat_to(out, to_string_view(fmt), + return vformat_to(out, detail::to_string_view(fmt), fmt::make_format_args>(args...)); } -template ::value)> -FMT_DEPRECATED auto format_to(basic_memory_buffer& buf, - const S& format_str, Args&&... args) -> - typename buffer_context::iterator { - detail::vformat_to(buf, to_string_view(format_str), - fmt::make_format_args>(args...), {}); - return detail::buffer_appender(buf); -} - template , FMT_ENABLE_IF(detail::is_output_iterator::value&& @@ -155,7 +147,8 @@ inline auto vformat_to( OutputIt out, const Locale& loc, const S& format_str, basic_format_args>> args) -> OutputIt { auto&& buf = detail::get_buffer(out); - vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc)); + vformat_to(buf, detail::to_string_view(format_str), args, + detail::locale_ref(loc)); return detail::get_iterator(buf); } @@ -190,7 +183,7 @@ template ::value)> inline auto format_to_n(OutputIt out, size_t n, const S& fmt, const Args&... args) -> format_to_n_result { - return vformat_to_n(out, n, to_string_view(fmt), + return vformat_to_n(out, n, detail::to_string_view(fmt), fmt::make_format_args>(args...)); } @@ -198,7 +191,7 @@ template , FMT_ENABLE_IF(detail::is_exotic_char::value)> inline auto formatted_size(const S& fmt, Args&&... args) -> size_t { detail::counting_buffer buf; - detail::vformat_to(buf, to_string_view(fmt), + detail::vformat_to(buf, detail::to_string_view(fmt), fmt::make_format_args>(args...)); return buf.count(); } diff --git a/Externals/fmt/src/format.cc b/Externals/fmt/src/format.cc index 70206cf496..99b7e9dd47 100755 --- a/Externals/fmt/src/format.cc +++ b/Externals/fmt/src/format.cc @@ -10,96 +10,38 @@ FMT_BEGIN_NAMESPACE namespace detail { -// DEPRECATED! -template struct basic_data { - FMT_API static constexpr const char digits[100][2] = { - {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, - {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, - {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, - {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, - {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, - {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, - {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'}, - {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, - {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, - {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, - {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, - {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'}, - {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, - {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, - {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, - {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, - {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; - FMT_API static constexpr const char hex_digits[] = "0123456789abcdef"; - FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '}; - FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1, - 0}; - FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1, - 0}; - FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', - 0x1000000u | ' '}; -}; - -#ifdef FMT_SHARED -// Required for -flto, -fivisibility=hidden and -shared to work -extern template struct basic_data; -#endif - -#if __cplusplus < 201703L -// DEPRECATED! These are here only for ABI compatiblity. -template constexpr const char basic_data::digits[][2]; -template constexpr const char basic_data::hex_digits[]; -template constexpr const char basic_data::signs[]; -template constexpr const char basic_data::left_padding_shifts[]; -template -constexpr const char basic_data::right_padding_shifts[]; -template constexpr const unsigned basic_data::prefixes[]; -#endif - -template FMT_API dragonbox::decimal_fp dragonbox::to_decimal( - float x) noexcept; -template FMT_API dragonbox::decimal_fp dragonbox::to_decimal( - double x) noexcept; -} // namespace detail - -// Workaround a bug in MSVC2013 that prevents instantiation of format_float. -int (*instantiate_format_float)(double, int, detail::float_specs, - detail::buffer&) = detail::format_float; +template FMT_API auto dragonbox::to_decimal(float x) noexcept + -> dragonbox::decimal_fp; +template FMT_API auto dragonbox::to_decimal(double x) noexcept + -> dragonbox::decimal_fp; #ifndef FMT_STATIC_THOUSANDS_SEPARATOR -template FMT_API detail::locale_ref::locale_ref(const std::locale& loc); -template FMT_API std::locale detail::locale_ref::get() const; +template FMT_API locale_ref::locale_ref(const std::locale& loc); +template FMT_API auto locale_ref::get() const -> std::locale; #endif // Explicit instantiations for char. -template FMT_API auto detail::thousands_sep_impl(locale_ref) +template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; -template FMT_API char detail::decimal_point_impl(locale_ref); +template FMT_API auto decimal_point_impl(locale_ref) -> char; -template FMT_API void detail::buffer::append(const char*, const char*); +template FMT_API void buffer::append(const char*, const char*); // DEPRECATED! // There is no correspondent extern template in format.h because of // incompatibility between clang and gcc (#2377). -template FMT_API void detail::vformat_to( - detail::buffer&, string_view, - basic_format_args, detail::locale_ref); - -template FMT_API int detail::format_float(double, int, detail::float_specs, - detail::buffer&); -template FMT_API int detail::format_float(long double, int, detail::float_specs, - detail::buffer&); +template FMT_API void vformat_to(buffer&, string_view, + basic_format_args, + locale_ref); // Explicit instantiations for wchar_t. -template FMT_API auto detail::thousands_sep_impl(locale_ref) +template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; -template FMT_API wchar_t detail::decimal_point_impl(locale_ref); +template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; -template FMT_API void detail::buffer::append(const wchar_t*, - const wchar_t*); - -template struct detail::basic_data; +template FMT_API void buffer::append(const wchar_t*, const wchar_t*); +} // namespace detail FMT_END_NAMESPACE diff --git a/Externals/fmt/src/os.cc b/Externals/fmt/src/os.cc index faa84c4948..f388ead019 100644 --- a/Externals/fmt/src/os.cc +++ b/Externals/fmt/src/os.cc @@ -51,10 +51,6 @@ # include #endif -#ifdef fileno -# undef fileno -#endif - namespace { #ifdef _WIN32 // Return type of read and write functions. @@ -173,7 +169,7 @@ void detail::format_windows_error(detail::buffer& out, int error_code, if (msg) { utf16_to_utf8 utf8_message; if (utf8_message.convert(msg) == ERROR_SUCCESS) { - format_to(buffer_appender(out), "{}: {}", message, utf8_message); + fmt::format_to(buffer_appender(out), "{}: {}", message, utf8_message); return; } } @@ -206,11 +202,8 @@ void buffered_file::close() { if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); } -// A macro used to prevent expansion of fileno on broken versions of MinGW. -#define FMT_ARGS - -int buffered_file::fileno() const { - int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); +int buffered_file::descriptor() const { + int fd = FMT_POSIX_CALL(fileno(file_)); if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor")); return fd; } diff --git a/Externals/fmt/support/appveyor-build.py b/Externals/fmt/support/appveyor-build.py deleted file mode 100755 index 6544610326..0000000000 --- a/Externals/fmt/support/appveyor-build.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python -# Build the project on AppVeyor. - -import os -from subprocess import check_call - -build = os.environ['BUILD'] -config = os.environ['CONFIGURATION'] -platform = os.environ['PLATFORM'] -path = os.environ['PATH'] -image = os.environ['APPVEYOR_BUILD_WORKER_IMAGE'] -jobid = os.environ['APPVEYOR_JOB_ID'] -cmake_command = ['cmake', '-DFMT_PEDANTIC=ON', '-DCMAKE_BUILD_TYPE=' + config, '..'] -if build == 'mingw': - cmake_command.append('-GMinGW Makefiles') - build_command = ['mingw32-make', '-j4'] - test_command = ['mingw32-make', 'test'] - # Remove the path to Git bin directory from $PATH because it breaks - # MinGW config. - path = path.replace(r'C:\Program Files (x86)\Git\bin', '') - os.environ['PATH'] = r'C:\MinGW\bin;' + path -else: - # Add MSBuild 14.0 to PATH as described in - # http://help.appveyor.com/discussions/problems/2229-v140-not-found-on-vs2105rc. - os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\15.0\Bin;' + path - if image == 'Visual Studio 2019': - generator = 'Visual Studio 16 2019' - if platform == 'x64': - cmake_command.extend(['-A', 'x64']) - else: - if image == 'Visual Studio 2015': - generator = 'Visual Studio 14 2015' - elif image == 'Visual Studio 2017': - generator = 'Visual Studio 15 2017' - if platform == 'x64': - generator += ' Win64' - cmake_command.append('-G' + generator) - build_command = ['cmake', '--build', '.', '--config', config, '--', '/m:4'] - test_command = ['ctest', '-C', config] - -check_call(cmake_command) -check_call(build_command) -check_call(test_command) diff --git a/Externals/fmt/support/appveyor.yml b/Externals/fmt/support/appveyor.yml deleted file mode 100755 index 64ca58a949..0000000000 --- a/Externals/fmt/support/appveyor.yml +++ /dev/null @@ -1,31 +0,0 @@ -configuration: - - Debug - - Release - -clone_depth: 1 - -image: - - Visual Studio 2015 - -platform: - - x64 - -environment: - CTEST_OUTPUT_ON_FAILURE: 1 - MSVC_DEFAULT_OPTIONS: ON - BUILD: msvc - -before_build: - - mkdir build - - cd build - -build_script: - - python ../support/appveyor-build.py - -on_failure: - - appveyor PushArtifact Testing/Temporary/LastTest.log - - appveyor AddTest test - -# Uncomment this to debug AppVeyor failures. -#on_finish: -# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/Externals/fmt/support/bazel/.bazelversion b/Externals/fmt/support/bazel/.bazelversion index 0062ac9718..ac14c3dfaa 100644 --- a/Externals/fmt/support/bazel/.bazelversion +++ b/Externals/fmt/support/bazel/.bazelversion @@ -1 +1 @@ -5.0.0 +5.1.1 diff --git a/Externals/fmt/support/bazel/BUILD.bazel b/Externals/fmt/support/bazel/BUILD.bazel index 3380bbcab2..0857658902 100644 --- a/Externals/fmt/support/bazel/BUILD.bazel +++ b/Externals/fmt/support/bazel/BUILD.bazel @@ -11,18 +11,17 @@ cc_library( "include/fmt/color.h", "include/fmt/compile.h", "include/fmt/core.h", - "include/fmt/format.h", "include/fmt/format-inl.h", - "include/fmt/locale.h", + "include/fmt/format.h", "include/fmt/os.h", "include/fmt/ostream.h", "include/fmt/printf.h", "include/fmt/ranges.h", + "include/fmt/std.h", "include/fmt/xchar.h", ], includes = [ - "include", - "src", + "include", ], strip_include_prefix = "include", visibility = ["//visibility:public"], diff --git a/Externals/fmt/support/cmake/fmt-config.cmake.in b/Externals/fmt/support/cmake/fmt-config.cmake.in index 71e302860b..bc1684f245 100755 --- a/Externals/fmt/support/cmake/fmt-config.cmake.in +++ b/Externals/fmt/support/cmake/fmt-config.cmake.in @@ -1,4 +1,7 @@ @PACKAGE_INIT@ -include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake) +if (NOT TARGET fmt::fmt) + include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake) +endif () + check_required_components(fmt) diff --git a/Externals/fmt/support/manage.py b/Externals/fmt/support/manage.py index bece4566f5..d2afcee92d 100755 --- a/Externals/fmt/support/manage.py +++ b/Externals/fmt/support/manage.py @@ -183,6 +183,12 @@ def update_site(env): with rewrite(index) as b: b.data = b.data.replace( 'doc/latest/index.html#format-string-syntax', 'syntax.html') + # Fix issues in syntax.rst. + index = os.path.join(target_doc_dir, 'syntax.rst') + with rewrite(index) as b: + b.data = b.data.replace( + '..productionlist:: sf\n', '.. productionlist:: sf\n ') + b.data = b.data.replace('Examples:\n', 'Examples::\n') # Build the docs. html_dir = os.path.join(env.build_dir, 'html') if os.path.exists(html_dir):