From f8e590b1fe8546957dd09945d013d9a1e22346d8 Mon Sep 17 00:00:00 2001 From: Marco Auer Date: Thu, 24 Mar 2022 08:38:48 +0100 Subject: [PATCH] Squashed 'lib/fmt/' changes from 17dda583..b6f4ceae REVERT: 17dda583 constexpr -> const for portability REVERT: 7ffe87c0 Fix docs REVERT: 3c4273dd Simplify UDL REVERT: 36d95c9f Fix docs REVERT: 44abd1f4 Update signatures in docs and ostream.h REVERT: db745986 Workaround broken std::numeric_limits REVERT: 8271e43e Improve __float128 support and use constexpr REVERT: 3f9b7433 Improve __float128 support REVERT: 71778e8b Specialize float_info for __float128 REVERT: f024565c Improve exponent handling in Dragon REVERT: e7f31f5c Cleanup format_dragon REVERT: 3c61799f Cleanup fuzzing mode REVERT: 4e39e130 Remove xchar.h include from ostream.h REVERT: ac0d9d5f Issue #2816: also strip named-arg for the fallback formatter REVERT: 4ad90578 Fix #2818: diagnose unformattable arguments in unpacked case REVERT: 17ba99c1 Fix #2817: add compile-time checking to ostream overloads of fmt::print REVERT: 3d19be28 Fix #2816: strip named argument wrappers for compile-time checking REVERT: c076a54a Move snprintf_float to format.h REVERT: 0419d238 Add FMT_USE_FLOAT128 REVERT: 69396347 Update color.h (#2815) REVERT: c51604a0 Reduce the number of configs REVERT: 587dc994 Remove windows-2016 env no longer suppported by GA REVERT: 1f3d44b8 Update std::tm/chrono docs REVERT: bc654faf Add is_floating_point that works with __float128 REVERT: 26bffce6 Simplify basic_memory_buffer REVERT: ed18ca3e Implement isnan REVERT: a204b8dd Add initial __float128 support REVERT: b6b003b0 Cleanup test REVERT: f2543b0a Add initial support for 128-bit floats REVERT: 72f48756 Simplify float_info REVERT: f91f61cd Reuse num_significand_bits REVERT: 9a1beab5 Workaround Windows API garbage REVERT: a8fe8bec Fix compilation error for ranges with ADL `begin`/`end` (#2807) REVERT: f6bcb25e Remove extra dot REVERT: b4a4189d Fix handling of implicit bit REVERT: 32d477e5 Add `styled` in documentation (#2805) REVERT: 0b7c045a Simplify _cf REVERT: c10fffec Make _cf visible in the doc build REVERT: dcfbe4a7 Document output_file default behavior correctly (#2803) REVERT: 8c9bc070 Implement styled arguments (#2793) REVERT: 5bc39d36 Eliminate intel compiler warnings (#2802) REVERT: e3d688e7 Fix warning C4251: class fmt::v8::file needs to have dll-interface (#2797) REVERT: 8d4f3e91 Update docs REVERT: 0cef1f81 Fixing formatting of certain kinds of ranges of ranges. (#2787) REVERT: 5c0d6564 Fix apt install REVERT: d416a995 Update README.rst REVERT: 3f67a124 Update README.rst REVERT: cc57e359 Update godbolt link in the readme (#2789) REVERT: 86477f7e Fix size computation REVERT: 0742606f Fix Conversion Warning (#2782) REVERT: 1ba69fb5 Remove snprintf FP fallback REVERT: ea6f0bf0 Minor cleanup REVERT: 1a18a2f3 Fixing "C4127: conditional expression is constant" Visual Studio 2022 warning in pedantic mode (#2783) REVERT: 4fcacea3 Parameterized fp on significand type REVERT: cf940ae8 Simplify to_decimal REVERT: 70dc3de0 Update format.h REVERT: cbc59ca8 Clear moved from memory buffer REVERT: ea3d326c Fix clang -Wliteral-range warning (#2779) REVERT: aad44f28 Add fmt::enums::format_as REVERT: 1319719a Add underlying_t REVERT: af5d8004 Limit Dragonbox to supported FP formats REVERT: 7b964209 Remove unused include REVERT: a0b43bfa Add support for 96-bit long double REVERT: 2c8cd2db Fix handling of zero precision REVERT: b6d56170 Remove unnecessary inline REVERT: 05432e57 Use consistent indentation REVERT: 47da218c Remove uintptr_fallback REVERT: 4ddab890 Merge accumulator into int128_fallback REVERT: d38f72af Refactor fallback ints REVERT: 15c2a3ba int128_t -> int128_opt REVERT: 532a69a6 Fix handling of 96-bit long double with -m32 REVERT: d8e1dd4a improve installing headers REVERT: ae25f796 add ability to build Apple framework using CMAKE_FRAMEWORK REVERT: ce93a66d Implement a fallback uint128_t REVERT: 6a134640 Include 128-bit with other signed integers in specifier check REVERT: 70de324a Apply 2746 fix for NVidia compiler also (#2770) REVERT: a1ea3e01 Move built-in formatter specialization to core REVERT: 161059dd Add support for extended precision FP REVERT: c4c6b42d Bump version REVERT: 21785040 Fix markup REVERT: 2b6f7fc7 Add partial support for extended precision FP REVERT: 0a24a071 Clz builtin may be not constexpr (Issue #2761) (#2762) REVERT: ba6f89c7 Update .bazelversion (#2766) REVERT: 5594edaf Address https://github.com/fmtlib/fmt/issues/2763 (#2765) REVERT: 10e3b83a Replace ``make_args_checked`` with ``make_format_args`` (#2760) REVERT: c48353cb Update docs REVERT: 083510f0 Add FMT_CONSTEXPR to rotr instead REVERT: dba99bc8 Revert adding constexpr to rotr to satisfy C++11 compilers REVERT: c04af4bf Simplify remove_trailing_zeros REVERT: b348caa9 Remove some C-style casts for consistency REVERT: c8bd1e64 Simplify remove_trailing_zeros REVERT: 9b23e9dc Fix wrong comment/refer to a correct reference REVERT: 69f2c550 Remove std:: infront of uint32_t/64_t & add constexpr to rotr REVERT: 9b62310f Fix some conversion issues REVERT: 08d12f31 Fix typo REVERT: dbddb1d0 Remove literal separator to satisfy some compilers REVERT: 7dbe3dcd Recover log10_2_significand REVERT: 10642e60 Optimize remove_trailing_zeros REVERT: 7b4323e1 Add rotr REVERT: f1bd6f77 Check r < deltai first, because that is the major branch chosen for short inputs REVERT: 5d8eb6a1 Reflect the new paper - Change constants appearing in log & division computations - Rename beta_minus_1 to beta REVERT: 8e2e4d40 Suppress a gcc warning REVERT: a44716f5 Workaround to Intel compiler (#2758) REVERT: c71b0701 Add missing const qualifier (#2755) REVERT: ecd6022c Update docs REVERT: afbcf1e8 Remove legacy C locale wrapper REVERT: 90325d09 Fix stored type detection REVERT: e2ba01fc Fix overload ambiguity in print REVERT: 17b362f7 Simplify ostream opt-in API REVERT: a5a7e3a2 Update docs REVERT: f055ebbd Make ostream operators opt in to reduce the risk of ODR violations REVERT: 8a21e328 Remove problematic constructibility check REVERT: 31e743d0 Don't use ostream for types convertible to string_view REVERT: 35c0286c Simplify byte handling REVERT: c7173a36 Drop :: and fix formatting REVERT: 3e8372b9 qualify unqualified calls to format in compile.h (#2742) REVERT: a34a97cc Supporting ? as a string presentation type (#2674) REVERT: ae1aaaee Fix access mode of files created (#2530) (#2733) REVERT: 1557ab76 Add format_as for enums REVERT: b00a1eac Fixes NVIDIA HPC compiler and Intel ICC compatibility (#2732) REVERT: a7aecbfc Remove an old mingw workaround REVERT: dfcc730c Making target_compile_options PRIVATE, fix #2726, fix #2507 REVERT: f7a809be Clarify the choice of magic numbers and compute the most magic one REVERT: 09fde7f4 Add fmt::underlying for enum classes REVERT: 0014024a Don't rely on transitive includes REVERT: c2850055 FMT_NOEXCEPT -> noexcept REVERT: 6240d020 Improve comments REVERT: 925b744a Improve comments REVERT: 22b14ff2 Simplify cache recovery REVERT: 3dc26b44 Make a fallback path more compiler-friendly REVERT: 2e4038bf Simplify lines with __builtin_addcll and friends REVERT: 76336b4f Replace noexcept with FMT_NOEXCEPT REVERT: 91819834 Fix syntax errors REVERT: 74097a14 Remove now-unused stuffs REVERT: 21a1c533 Fix typo REVERT: 04eea0f0 Remove now-unused stuffs REVERT: 35a468ed Simplify integer checks REVERT: 1882a7a2 Replace Dragonbox cache which allows simpler cache recovery & integer checks REVERT: f4dd1b1b Simplify Dragonbox Step 3. REVERT: 70561ed1 Minimize the usage of built-in 128-bit ints It usually generates slower code than manual handling. REVERT: cdf1a3b5 Fix codecvt warning (#2408) (#2725) REVERT: b8b037e9 Fix -Wconversion warning (#2724) REVERT: 5985f0a7 Fix overflow for chrono durations (#2722) REVERT: 8f8a1a02 Fix handling of formattable types implicitly convertible to pointers REVERT: b02e5af5 fmt::join support FMT_COMPILE (#2720) REVERT: 58fb7823 Improve docs REVERT: 4fe6129d Fix FMT_NOEXCEPT definition REVERT: c056a009 Docs: Fix link to "Compile-time Format String Checks" section (#2712) REVERT: 7c12118c Deprecate buffered_file::fileno REVERT: 2a09d468 Use noexcept unconditionally REVERT: a126b4d8 Check if right shift is arithmetic REVERT: 9ff91b18 Simplify write_fractional_seconds REVERT: d9f045fb Fix a UB in chrono REVERT: c06bef72 Adding comments for range formatting. (#2706) REVERT: 3c98f1a4 Comment style REVERT: 6e0f1399 Supporting nested format specs for ranges. (#2673) REVERT: 0102101a Make colored print handle UTF-8 (#2701) REVERT: 4ac5269b Update ChangeLog.rst git-subtree-dir: lib/fmt git-subtree-split: b6f4ceaed0a0a24ccf575fab6c56dd50ccf6f1a9 --- .github/workflows/linux.yml | 1 - .github/workflows/windows.yml | 15 +- CMakeLists.txt | 27 +- ChangeLog.rst | 2 +- README.rst | 13 +- doc/api.rst | 50 +- doc/build.py | 1 - doc/index.rst | 2 +- doc/syntax.rst | 35 +- include/fmt/args.h | 8 +- include/fmt/chrono.h | 76 +- include/fmt/color.h | 164 +- include/fmt/compile.h | 75 +- include/fmt/core.h | 288 ++-- include/fmt/format-inl.h | 1890 ++++++++++++------------ include/fmt/format.h | 885 ++++------- include/fmt/os.h | 98 +- include/fmt/ostream.h | 46 +- include/fmt/ranges.h | 411 ++++-- include/fmt/xchar.h | 26 +- src/format.cc | 27 +- src/os.cc | 29 +- support/bazel/.bazelversion | 2 +- support/printable.py | 2 +- test/CMakeLists.txt | 5 +- test/chrono-test.cc | 7 - test/color-test.cc | 6 - test/compile-error-test/CMakeLists.txt | 26 - test/compile-test.cc | 5 - test/core-test.cc | 52 +- test/format-impl-test.cc | 52 +- test/format-test.cc | 165 +-- test/gtest-extra.cc | 2 +- test/gtest-extra.h | 2 +- test/module-test.cc | 8 + test/ostream-test.cc | 69 +- test/posix-mock-test.cc | 81 + test/printf-test.cc | 5 + test/ranges-test.cc | 41 +- test/xchar-test.cc | 22 +- 40 files changed, 2263 insertions(+), 2458 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 5467cd8f..7f8500ef 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -52,7 +52,6 @@ jobs: - name: Create Build Environment run: | ${{matrix.install}} - sudo apt update sudo apt install locales-all cmake -E make_directory ${{runner.workspace}}/build diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c060f8b9..ece0372b 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -7,21 +7,26 @@ jobs: runs-on: ${{matrix.os}} strategy: matrix: - # windows-2019 has MSVC 2019 installed: - # https://github.com/actions/virtual-environments. - os: [windows-2019] + # windows-2016 and windows-2019 have MSVC 2017 and 2019 installed + # respectively: https://github.com/actions/virtual-environments. + os: [windows-2016, windows-2019] platform: [Win32, x64] build_type: [Debug, Release] standard: [11, 17, 20] include: - - os: windows-2019 + - os: windows-2016 platform: Win32 build_type: Debug shared: -DBUILD_SHARED_LIBS=ON exclude: + - os: windows-2016 + platform: Win32 + - os: windows-2016 + standard: 17 + - os: windows-2016 + standard: 20 - os: windows-2019 standard: 11 - platform: Win32 - os: windows-2019 standard: 20 platform: Win32 diff --git a/CMakeLists.txt b/CMakeLists.txt index b9e5c58b..3bf80f80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -209,6 +209,18 @@ if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio") ${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*") endif () +set(strtod_l_headers stdlib.h) +if (APPLE) + set(strtod_l_headers ${strtod_l_headers} xlocale.h) +endif () + +include(CheckSymbolExists) +if (WIN32) + check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L) +else () + check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L) +endif () + function(add_headers VAR) set(headers ${${VAR}}) foreach (header ${ARGN}) @@ -232,6 +244,17 @@ endif () add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst) add_library(fmt::fmt ALIAS fmt) +if (HAVE_STRTOD_L) + target_compile_definitions(fmt PUBLIC FMT_LOCALE) +endif () + +if (MINGW) + check_cxx_compiler_flag("-Wa,-mbig-obj" FMT_HAS_MBIG_OBJ) + if (${FMT_HAS_MBIG_OBJ}) + target_compile_options(fmt PUBLIC "-Wa,-mbig-obj") + endif() +endif () + if (FMT_WERROR) target_compile_options(fmt PRIVATE ${WERROR_FLAG}) endif () @@ -252,7 +275,6 @@ set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.") set_target_properties(fmt PROPERTIES VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR} - PUBLIC_HEADER "${FMT_HEADERS}" DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}") # Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target @@ -330,8 +352,6 @@ if (FMT_INSTALL) install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} LIBRARY DESTINATION ${FMT_LIB_DIR} ARCHIVE DESTINATION ${FMT_LIB_DIR} - PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt" - FRAMEWORK DESTINATION "." RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) # Use a namespace because CMake provides better diagnostics for namespaced @@ -348,6 +368,7 @@ if (FMT_INSTALL) install(FILES $ DESTINATION ${FMT_LIB_DIR} OPTIONAL) + install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt") install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") endif () diff --git a/ChangeLog.rst b/ChangeLog.rst index 653002b9..18b84c7d 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -6,7 +6,7 @@ `#2696 `_). Thanks `@saraedum (Julian RĂ¼th) `_. -* Fixed chrono formatting on big endian systems +* Fixed chorno formatting on big endian systems (`#2698 `_, `#2699 `_). Thanks `@phprus (Vladislav Shchapov) `_ and diff --git a/README.rst b/README.rst index feb4ab5e..394f28d9 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,5 @@ -.. image:: https://user-images.githubusercontent.com/ - 576385/156254208-f5b743a9-88cf-439d-b0c0-923d53e8d551.png - :width: 25% - :alt: {fmt} +{fmt} +===== .. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux @@ -28,8 +26,9 @@ **{fmt}** is an open-source formatting library providing a fast and safe alternative to C stdio and C++ iostreams. -If you like this project, please consider donating to one of the funds that -help victims of the war in Ukraine: https://www.stopputin.net/. +If you like this project, please consider donating to the BYSOL +Foundation that helps victims of political repressions in Belarus: +https://bysol.org/en/bs/general/. `Documentation `__ @@ -124,7 +123,7 @@ Output:: Default format: 42s 100ms strftime-like format: 03:15:30 -**Print a container** (`run `_) +**Print a container** (`run `_) .. code:: c++ diff --git a/doc/api.rst b/doc/api.rst index 90634d0f..93fb3faf 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -113,7 +113,8 @@ binary footprint, for example (https://godbolt.org/z/oba4Mc): template void log(const char* file, int line, const S& format, Args&&... args) { - vlog(file, line, format, fmt::make_format_args(args...)); + vlog(file, line, format, + fmt::make_args_checked(format, args...)); } #define MY_LOG(format, ...) \ @@ -124,6 +125,8 @@ binary footprint, for example (https://godbolt.org/z/oba4Mc): Note that ``vlog`` is not parameterized on argument types which improves compile times and reduces binary code size compared to a fully parameterized version. +.. doxygenfunction:: fmt::make_args_checked(const S&, const remove_reference_t&...) + .. doxygenfunction:: fmt::make_format_args(const Args&...) .. doxygenclass:: fmt::format_arg_store @@ -179,11 +182,6 @@ functions and locale support. Formatting User-defined Types ----------------------------- -The {fmt} library provides formatters for many standard C++ types. -See :ref:`fmt/ranges.h ` for ranges and tuples including standard -containers such as ``std::vector`` and :ref:`fmt/chrono.h ` for date -and time formatting. - To make a user-defined type formattable, specialize the ``formatter`` struct template and implement ``parse`` and ``format`` methods:: @@ -224,7 +222,7 @@ template and implement ``parse`` and ``format`` methods:: // Formats the point p using the parsed format specification (presentation) // stored in this formatter. template - auto format(const point& p, FormatContext& ctx) const -> decltype(ctx.out()) { + auto format(const point& p, FormatContext& ctx) -> decltype(ctx.out()) { // ctx.out() is an output iterator to write to. return presentation == 'f' ? format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y) @@ -246,7 +244,7 @@ example:: template <> struct fmt::formatter: formatter { // parse is inherited from formatter. template - auto format(color c, FormatContext& ctx) const { + auto format(color c, FormatContext& ctx) { string_view name = "unknown"; switch (c) { case color::red: name = "red"; break; @@ -284,7 +282,7 @@ You can also write a formatter for a hierarchy of classes:: struct fmt::formatter::value, char>> : fmt::formatter { template - auto format(const A& a, FormatCtx& ctx) const { + auto format(const A& a, FormatCtx& ctx) { return fmt::formatter::format(a.name(), ctx); } }; @@ -309,7 +307,7 @@ The following user-defined literals are defined in ``fmt/format.h``. .. doxygenfunction:: operator""_format(const char *s, size_t n) -> detail::udl_formatter -.. doxygenfunction:: operator""_a() +.. doxygenfunction:: operator""_a(const char *s, size_t) -> detail::udl_arg Utilities --------- @@ -318,8 +316,6 @@ Utilities .. doxygenfunction:: fmt::ptr(const std::unique_ptr &p) -> const void* .. doxygenfunction:: fmt::ptr(const std::shared_ptr &p) -> const void* -.. doxygenfunction:: fmt::underlying(Enum e) -> typename std::underlying_type::type - .. doxygenfunction:: fmt::to_string(const T &value) -> std::string .. doxygenfunction:: fmt::to_string_view(const Char *s) -> basic_string_view @@ -450,19 +446,16 @@ The format syntax is described in :ref:`chrono-specs`. Format string compilation ========================= -``fmt/compile.h`` provides format string compilation enabled via the -``FMT_COMPILE`` macro or the ``_cf`` user-defined literal. Format strings -marked with ``FMT_COMPILE`` or ``_cf`` are parsed, checked and converted into -efficient formatting code at compile-time. This supports arguments of built-in -and string types as well as user-defined types with ``constexpr`` ``parse`` -functions in their ``formatter`` specializations. Format string compilation can -generate more binary code compared to the default API and is only recommended in -places where formatting is a performance bottleneck. +``fmt/compile.h`` provides format string compilation support when using +``FMT_COMPILE``. Format strings are parsed, checked and converted into efficient +formatting code at compile-time. This supports arguments of built-in and string +types as well as user-defined types with ``constexpr`` ``parse`` functions in +their ``formatter`` specializations. Format string compilation can generate more +binary code compared to the default API and is only recommended in places where +formatting is a performance bottleneck. .. doxygendefine:: FMT_COMPILE -.. doxygenfunction:: operator""_cf() - .. _color-api: Terminal color and text style @@ -476,8 +469,6 @@ Terminal color and text style .. doxygenfunction:: bg(detail::color_type) -.. doxygenfunction:: styled(const T& value, text_style ts) - .. _os-api: System APIs @@ -495,9 +486,7 @@ System APIs ======================== ``fmt/ostream.h`` provides ``std::ostream`` support including formatting of -user-defined types that have an overloaded insertion operator (``operator<<``). -In order to make a type formattable via ``std::ostream`` you should provide a -``formatter`` specialization inherited from ``ostream_formatter``:: +user-defined types that have an overloaded insertion operator (``operator<<``):: #include @@ -511,12 +500,13 @@ In order to make a type formattable via ``std::ostream`` you should provide a } }; - 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" -.. doxygenfunction:: print(std::ostream &os, format_string fmt, T&&... args) +{fmt} only supports insertion operators that are defined in the same namespaces +as the types they format and can be found with the argument-dependent lookup. + +.. doxygenfunction:: print(std::basic_ostream &os, const S &format_str, Args&&... args) .. _printf-api: diff --git a/doc/build.py b/doc/build.py index 953e9006..ae1ccfc8 100755 --- a/doc/build.py +++ b/doc/build.py @@ -65,7 +65,6 @@ def build_docs(version='dev', **kwargs): FMT_USE_RVALUE_REFERENCES=1 \ FMT_USE_USER_DEFINED_LITERALS=1 \ FMT_USE_ALIAS_TEMPLATES=1 \ - FMT_USE_NONTYPE_TEMPLATE_PARAMETERS=1 \ FMT_API= \ "FMT_BEGIN_NAMESPACE=namespace fmt {{" \ "FMT_END_NAMESPACE=}}" \ diff --git a/doc/index.rst b/doc/index.rst index d5c4fa5f..92221225 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -101,7 +101,7 @@ The code format(FMT_STRING("The answer is {:d}"), "forty-two"); reports a compile-time error on compilers that support relaxed ``constexpr``. -See `here `_ for details. +See `here `_ for details. The following code diff --git a/doc/syntax.rst b/doc/syntax.rst index 9bf8dba7..9d3cb57c 100644 --- a/doc/syntax.rst +++ b/doc/syntax.rst @@ -304,8 +304,7 @@ The available presentation types for pointers are: Chrono Format Specifications ============================ -Format specifications for chrono types and ``std::tm`` have the following -syntax: +Format specifications for chrono types have the following syntax: .. productionlist:: sf chrono_format_spec: [[`fill`]`align`][`width`]["." `precision`][`chrono_specs`] @@ -349,35 +348,9 @@ points are: Specifiers that have a calendaric component such as `'d'` (the day of month) are valid only for ``std::tm`` and not durations or time points. -.. range-specs: - -Range Format Specifications -=========================== - -Format specifications for range types have the following syntax: - -..productionlist:: sf - range_format_spec: [":" [`underlying_spec`]] - -The `underlying_spec` is parsed based on the formatter of the range's -reference type. - -By default, a range of characters or strings is printed escaped and quoted. But -if any `underlying_spec` is provided (even if it is empty), then the characters -or strings are printed according to the provided specification. - -Examples: - - fmt::format("{}", std::vector{10, 20, 30}); - // Result: [10, 20, 30] - fmt::format("{::#x}", std::vector{10, 20, 30}); - // Result: [0xa, 0x14, 0x13] - fmt::format("{}", vector{'h', 'e', 'l', 'l', 'o'}); - // Result: ['h', 'e', 'l', 'l', 'o'] - fmt::format("{::}", vector{'h', 'e', 'l', 'l', 'o'}); - // Result: [h, e, l, l, o] - fmt::format("{::d}", vector{'h', 'e', 'l', 'l', 'o'}); - // Result: [104, 101, 108, 108, 111] +``std::tm`` uses the system's `strftime +`_ so refer to its +documentation for details on supported conversion specifiers. .. _formatexamples: diff --git a/include/fmt/args.h b/include/fmt/args.h index a3966d14..9a8e4ed2 100644 --- a/include/fmt/args.h +++ b/include/fmt/args.h @@ -95,10 +95,10 @@ class dynamic_format_arg_store }; template - using stored_type = conditional_t< - std::is_convertible>::value && - !detail::is_reference_wrapper::value, - std::basic_string, T>; + using stored_type = conditional_t::value && + !has_formatter::value && + !detail::is_reference_wrapper::value, + std::basic_string, T>; // Storage of basic_format_arg must be contiguous. std::vector> data_; diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 7872fb4b..682efd8d 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -10,8 +10,6 @@ #include #include -#include // std::isfinite -#include // std::memcpy #include #include #include @@ -323,13 +321,14 @@ constexpr const size_t codecvt_result::max_size; template void write_codecvt(codecvt_result& out, string_view in_buf, const std::locale& loc) { + using codecvt = std::codecvt; #if FMT_CLANG_VERSION # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdeprecated" - auto& f = std::use_facet>(loc); + auto& f = std::use_facet(loc); # pragma clang diagnostic pop #else - auto& f = std::use_facet>(loc); + auto& f = std::use_facet(loc); #endif auto mb = std::mbstate_t(); const char* from_next = nullptr; @@ -563,10 +562,10 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b, constexpr const size_t len = 8; if (const_check(is_big_endian())) { char tmp[len]; - std::memcpy(tmp, &digits, len); + memcpy(tmp, &digits, len); std::reverse_copy(tmp, tmp + len, buf); } else { - std::memcpy(buf, &digits, len); + memcpy(buf, &digits, len); } } @@ -1215,7 +1214,7 @@ template class tm_writer { char buf[10]; size_t offset = 0; if (year >= 0 && year < 10000) { - copy2(buf, digits2(static_cast(year / 100))); + copy2(buf, digits2(to_unsigned(year / 100))); } else { offset = 4; write_year_extended(year); @@ -1388,6 +1387,15 @@ struct chrono_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_duration_unit() {} }; +template ::value)> +inline bool isnan(T) { + return false; +} +template ::value)> +inline bool isnan(T value) { + return std::isnan(value); +} + template ::value)> inline bool isfinite(T) { return true; @@ -1462,23 +1470,14 @@ inline std::chrono::duration get_milliseconds( #endif } -// Counts the number of fractional digits in the range [0, 18] according to the +// Returns the number of fractional digits in the range [0, 18] according to the // C++20 spec. If more than 18 fractional digits are required then returns 6 for // microseconds precision. -template ::max() / 10)> -struct count_fractional_digits { - static constexpr int value = - Num % Den == 0 ? N : count_fractional_digits::value; -}; - -// Base case that doesn't instantiate any more templates -// in order to avoid overflow. -template -struct count_fractional_digits { - static constexpr int value = (Num % Den == 0) ? N : 6; -}; +constexpr int count_fractional_digits(long long num, long long den, int n = 0) { + return num % den == 0 + ? n + : (n > 18 ? 6 : count_fractional_digits(num * 10, den, n + 1)); +} constexpr long long pow10(std::uint32_t n) { return n == 0 ? 1 : 10 * pow10(n - 1); @@ -1664,11 +1663,9 @@ struct chrono_formatter { out = format_decimal(out, n, num_digits).end; } - template void write_fractional_seconds(Duration d) { - FMT_ASSERT(!std::is_floating_point::value, ""); + template void write_fractional_seconds(Duration d) { constexpr auto num_fractional_digits = - count_fractional_digits::value; + count_fractional_digits(Duration::period::num, Duration::period::den); using subsecond_precision = std::chrono::duration< typename std::common_type::value) { *out++ = '.'; - auto fractional = - detail::abs(d) - std::chrono::duration_cast(d); - auto subseconds = + // Don't convert long double to integer seconds to avoid overflow. + using sec = conditional_t< + std::is_same::value, + std::chrono::duration, std::chrono::seconds>; + auto fractional = detail::abs(d) - std::chrono::duration_cast(d); + const auto subseconds = std::chrono::treat_as_floating_point< typename subsecond_precision::rep>::value ? fractional.count() @@ -1770,22 +1770,8 @@ struct chrono_formatter { if (handle_nan_inf()) return; if (ns == numeric_system::standard) { - if (std::is_floating_point::value) { - constexpr auto num_fractional_digits = - count_fractional_digits::value; - auto buf = memory_buffer(); - format_to(std::back_inserter(buf), runtime("{:.{}f}"), - std::fmod(val * static_cast(Period::num) / - static_cast(Period::den), - 60), - num_fractional_digits); - if (negative) *out++ = '-'; - if (buf.size() < 2 || buf[1] == '.') *out++ = '0'; - out = std::copy(buf.begin(), buf.end(), out); - } else { - write(second(), 2); - write_fractional_seconds(std::chrono::duration(val)); - } + write(second(), 2); + write_fractional_seconds(std::chrono::duration{val}); return; } auto time = tm(); diff --git a/include/fmt/color.h b/include/fmt/color.h index 762809ca..dfbe4829 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -214,16 +214,17 @@ FMT_BEGIN_DETAIL_NAMESPACE // color is a struct of either a rgb color or a terminal color. struct color_type { - FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {} - FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} { + FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} + FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), + value{} { value.rgb_color = static_cast(rgb_color); } - FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} { + FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} { value.rgb_color = (static_cast(rgb_color.r) << 16) | (static_cast(rgb_color.g) << 8) | rgb_color.b; } - FMT_CONSTEXPR color_type(terminal_color term_color) noexcept - : is_rgb(), value{} { + FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), + value{} { value.term_color = static_cast(term_color); } bool is_rgb; @@ -238,8 +239,10 @@ FMT_END_DETAIL_NAMESPACE /** A text style consisting of foreground and background colors and emphasis. */ class text_style { public: - FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept - : set_foreground_color(), set_background_color(), ems(em) {} + FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT + : set_foreground_color(), + set_background_color(), + ems(em) {} FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { if (!set_foreground_color) { @@ -280,32 +283,34 @@ class text_style { return lhs.and_assign(rhs); } - FMT_CONSTEXPR bool has_foreground() const noexcept { + FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { return set_foreground_color; } - FMT_CONSTEXPR bool has_background() const noexcept { + FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { return set_background_color; } - FMT_CONSTEXPR bool has_emphasis() const noexcept { + FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { return static_cast(ems) != 0; } - FMT_CONSTEXPR detail::color_type get_foreground() const noexcept { + FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT { FMT_ASSERT(has_foreground(), "no foreground specified for this style"); return foreground_color; } - FMT_CONSTEXPR detail::color_type get_background() const noexcept { + FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT { FMT_ASSERT(has_background(), "no background specified for this style"); return background_color; } - FMT_CONSTEXPR emphasis get_emphasis() const noexcept { + FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); return ems; } private: FMT_CONSTEXPR text_style(bool is_foreground, - detail::color_type text_color) noexcept - : set_foreground_color(), set_background_color(), ems() { + detail::color_type text_color) FMT_NOEXCEPT + : set_foreground_color(), + set_background_color(), + ems() { if (is_foreground) { foreground_color = text_color; set_foreground_color = true; @@ -340,11 +345,11 @@ class text_style { return *this; } - friend FMT_CONSTEXPR_DECL text_style - fg(detail::color_type foreground) noexcept; + friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground) + FMT_NOEXCEPT; - friend FMT_CONSTEXPR_DECL text_style - bg(detail::color_type background) noexcept; + friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background) + FMT_NOEXCEPT; detail::color_type foreground_color; detail::color_type background_color; @@ -354,16 +359,17 @@ class text_style { }; /** Creates a text style from the foreground (text) color. */ -FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept { +FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) FMT_NOEXCEPT { return text_style(true, foreground); } /** Creates a text style from the background color. */ -FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept { +FMT_CONSTEXPR inline text_style bg(detail::color_type background) FMT_NOEXCEPT { return text_style(false, background); } -FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept { +FMT_CONSTEXPR inline text_style operator|(emphasis lhs, + emphasis rhs) FMT_NOEXCEPT { return text_style(lhs) | rhs; } @@ -371,7 +377,7 @@ FMT_BEGIN_DETAIL_NAMESPACE template struct ansi_color_escape { FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, - const char* esc) noexcept { + const char* esc) FMT_NOEXCEPT { // If we have a terminal color, we need to output another escape code // sequence. if (!text_color.is_rgb) { @@ -406,7 +412,7 @@ template struct ansi_color_escape { to_esc(color.b, buffer + 15, 'm'); buffer[19] = static_cast(0); } - FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept { + FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { uint8_t em_codes[num_emphases] = {}; if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2; @@ -427,10 +433,10 @@ template struct ansi_color_escape { } buffer[index++] = static_cast(0); } - FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } + FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } - FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; } - FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept { + FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } + FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT { return buffer + std::char_traits::length(buffer); } @@ -439,64 +445,59 @@ template struct ansi_color_escape { Char buffer[7u + 3u * num_emphases + 1u]; static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, - char delimiter) noexcept { + char delimiter) FMT_NOEXCEPT { out[0] = static_cast('0' + c / 100); out[1] = static_cast('0' + c / 10 % 10); out[2] = static_cast('0' + c % 10); out[3] = static_cast(delimiter); } - static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept { + static FMT_CONSTEXPR bool has_emphasis(emphasis em, + emphasis mask) FMT_NOEXCEPT { return static_cast(em) & static_cast(mask); } }; template FMT_CONSTEXPR ansi_color_escape make_foreground_color( - detail::color_type foreground) noexcept { + detail::color_type foreground) FMT_NOEXCEPT { return ansi_color_escape(foreground, "\x1b[38;2;"); } template FMT_CONSTEXPR ansi_color_escape make_background_color( - detail::color_type background) noexcept { + detail::color_type background) FMT_NOEXCEPT { return ansi_color_escape(background, "\x1b[48;2;"); } template -FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) noexcept { +FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) FMT_NOEXCEPT { return ansi_color_escape(em); } -template inline void fputs(const Char* chars, FILE* stream) { - int result = std::fputs(chars, stream); - if (result < 0) - FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); +template +inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { + std::fputs(chars, stream); } -template <> inline void fputs(const wchar_t* chars, FILE* stream) { - int result = std::fputws(chars, stream); - if (result < 0) - FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); +template <> +inline void fputs(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { + std::fputws(chars, stream); } -template inline void reset_color(FILE* stream) { +template inline void reset_color(FILE* stream) FMT_NOEXCEPT { fputs("\x1b[0m", stream); } -template <> inline void reset_color(FILE* stream) { +template <> inline void reset_color(FILE* stream) FMT_NOEXCEPT { fputs(L"\x1b[0m", stream); } -template inline void reset_color(buffer& buffer) { +template +inline void reset_color(buffer& buffer) FMT_NOEXCEPT { auto reset_color = string_view("\x1b[0m"); buffer.append(reset_color.begin(), reset_color.end()); } -template struct styled_arg { - const T& value; - text_style style; -}; - template void vformat_to(buffer& buf, const text_style& ts, basic_string_view format_str, @@ -528,12 +529,8 @@ 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); - if (detail::is_utf8()) { - detail::print(f, basic_string_view(buf.begin(), buf.size())); - } else { - buf.push_back(Char(0)); - detail::fputs(buf.data(), f); - } + buf.push_back(Char(0)); + detail::fputs(buf.data(), f); } /** @@ -552,7 +549,7 @@ template >>(args...)); + fmt::make_args_checked(format_str, args...)); } /** @@ -597,7 +594,7 @@ template > inline std::basic_string format(const text_style& ts, const S& format_str, const Args&... args) { return fmt::vformat(ts, to_string_view(format_str), - fmt::make_format_args>(args...)); + fmt::make_args_checked(format_str, args...)); } /** @@ -632,60 +629,7 @@ inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, Args&&... args) -> typename std::enable_if::type { return vformat_to(out, ts, to_string_view(format_str), - fmt::make_format_args>>(args...)); -} - -template -struct formatter, Char> : formatter { - template - auto format(const detail::styled_arg& arg, FormatContext& ctx) const - -> decltype(ctx.out()) { - const auto& ts = arg.style; - const auto& value = arg.value; - auto out = ctx.out(); - - bool has_style = false; - if (ts.has_emphasis()) { - has_style = true; - auto emphasis = detail::make_emphasis(ts.get_emphasis()); - out = std::copy(emphasis.begin(), emphasis.end(), out); - } - if (ts.has_foreground()) { - has_style = true; - auto foreground = - detail::make_foreground_color(ts.get_foreground()); - out = std::copy(foreground.begin(), foreground.end(), out); - } - if (ts.has_background()) { - has_style = true; - auto background = - detail::make_background_color(ts.get_background()); - out = std::copy(background.begin(), background.end(), out); - } - out = formatter::format(value, ctx); - if (has_style) { - auto reset_color = string_view("\x1b[0m"); - out = std::copy(reset_color.begin(), reset_color.end(), out); - } - return out; - } -}; - -/** - \rst - Returns an argument that will be formatted using ANSI escape sequences, - to be used in a formatting function. - - **Example**:: - - fmt::print("Elapsed time: {s:.2f} seconds", - fmt::styled(1.23, fmt::fg(fmt::color::green) | fmt::bg(fmt::color::blue))); - \endrst - */ -template -FMT_CONSTEXPR auto styled(const T& value, text_style ts) - -> detail::styled_arg> { - return detail::styled_arg>{value, ts}; + fmt::make_args_checked(format_str, args...)); } FMT_MODULE_EXPORT_END diff --git a/include/fmt/compile.h b/include/fmt/compile.h index b2bf1ba1..1dba3ddb 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -13,6 +13,45 @@ FMT_BEGIN_NAMESPACE namespace detail { +// An output iterator that counts the number of objects written to it and +// discards them. +class counting_iterator { + private: + size_t count_; + + public: + using iterator_category = std::output_iterator_tag; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = void; + using _Unchecked_type = counting_iterator; // Mark iterator as checked. + + struct value_type { + template void operator=(const T&) {} + }; + + counting_iterator() : count_(0) {} + + size_t count() const { return count_; } + + counting_iterator& operator++() { + ++count_; + return *this; + } + counting_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } + + friend counting_iterator operator+(counting_iterator it, difference_type n) { + it.count_ += static_cast(n); + return it; + } + + value_type operator*() const { return {}; } +}; + template inline counting_iterator copy_str(InputIt begin, InputIt end, counting_iterator it) { @@ -129,7 +168,7 @@ template Str> struct udl_compiled_string : compiled_string { using char_type = Char; - explicit constexpr operator basic_string_view() const { + constexpr operator basic_string_view() const { return {Str.data, N - 1}; } }; @@ -534,11 +573,10 @@ FMT_INLINE std::basic_string format(const S&, constexpr auto compiled = detail::compile(S()); if constexpr (std::is_same, detail::unknown_format>()) { - return fmt::format( - static_cast>(S()), - std::forward(args)...); + return format(static_cast>(S()), + std::forward(args)...); } else { - return fmt::format(compiled, std::forward(args)...); + return format(compiled, std::forward(args)...); } } @@ -548,11 +586,11 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { constexpr auto compiled = detail::compile(S()); if constexpr (std::is_same, detail::unknown_format>()) { - return fmt::format_to( - out, static_cast>(S()), - std::forward(args)...); + return format_to(out, + static_cast>(S()), + std::forward(args)...); } else { - return fmt::format_to(out, compiled, std::forward(args)...); + return format_to(out, compiled, std::forward(args)...); } } #endif @@ -561,23 +599,22 @@ template ::value)> format_to_n_result format_to_n(OutputIt out, size_t n, const S& format_str, Args&&... args) { - auto it = fmt::format_to(detail::truncating_iterator(out, n), - format_str, std::forward(args)...); + auto it = format_to(detail::truncating_iterator(out, n), format_str, + std::forward(args)...); return {it.base(), it.count()}; } template ::value)> size_t formatted_size(const S& format_str, const Args&... args) { - return fmt::format_to(detail::counting_iterator(), format_str, args...) - .count(); + return format_to(detail::counting_iterator(), format_str, args...).count(); } template ::value)> void print(std::FILE* f, const S& format_str, const Args&... args) { memory_buffer buffer; - fmt::format_to(std::back_inserter(buffer), format_str, args...); + format_to(std::back_inserter(buffer), format_str, args...); detail::print(f, {buffer.data(), buffer.size()}); } @@ -589,10 +626,12 @@ void print(const S& format_str, const Args&... args) { #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS inline namespace literals { -template constexpr auto operator""_cf() { - using char_t = remove_cvref_t; - return detail::udl_compiled_string(); +template +constexpr detail::udl_compiled_string< + remove_cvref_t, + sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> +operator""_cf() { + return {}; } } // namespace literals #endif diff --git a/include/fmt/core.h b/include/fmt/core.h index 106fd6fe..92a7aa1d 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -8,15 +8,16 @@ #ifndef FMT_CORE_H_ #define FMT_CORE_H_ +#include // std::byte #include // std::FILE -#include // std::strlen +#include #include #include #include #include // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 80102 +#define FMT_VERSION 80101 #if defined(__clang__) && !defined(__ibmxl__) # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) @@ -48,13 +49,6 @@ # 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 @@ -151,6 +145,28 @@ # endif #endif +// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). +#ifndef FMT_USE_NOEXCEPT +# define FMT_USE_NOEXCEPT 0 +#endif + +#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ + FMT_GCC_VERSION >= 408 || FMT_MSC_VER >= 1900 +# define FMT_DETECTED_NOEXCEPT noexcept +# define FMT_HAS_CXX11_NOEXCEPT 1 +#else +# define FMT_DETECTED_NOEXCEPT throw() +# define FMT_HAS_CXX11_NOEXCEPT 0 +#endif + +#ifndef FMT_NOEXCEPT +# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT +# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT +# else +# define FMT_NOEXCEPT +# endif +#endif + // [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code // warnings. #if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \ @@ -293,7 +309,7 @@ // Enable minimal optimizations for more compact code in debug mode. FMT_GCC_PRAGMA("GCC push_options") -#if !defined(__OPTIMIZE__) && !FMT_NVCOMPILER_VERSION +#ifndef __OPTIMIZE__ FMT_GCC_PRAGMA("GCC optimize(\"Og\")") #endif @@ -314,8 +330,6 @@ template using remove_cvref_t = typename std::remove_cv>::type; template struct type_identity { using type = T; }; template using type_identity_t = typename type_identity::type; -template -using underlying_t = typename std::underlying_type::type; struct monostate { constexpr monostate() {} @@ -337,8 +351,8 @@ FMT_BEGIN_DETAIL_NAMESPACE // (void)var does not work on many Intel compilers. template FMT_CONSTEXPR void ignore_unused(const T&...) {} -constexpr FMT_INLINE auto is_constant_evaluated( - bool default_value = false) noexcept -> bool { +constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false) + FMT_NOEXCEPT -> bool { #ifdef __cpp_lib_is_constant_evaluated ignore_unused(default_value); return std::is_constant_evaluated(); @@ -368,6 +382,12 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, # endif #endif +#ifdef __cpp_lib_byte +using byte = std::byte; +#else +enum class byte : unsigned char {}; +#endif + #if defined(FMT_USE_STRING_VIEW) template using std_string_view = std::basic_string_view; #elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) @@ -382,8 +402,8 @@ template struct std_string_view {}; #elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \ !(FMT_CLANG_VERSION && FMT_MSC_VER) # define FMT_USE_INT128 1 -using int128_opt = __int128_t; // An optional 128-bit integer. -using uint128_opt = __uint128_t; +using int128_t = __int128_t; +using uint128_t = __uint128_t; template inline auto convert_for_visit(T value) -> T { return value; } @@ -391,10 +411,12 @@ template inline auto convert_for_visit(T value) -> T { # define FMT_USE_INT128 0 #endif #if !FMT_USE_INT128 -enum class int128_opt {}; -enum class uint128_opt {}; +enum class int128_t {}; +enum class uint128_t {}; // Reduce template instantiations. -template auto convert_for_visit(T) -> monostate { return {}; } +template inline auto convert_for_visit(T) -> monostate { + return {}; +} #endif // Casts a nonnegative integer to unsigned. @@ -432,11 +454,12 @@ template class basic_string_view { using value_type = Char; using iterator = const Char*; - constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} + constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {} /** Constructs a string reference object from a C string and a size. */ - constexpr basic_string_view(const Char* s, size_t count) noexcept - : data_(s), size_(count) {} + constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT + : data_(s), + size_(count) {} /** \rst @@ -456,28 +479,29 @@ template class basic_string_view { /** Constructs a string reference from a ``std::basic_string`` object. */ template FMT_CONSTEXPR basic_string_view( - const std::basic_string& s) noexcept - : data_(s.data()), size_(s.size()) {} + const std::basic_string& s) FMT_NOEXCEPT + : data_(s.data()), + size_(s.size()) {} template >::value)> - FMT_CONSTEXPR basic_string_view(S s) noexcept - : data_(s.data()), size_(s.size()) {} + FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()), + size_(s.size()) {} /** Returns a pointer to the string data. */ - constexpr auto data() const noexcept -> const Char* { return data_; } + constexpr auto data() const FMT_NOEXCEPT -> const Char* { return data_; } /** Returns the string size. */ - constexpr auto size() const noexcept -> size_t { return size_; } + constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; } - constexpr auto begin() const noexcept -> iterator { return data_; } - constexpr auto end() const noexcept -> iterator { return data_ + size_; } + constexpr auto begin() const FMT_NOEXCEPT -> iterator { return data_; } + constexpr auto end() const FMT_NOEXCEPT -> iterator { return data_ + size_; } - constexpr auto operator[](size_t pos) const noexcept -> const Char& { + constexpr auto operator[](size_t pos) const FMT_NOEXCEPT -> const Char& { return data_[pos]; } - FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { + FMT_CONSTEXPR void remove_prefix(size_t n) FMT_NOEXCEPT { data_ += n; size_ -= n; } @@ -624,14 +648,16 @@ class basic_format_parse_context : private ErrorHandler { Returns an iterator to the beginning of the format string range being parsed. */ - constexpr auto begin() const noexcept -> iterator { + constexpr auto begin() const FMT_NOEXCEPT -> iterator { return format_str_.begin(); } /** Returns an iterator past the end of the format string range being parsed. */ - constexpr auto end() const noexcept -> iterator { return format_str_.end(); } + constexpr auto end() const FMT_NOEXCEPT -> iterator { + return format_str_.end(); + } /** Advances the begin iterator to ``it``. */ FMT_CONSTEXPR void advance_to(iterator it) { @@ -758,16 +784,18 @@ template class buffer { protected: // Don't initialize ptr_ since it is not accessed to save a few cycles. FMT_MSC_WARNING(suppress : 26495) - buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {} + buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {} - FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept - : ptr_(p), size_(sz), capacity_(cap) {} + FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, + size_t cap = 0) FMT_NOEXCEPT : ptr_(p), + size_(sz), + capacity_(cap) {} FMT_CONSTEXPR20 ~buffer() = default; buffer(buffer&&) = default; /** Sets the buffer data and capacity. */ - FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { + FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT { ptr_ = buf_data; capacity_ = buf_capacity; } @@ -782,23 +810,23 @@ template class buffer { buffer(const buffer&) = delete; void operator=(const buffer&) = delete; - auto begin() noexcept -> T* { return ptr_; } - auto end() noexcept -> T* { return ptr_ + size_; } + auto begin() FMT_NOEXCEPT -> T* { return ptr_; } + auto end() FMT_NOEXCEPT -> T* { return ptr_ + size_; } - auto begin() const noexcept -> const T* { return ptr_; } - auto end() const noexcept -> const T* { return ptr_ + size_; } + auto begin() const FMT_NOEXCEPT -> const T* { return ptr_; } + auto end() const FMT_NOEXCEPT -> const T* { return ptr_ + size_; } /** Returns the size of this buffer. */ - constexpr auto size() const noexcept -> size_t { return size_; } + constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; } /** Returns the capacity of this buffer. */ - constexpr auto capacity() const noexcept -> size_t { return capacity_; } + constexpr auto capacity() const FMT_NOEXCEPT -> size_t { return capacity_; } /** Returns a pointer to the buffer data. */ - FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } + FMT_CONSTEXPR auto data() FMT_NOEXCEPT -> T* { return ptr_; } /** Returns a pointer to the buffer data. */ - FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } + FMT_CONSTEXPR auto data() const FMT_NOEXCEPT -> const T* { return ptr_; } /** Clears this buffer. */ void clear() { size_ = 0; } @@ -1016,11 +1044,7 @@ struct fallback_formatter { // Specifies if T has an enabled fallback_formatter specialization. template using has_fallback_formatter = -#ifdef FMT_DEPRECATED_OSTREAM std::is_constructible>; -#else - std::false_type; -#endif struct view {}; @@ -1140,8 +1164,8 @@ 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(int128_t, int128_type); +FMT_TYPE_CONSTANT(uint128_t, uint128_type); FMT_TYPE_CONSTANT(bool, bool_type); FMT_TYPE_CONSTANT(Char, char_type); FMT_TYPE_CONSTANT(float, float_type); @@ -1191,8 +1215,8 @@ template class value { unsigned uint_value; long long long_long_value; unsigned long long ulong_long_value; - int128_opt int128_value; - uint128_opt uint128_value; + int128_t int128_value; + uint128_t uint128_value; bool bool_value; char_type char_value; float float_value; @@ -1209,8 +1233,8 @@ template class value { constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} constexpr FMT_INLINE value(long long val) : long_long_value(val) {} constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} - FMT_INLINE value(int128_opt val) : int128_value(val) {} - FMT_INLINE value(uint128_opt val) : uint128_value(val) {} + FMT_INLINE value(int128_t val) : int128_value(val) {} + FMT_INLINE value(uint128_t val) : uint128_value(val) {} constexpr FMT_INLINE value(float val) : float_value(val) {} constexpr FMT_INLINE value(double val) : double_value(val) {} FMT_INLINE value(long double val) : long_double_value(val) {} @@ -1260,7 +1284,7 @@ template class value { }; template -FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg; +FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg; // To minimize the number of types we need to deal with, long is translated // either to int or to long long depending on its size. @@ -1293,12 +1317,8 @@ template struct arg_mapper { -> unsigned long long { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt { - return val; - } + FMT_CONSTEXPR FMT_INLINE auto map(int128_t val) -> int128_t { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(uint128_t val) -> uint128_t { return val; } FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; } template ::value || @@ -1345,19 +1365,20 @@ template struct arg_mapper { } template >::value && + std::is_constructible, T>::value && !is_string::value && !has_formatter::value && !has_fallback_formatter::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> basic_string_view { return basic_string_view(val); } - template >::value && - !std::is_convertible>::value && - !is_string::value && !has_formatter::value && - !has_fallback_formatter::value)> + template < + typename T, + FMT_ENABLE_IF( + std::is_constructible, T>::value && + !std::is_constructible, T>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> basic_string_view { return std_string_view(val); @@ -1396,11 +1417,10 @@ template struct arg_mapper { template < typename T, FMT_ENABLE_IF( - std::is_pointer::value || std::is_member_pointer::value || + std::is_member_pointer::value || std::is_function::type>::value || (std::is_convertible::value && - !std::is_convertible::value && - !has_formatter::value))> + !std::is_convertible::value))> FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { return {}; } @@ -1418,15 +1438,12 @@ template struct arg_mapper { !has_fallback_formatter::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(std::declval().map( - static_cast>(val))) { - return map(static_cast>(val)); + static_cast::type>(val))) { + return map(static_cast::type>(val)); } - template ::value&& std::is_integral::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) - -> decltype(std::declval().map(U())) { - return map(format_as(val)); + FMT_CONSTEXPR FMT_INLINE auto map(detail::byte val) -> unsigned { + return map(static_cast(val)); } template > @@ -1435,9 +1452,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 - // Workaround a bug in MSVC and Intel (Issue 2746). +#if FMT_MSC_VER != 0 && FMT_MSC_VER < 1910 + // Workaround a bug in MSVC. template FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { return val; } @@ -1497,12 +1513,12 @@ class appender : public std::back_insert_iterator> { public: using std::back_insert_iterator>::back_insert_iterator; - appender(base it) noexcept : base(it) {} + appender(base it) FMT_NOEXCEPT : base(it) {} using _Unchecked_type = appender; // Mark iterator as checked. - auto operator++() noexcept -> appender& { return *this; } + auto operator++() FMT_NOEXCEPT -> appender& { return *this; } - auto operator++(int) noexcept -> appender { return *this; } + auto operator++(int) FMT_NOEXCEPT -> appender { return *this; } }; // A formatting argument. It is a trivially copyable/constructible type to @@ -1513,7 +1529,7 @@ template class basic_format_arg { detail::type type_; template - friend FMT_CONSTEXPR auto detail::make_arg(T&& value) + friend FMT_CONSTEXPR auto detail::make_arg(const T& value) -> basic_format_arg; template @@ -1548,7 +1564,7 @@ template class basic_format_arg { constexpr basic_format_arg() : type_(detail::type::none_type) {} - constexpr explicit operator bool() const noexcept { + constexpr explicit operator bool() const FMT_NOEXCEPT { return type_ != detail::type::none_type; } @@ -1658,7 +1674,7 @@ class locale_ref { constexpr locale_ref() : locale_(nullptr) {} template explicit locale_ref(const Locale& loc); - explicit operator bool() const noexcept { return locale_ != nullptr; } + explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } template auto get() const -> Locale; }; @@ -1674,7 +1690,19 @@ constexpr auto encode_types() -> unsigned long long { } template -FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value { +FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg { + basic_format_arg arg; + arg.type_ = mapped_type_constant::value; + arg.value_ = arg_mapper().map(value); + return arg; +} + +// The type template parameter is there to avoid an ODR violation when using +// a fallback formatter in one translation unit and an implicit conversion in +// another (not recommended). +template +FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { const auto& arg = arg_mapper().map(std::forward(val)); constexpr bool formattable_char = @@ -1703,26 +1731,9 @@ FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value { return {arg}; } -template -FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg { - basic_format_arg arg; - arg.type_ = mapped_type_constant::value; - arg.value_ = make_value(value); - return arg; -} - -// The type template parameter is there to avoid an ODR violation when using -// a fallback formatter in one translation unit and an implicit conversion in -// another (not recommended). -template -FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { - return make_value(val); -} - template -FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg { +inline auto make_arg(const T& value) -> basic_format_arg { return make_arg(value); } FMT_END_DETAIL_NAMESPACE @@ -2061,8 +2072,7 @@ enum class presentation_type : unsigned char { general_upper, // 'G' chr, // 'c' string, // 's' - pointer, // 'p' - debug // '?' + pointer // 'p' }; // Format specifiers for built-in and string types. @@ -2221,12 +2231,13 @@ template constexpr bool is_ascii_letter(Char c) { // Converts a character to ASCII. Returns a number > 127 on conversion failure. template ::value)> -constexpr auto to_ascii(Char c) -> Char { - return c; +constexpr auto to_ascii(Char value) -> Char { + return value; } template ::value)> -constexpr auto to_ascii(Char c) -> underlying_t { - return c; +constexpr auto to_ascii(Char value) -> + typename std::underlying_type::type { + return value; } template @@ -2291,7 +2302,7 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, FMT_ASSERT(begin != end, ""); auto align = align::none; auto p = begin + code_point_length(begin); - if (end - p <= 0) p = begin; + if (p >= end) p = begin; for (;;) { switch (to_ascii(*p)) { case '<': @@ -2477,8 +2488,6 @@ FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type { return presentation_type::string; case 'p': return presentation_type::pointer; - case '?': - return presentation_type::debug; default: return presentation_type::none; } @@ -2652,27 +2661,17 @@ FMT_CONSTEXPR FMT_INLINE void parse_format_string( } } -template ::value> struct strip_named_arg { - using type = T; -}; - -template struct strip_named_arg { - using type = remove_cvref_t; -}; - template FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) -> decltype(ctx.begin()) { using char_type = typename ParseContext::char_type; using context = buffer_context; - using stripped_type = typename strip_named_arg::type; using mapped_type = conditional_t< mapped_type_constant::value != type::custom_type, - decltype(arg_mapper().map(std::declval())), - stripped_type>; + decltype(arg_mapper().map(std::declval())), T>; auto f = conditional_t::value, formatter, - fallback_formatter>(); + fallback_formatter>(); return f.parse(ctx); } @@ -2718,8 +2717,7 @@ template FMT_CONSTEXPR auto check_char_specs(const basic_format_specs& specs, ErrorHandler&& eh = {}) -> bool { if (specs.type != presentation_type::none && - specs.type != presentation_type::chr && - specs.type != presentation_type::debug) { + specs.type != presentation_type::chr) { check_int_type_spec(specs.type, eh); return false; } @@ -2743,6 +2741,7 @@ struct float_specs { bool upper : 1; bool locale : 1; bool binary32 : 1; + bool fallback : 1; bool showpoint : 1; }; @@ -2802,8 +2801,7 @@ FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type, template FMT_CONSTEXPR void check_string_type_spec(presentation_type type, ErrorHandler&& eh = {}) { - if (type != presentation_type::none && type != presentation_type::string && - type != presentation_type::debug) + if (type != presentation_type::none && type != presentation_type::string) eh.on_error("invalid type specifier"); } @@ -2837,8 +2835,7 @@ template class specs_checker : public Handler { FMT_CONSTEXPR void on_sign(sign_t s) { require_numeric_argument(); if (is_integral_type(arg_type_) && arg_type_ != type::int_type && - arg_type_ != type::long_long_type && arg_type_ != type::int128_type && - arg_type_ != type::char_type) { + arg_type_ != type::long_long_type && arg_type_ != type::char_type) { this->on_error("format specifier requires signed argument"); } Handler::on_sign(s); @@ -3046,27 +3043,6 @@ struct formatter decltype(ctx.out()); }; -#define FMT_FORMAT_AS(Type, Base) \ - template \ - struct formatter : formatter { \ - template \ - auto format(Type const& val, FormatContext& ctx) const \ - -> decltype(ctx.out()) { \ - return formatter::format(static_cast(val), ctx); \ - } \ - } - -FMT_FORMAT_AS(signed char, int); -FMT_FORMAT_AS(unsigned char, unsigned); -FMT_FORMAT_AS(short, int); -FMT_FORMAT_AS(unsigned short, unsigned); -FMT_FORMAT_AS(long, long long); -FMT_FORMAT_AS(unsigned long, unsigned long long); -FMT_FORMAT_AS(Char*, const Char*); -FMT_FORMAT_AS(std::basic_string, basic_string_view); -FMT_FORMAT_AS(std::nullptr_t, const void*); -FMT_FORMAT_AS(detail::std_string_view, basic_string_view); - template struct basic_runtime { basic_string_view str; }; /** A compile-time format string. */ diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 9a99b7a1..2c51c50a 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -44,8 +44,21 @@ FMT_FUNC void throw_format_error(const char* message) { FMT_THROW(format_error(message)); } +#ifndef _MSC_VER +# define FMT_SNPRINTF snprintf +#else // _MSC_VER +inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) { + va_list args; + va_start(args, format); + int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); + va_end(args); + return result; +} +# define FMT_SNPRINTF fmt_snprintf +#endif // _MSC_VER + FMT_FUNC void format_error_code(detail::buffer& out, int error_code, - string_view message) noexcept { + string_view message) FMT_NOEXCEPT { // Report error code making sure that the output fits into // inline_buffer_size to avoid dynamic memory allocation and potential // bad_alloc. @@ -68,7 +81,7 @@ FMT_FUNC void format_error_code(detail::buffer& out, int error_code, } FMT_FUNC void report_error(format_func func, int error_code, - const char* message) noexcept { + const char* message) FMT_NOEXCEPT { memory_buffer full_message; func(full_message, error_code, message); // Don't use fwrite_fully because the latter may throw. @@ -117,7 +130,7 @@ template FMT_FUNC Char decimal_point_impl(locale_ref) { } // namespace detail #if !FMT_MSC_VER -FMT_API FMT_FUNC format_error::~format_error() noexcept = default; +FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default; #endif FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str, @@ -128,6 +141,17 @@ FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str, namespace detail { +template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) { + // fallback_uintptr is always stored in little endian. + int i = static_cast(sizeof(void*)) - 1; + while (i > 0 && n.value[i] == 0) --i; + auto char_digits = std::numeric_limits::digits / 4; + return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1; +} + +// log10(2) = 0x0.4d104d427de7fbcc... +static constexpr uint64_t log10_2_significand = 0x4d104d427de7fbcc; + template struct basic_impl_data { // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. // These are generated by support/compute-powers.py. @@ -202,53 +226,66 @@ template struct bits { 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; +// Returns the number of significand bits in Float excluding the implicit bit. +template constexpr int num_significand_bits() { + // Subtract 1 to account for an implicit most significant bit in the + // normalized form. + return std::numeric_limits::digits - 1; +} + +// A floating-point number f * pow(2, e). +struct fp { + uint64_t f; int e; - static constexpr const int num_significand_bits = bits::value; + static constexpr const int num_significand_bits = bits::value; + + constexpr fp() : f(0), e(0) {} + constexpr fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} - 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. It is a template to + // prevent compile errors on systems where n is not IEEE754. + template explicit FMT_CONSTEXPR fp(Float n) { assign(n); } - // Constructs fp from an IEEE754 floating-point number. - template FMT_CONSTEXPR basic_fp(Float n) { assign(n); } + template + using is_supported = bool_constant; - // Assigns n to this and return true iff predecessor is closer than successor. - template FMT_CONSTEXPR bool assign(Float n) { - 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 = + // Assigns d to this and return true iff predecessor is closer than successor. + template ::value)> + FMT_CONSTEXPR bool assign(Float n) { + // Assume float is in the format [sign][exponent][significand]. + const int num_float_significand_bits = detail::num_significand_bits(); - const 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); + const uint64_t implicit_bit = 1ULL << num_float_significand_bits; + const uint64_t significand_mask = implicit_bit - 1; + constexpr bool is_double = sizeof(Float) == sizeof(uint64_t); + auto u = bit_cast>(n); + f = u & significand_mask; + const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask; + int biased_e = + static_cast((u & exponent_mask) >> num_float_significand_bits); // The predecessor is closer if n is a normalized power of 2 (f == 0) other // than the smallest normalized number (biased_e > 1). - auto is_predecessor_closer = f == 0 && biased_e > 1; + bool is_predecessor_closer = f == 0 && biased_e > 1; if (biased_e != 0) - f += static_cast(implicit_bit); + f += implicit_bit; else biased_e = 1; // Subnormals use biased exponent 1 (min exponent). - e = biased_e - exponent_bias() - num_float_significand_bits; - if (!has_implicit_bit()) ++e; + const int exponent_bias = std::numeric_limits::max_exponent - 1; + e = biased_e - exponent_bias - num_float_significand_bits; return is_predecessor_closer; } -}; -using fp = basic_fp; + template ::value)> + bool assign(Float) { + FMT_ASSERT(false, ""); + return false; + } +}; // Normalizes the value converted from double and multiplied by (1 << SHIFT). -template -FMT_CONSTEXPR basic_fp normalize(basic_fp value) { +template FMT_CONSTEXPR fp normalize(fp value) { // Handle subnormals. const uint64_t implicit_bit = 1ULL << num_significand_bits(); const auto shifted_implicit_bit = implicit_bit << SHIFT; @@ -264,9 +301,7 @@ FMT_CONSTEXPR basic_fp normalize(basic_fp value) { return value; } -template inline bool operator==(basic_fp x, basic_fp y) { - return x.f == y.f && x.e == y.e; -} +inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; } // Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { @@ -295,8 +330,7 @@ FMT_CONSTEXPR inline fp operator*(fp x, fp y) { 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; + const auto significand = static_cast(log10_2_significand); int index = static_cast( ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) + ((int64_t(1) << shift) - 1)) // ceil @@ -312,6 +346,29 @@ FMT_CONSTEXPR inline fp get_cached_power(int min_exponent, impl_data::pow10_exponents[index]}; } +// A simple accumulator to hold the sums of terms in bigint::square if uint128_t +// is not available. +struct accumulator { + uint64_t lower; + uint64_t upper; + + constexpr accumulator() : lower(0), upper(0) {} + constexpr explicit operator uint32_t() const { + return static_cast(lower); + } + + FMT_CONSTEXPR void operator+=(uint64_t n) { + lower += n; + if (lower < n) ++upper; + } + FMT_CONSTEXPR void operator>>=(int shift) { + FMT_ASSERT(shift == 32, ""); + (void)shift; + lower = (upper << 32) | (lower >> 32); + upper >>= 32; + } +}; + class bigint { private: // A bigint is stored as an array of bigits (big digits), with bigit at index @@ -388,6 +445,9 @@ class bigint { public: FMT_CONSTEXPR20 bigint() : exp_(0) {} explicit bigint(uint64_t n) { assign(n); } + FMT_CONSTEXPR20 ~bigint() { + FMT_ASSERT(bigits_.capacity() <= bigits_capacity, ""); + } bigint(const bigint&) = delete; void operator=(const bigint&) = delete; @@ -500,7 +560,8 @@ class bigint { 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(); + using accumulator_t = conditional_t; + auto sum = accumulator_t(); for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) { // Compute bigit at position bigit_index of the result by adding // cross-product terms n[i] * n[j] such that i + j == bigit_index. @@ -624,14 +685,6 @@ struct gen_digits_handler { } }; -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). @@ -649,7 +702,14 @@ FMT_INLINE FMT_CONSTEXPR20 digits::result grisu_gen_digits( 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); + // Adjust fixed precision by exponent because it is relative to decimal + // point. + int precision_offset = exp + handler.exp10; + if (precision_offset > 0 && + handler.precision > max_value() - precision_offset) { + FMT_THROW(format_error("number is too big")); + } + handler.precision += precision_offset; // Check if precision is satisfied just by leading zeros, e.g. // format("{:.2f}", 0.001) gives "0.00" without generating any digits. if (handler.precision <= 0) { @@ -725,63 +785,66 @@ FMT_INLINE FMT_CONSTEXPR20 digits::result grisu_gen_digits( } } -// A 128-bit integer type used internally. +// A 128-bit integer type used internally, struct uint128_wrapper { uint128_wrapper() = default; +#if FMT_USE_INT128 + uint128_t internal_; + + constexpr uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT + : internal_{static_cast(low) | + (static_cast(high) << 64)} {} + + constexpr uint128_wrapper(uint128_t u) : internal_{u} {} + + constexpr uint64_t high() const FMT_NOEXCEPT { + return uint64_t(internal_ >> 64); + } + constexpr uint64_t low() const FMT_NOEXCEPT { return uint64_t(internal_); } + + uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { + internal_ += n; + return *this; + } +#else uint64_t high_; uint64_t low_; - constexpr uint128_wrapper(uint64_t high, uint64_t low) noexcept - : high_{high}, low_{low} {} - - constexpr uint64_t high() const noexcept { return high_; } - constexpr uint64_t low() const noexcept { return low_; } - - uint128_wrapper& operator+=(uint64_t n) noexcept { -#if FMT_HAS_BUILTIN(__builtin_addcll) - unsigned long long carry; - low_ = __builtin_addcll(low_, n, 0, &carry); - high_ += carry; -#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) - unsigned long long result; - auto carry = __builtin_ia32_addcarryx_u64(0, low_, n, &result); - low_ = result; - high_ += carry; -#elif defined(_MSC_VER) && defined(_M_X64) - auto carry = _addcarry_u64(0, low_, n, &low_); + constexpr uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT + : high_{high}, + low_{low} {} + + constexpr uint64_t high() const FMT_NOEXCEPT { return high_; } + constexpr uint64_t low() const FMT_NOEXCEPT { return low_; } + + uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { +# if defined(_MSC_VER) && defined(_M_X64) + unsigned char carry = _addcarry_u64(0, low_, n, &low_); _addcarry_u64(carry, high_, 0, &high_); -#else - low_ += n; - high_ += (low_ < n ? 1 : 0); -#endif return *this; +# else + uint64_t sum = low_ + n; + high_ += (sum < low_ ? 1 : 0); + low_ = sum; + return *this; +# endif } +#endif }; -// Compilers should be able to optimize this into the ror instruction. -FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept { - r &= 31; - return (n >> r) | (n << (32 - r)); -} -FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept { - r &= 63; - return (n >> r) | (n << (64 - r)); -} - // Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. namespace dragonbox { // Computes 128-bit result of multiplication of two 64-bit unsigned integers. -inline uint128_wrapper umul128(uint64_t x, uint64_t y) noexcept { +inline uint128_wrapper umul128(uint64_t x, uint64_t y) FMT_NOEXCEPT { #if FMT_USE_INT128 - auto p = static_cast(x) * static_cast(y); - return {static_cast(p >> 64), static_cast(p)}; + return static_cast(x) * static_cast(y); #elif defined(_MSC_VER) && defined(_M_X64) uint128_wrapper result; result.low_ = _umul128(x, y, &result.high_); return result; #else - const uint64_t mask = static_cast(max_value()); + const uint64_t mask = (uint64_t(1) << 32) - uint64_t(1); uint64_t a = x >> 32; uint64_t b = x & mask; @@ -801,9 +864,9 @@ inline uint128_wrapper umul128(uint64_t x, uint64_t y) noexcept { } // Computes upper 64 bits of multiplication of two 64-bit unsigned integers. -inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { +inline uint64_t umul128_upper64(uint64_t x, uint64_t y) FMT_NOEXCEPT { #if FMT_USE_INT128 - auto p = static_cast(x) * static_cast(y); + auto p = static_cast(x) * static_cast(y); return static_cast(p >> 64); #elif defined(_MSC_VER) && defined(_M_X64) return __umulh(x, y); @@ -812,105 +875,170 @@ inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { #endif } -// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a +// Computes upper 64 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. -inline uint128_wrapper umul192_upper128(uint64_t x, - uint128_wrapper y) noexcept { - uint128_wrapper r = umul128(x, y.high()); - r += umul128_upper64(x, y.low()); - return r; +inline uint64_t umul192_upper64(uint64_t x, uint128_wrapper y) FMT_NOEXCEPT { + uint128_wrapper g0 = umul128(x, y.high()); + g0 += umul128_upper64(x, y.low()); + return g0.high(); } -// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a +// Computes upper 32 bits of multiplication of a 32-bit unsigned integer and a // 64-bit unsigned integer. -inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept { - return umul128_upper64(static_cast(x) << 32, y); +inline uint32_t umul96_upper32(uint32_t x, uint64_t y) FMT_NOEXCEPT { + return static_cast(umul128_upper64(x, y)); } -// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a +// Computes middle 64 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. -inline uint128_wrapper umul192_lower128(uint64_t x, - uint128_wrapper y) noexcept { - uint64_t high = x * y.high(); - uint128_wrapper high_low = umul128(x, y.low()); - return {high + high_low.high(), high_low.low()}; +inline uint64_t umul192_middle64(uint64_t x, uint128_wrapper y) FMT_NOEXCEPT { + uint64_t g01 = x * y.high(); + uint64_t g10 = umul128_upper64(x, y.low()); + return g01 + g10; } // Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a // 64-bit unsigned integer. -inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept { +inline uint64_t umul96_lower64(uint32_t x, uint64_t y) FMT_NOEXCEPT { return x * y; } -// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from -// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1. -inline int floor_log10_pow2(int e) noexcept { - FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent"); - static_assert((-1 >> 1) == -1, "right shift is not arithmetic"); - return (e * 315653) >> 20; +// Computes floor(log10(pow(2, e))) for e in [-1700, 1700] using the method from +// https://fmt.dev/papers/Grisu-Exact.pdf#page=5, section 3.4. +inline int floor_log10_pow2(int e) FMT_NOEXCEPT { + FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); + const int shift = 22; + return (e * static_cast(log10_2_significand >> (64 - shift))) >> shift; } // Various fast log computations. -inline int floor_log2_pow10(int e) noexcept { +inline int floor_log2_pow10(int e) FMT_NOEXCEPT { FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); - return (e * 1741647) >> 19; + const uint64_t log2_10_integer_part = 3; + const uint64_t log2_10_fractional_digits = 0x5269e12f346e2bf9; + const int shift_amount = 19; + return (e * static_cast( + (log2_10_integer_part << shift_amount) | + (log2_10_fractional_digits >> (64 - shift_amount)))) >> + shift_amount; +} +inline int floor_log10_pow2_minus_log10_4_over_3(int e) FMT_NOEXCEPT { + FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); + const uint64_t log10_4_over_3_fractional_digits = 0x1ffbfc2bbc780375; + const int shift_amount = 22; + return (e * static_cast(log10_2_significand >> (64 - shift_amount)) - + static_cast(log10_4_over_3_fractional_digits >> + (64 - shift_amount))) >> + shift_amount; } -inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept { - FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent"); - return (e * 631305 - 261663) >> 21; + +// Returns true iff x is divisible by pow(2, exp). +inline bool divisible_by_power_of_2(uint32_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp >= 1, ""); + FMT_ASSERT(x != 0, ""); +#ifdef FMT_BUILTIN_CTZ + return FMT_BUILTIN_CTZ(x) >= exp; +#else + return exp < num_bits() && x == ((x >> exp) << exp); +#endif +} +inline bool divisible_by_power_of_2(uint64_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp >= 1, ""); + FMT_ASSERT(x != 0, ""); +#ifdef FMT_BUILTIN_CTZLL + return FMT_BUILTIN_CTZLL(x) >= exp; +#else + return exp < num_bits() && x == ((x >> exp) << exp); +#endif } -static constexpr struct { - uint32_t divisor; - int shift_amount; -} div_small_pow10_infos[] = {{10, 16}, {100, 16}}; +// Table entry type for divisibility test. +template struct divtest_table_entry { + T mod_inv; + T max_quotient; +}; -// Replaces n by floor(n / pow(10, N)) returning true if and only if n is -// divisible by pow(10, N). -// Precondition: n <= pow(10, N + 1). +// Returns true iff x is divisible by pow(5, exp). +inline bool divisible_by_power_of_5(uint32_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp <= 10, "too large exponent"); + static constexpr const divtest_table_entry divtest_table[] = { + {0x00000001, 0xffffffff}, {0xcccccccd, 0x33333333}, + {0xc28f5c29, 0x0a3d70a3}, {0x26e978d5, 0x020c49ba}, + {0x3afb7e91, 0x0068db8b}, {0x0bcbe61d, 0x0014f8b5}, + {0x68c26139, 0x000431bd}, {0xae8d46a5, 0x0000d6bf}, + {0x22e90e21, 0x00002af3}, {0x3a2e9c6d, 0x00000897}, + {0x3ed61f49, 0x000001b7}}; + return x * divtest_table[exp].mod_inv <= divtest_table[exp].max_quotient; +} +inline bool divisible_by_power_of_5(uint64_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp <= 23, "too large exponent"); + static constexpr const divtest_table_entry divtest_table[] = { + {0x0000000000000001, 0xffffffffffffffff}, + {0xcccccccccccccccd, 0x3333333333333333}, + {0x8f5c28f5c28f5c29, 0x0a3d70a3d70a3d70}, + {0x1cac083126e978d5, 0x020c49ba5e353f7c}, + {0xd288ce703afb7e91, 0x0068db8bac710cb2}, + {0x5d4e8fb00bcbe61d, 0x0014f8b588e368f0}, + {0x790fb65668c26139, 0x000431bde82d7b63}, + {0xe5032477ae8d46a5, 0x0000d6bf94d5e57a}, + {0xc767074b22e90e21, 0x00002af31dc46118}, + {0x8e47ce423a2e9c6d, 0x0000089705f4136b}, + {0x4fa7f60d3ed61f49, 0x000001b7cdfd9d7b}, + {0x0fee64690c913975, 0x00000057f5ff85e5}, + {0x3662e0e1cf503eb1, 0x000000119799812d}, + {0xa47a2cf9f6433fbd, 0x0000000384b84d09}, + {0x54186f653140a659, 0x00000000b424dc35}, + {0x7738164770402145, 0x0000000024075f3d}, + {0xe4a4d1417cd9a041, 0x000000000734aca5}, + {0xc75429d9e5c5200d, 0x000000000170ef54}, + {0xc1773b91fac10669, 0x000000000049c977}, + {0x26b172506559ce15, 0x00000000000ec1e4}, + {0xd489e3a9addec2d1, 0x000000000002f394}, + {0x90e860bb892c8d5d, 0x000000000000971d}, + {0x502e79bf1b6f4f79, 0x0000000000001e39}, + {0xdcd618596be30fe5, 0x000000000000060b}}; + return x * divtest_table[exp].mod_inv <= divtest_table[exp].max_quotient; +} + +// Replaces n by floor(n / pow(5, N)) returning true if and only if n is +// divisible by pow(5, N). +// Precondition: n <= 2 * pow(5, N + 1). template -bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept { - // The numbers below are chosen such that: - // 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100, - // 2. nm mod 2^k < m if and only if n is divisible by d, - // where m is magic_number, k is shift_amount - // and d is divisor. - // - // Item 1 is a common technique of replacing division by a constant with - // multiplication, see e.g. "Division by Invariant Integers Using - // Multiplication" by Granlund and Montgomery (1994). magic_number (m) is set - // to ceil(2^k/d) for large enough k. - // The idea for item 2 originates from Schubfach. - constexpr auto info = div_small_pow10_infos[N - 1]; - FMT_ASSERT(n <= info.divisor * 10, "n is too large"); - constexpr uint32_t magic_number = - (1u << info.shift_amount) / info.divisor + 1; - n *= magic_number; - const uint32_t comparison_mask = (1u << info.shift_amount) - 1; - bool result = (n & comparison_mask) < magic_number; +bool check_divisibility_and_divide_by_pow5(uint32_t& n) FMT_NOEXCEPT { + static constexpr struct { + uint32_t magic_number; + int bits_for_comparison; + uint32_t threshold; + int shift_amount; + } infos[] = {{0xcccd, 16, 0x3333, 18}, {0xa429, 8, 0x0a, 20}}; + constexpr auto info = infos[N - 1]; + n *= info.magic_number; + const uint32_t comparison_mask = (1u << info.bits_for_comparison) - 1; + bool result = (n & comparison_mask) <= info.threshold; n >>= info.shift_amount; return result; } // Computes floor(n / pow(10, N)) for small n and N. // Precondition: n <= pow(10, N + 1). -template uint32_t small_division_by_pow10(uint32_t n) noexcept { - constexpr auto info = div_small_pow10_infos[N - 1]; - FMT_ASSERT(n <= info.divisor * 10, "n is too large"); - constexpr uint32_t magic_number = - (1u << info.shift_amount) / info.divisor + 1; - return (n * magic_number) >> info.shift_amount; +template uint32_t small_division_by_pow10(uint32_t n) FMT_NOEXCEPT { + static constexpr struct { + uint32_t magic_number; + int shift_amount; + uint32_t divisor_times_10; + } infos[] = {{0xcccd, 19, 100}, {0xa3d8, 22, 1000}}; + constexpr auto info = infos[N - 1]; + FMT_ASSERT(n <= info.divisor_times_10, "n is too large"); + return n * info.magic_number >> info.shift_amount; } // Computes floor(n / 10^(kappa + 1)) (float) -inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept { - // 1374389535 = ceil(2^37/100) - return static_cast((static_cast(n) * 1374389535) >> 37); +inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) FMT_NOEXCEPT { + return n / float_info::big_divisor; } // Computes floor(n / 10^(kappa + 1)) (double) -inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept { - // 2361183241434822607 = ceil(2^(64+7)/1000) - return umul128_upper64(n, 2361183241434822607ull) >> 7; +inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) FMT_NOEXCEPT { + return umul128_upper64(n, 0x83126e978d4fdf3c) >> 9; } // Various subroutines using pow10 cache @@ -920,7 +1048,7 @@ template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; using cache_entry_type = uint64_t; - static uint64_t get_cached_power(int k) noexcept { + static uint64_t get_cached_power(int k) FMT_NOEXCEPT { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); static constexpr const uint64_t pow10_significands[] = { @@ -943,65 +1071,54 @@ template <> struct cache_accessor { 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0, - 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940985, - 0xa18f07d736b90be6, 0xc9f2c9cd04674edf, 0xfc6f7c4045812297, - 0x9dc5ada82b70b59e, 0xc5371912364ce306, 0xf684df56c3e01bc7, - 0x9a130b963a6c115d, 0xc097ce7bc90715b4, 0xf0bdc21abb48db21, - 0x96769950b50d88f5, 0xbc143fa4e250eb32, 0xeb194f8e1ae525fe, - 0x92efd1b8d0cf37bf, 0xb7abc627050305ae, 0xe596b7b0c643c71a, - 0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f}; + 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940984, + 0xa18f07d736b90be5, 0xc9f2c9cd04674ede, 0xfc6f7c4045812296, + 0x9dc5ada82b70b59d, 0xc5371912364ce305, 0xf684df56c3e01bc6, + 0x9a130b963a6c115c, 0xc097ce7bc90715b3, 0xf0bdc21abb48db20, + 0x96769950b50d88f4, 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd, + 0x92efd1b8d0cf37be, 0xb7abc627050305ad, 0xe596b7b0c643c719, + 0x8f7e32ce7bea5c6f, 0xb35dbf821ae4f38b, 0xe0352f62a19e306e}; return pow10_significands[k - float_info::min_k]; } - struct compute_mul_result { - carrier_uint result; - bool is_integer; - }; - struct compute_mul_parity_result { - bool parity; - bool is_integer; - }; - - static compute_mul_result compute_mul( - carrier_uint u, const cache_entry_type& cache) noexcept { - auto r = umul96_upper64(u, cache); - return {static_cast(r >> 32), - static_cast(r) == 0}; + static carrier_uint compute_mul(carrier_uint u, + const cache_entry_type& cache) FMT_NOEXCEPT { + return umul96_upper32(u, cache); } static uint32_t compute_delta(const cache_entry_type& cache, - int beta) noexcept { - return static_cast(cache >> (64 - 1 - beta)); + int beta_minus_1) FMT_NOEXCEPT { + return static_cast(cache >> (64 - 1 - beta_minus_1)); } - static compute_mul_parity_result compute_mul_parity( - carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { - FMT_ASSERT(beta >= 1, ""); - FMT_ASSERT(beta < 64, ""); + static bool compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta_minus_1) FMT_NOEXCEPT { + FMT_ASSERT(beta_minus_1 >= 1, ""); + FMT_ASSERT(beta_minus_1 < 64, ""); - auto r = umul96_lower64(two_f, cache); - return {((r >> (64 - beta)) & 1) != 0, - static_cast(r >> (32 - beta)) == 0}; + return ((umul96_lower64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0; } static carrier_uint compute_left_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { return static_cast( - (cache - (cache >> (num_significand_bits() + 2))) >> - (64 - num_significand_bits() - 1 - beta)); + (cache - (cache >> (float_info::significand_bits + 2))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1)); } static carrier_uint compute_right_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { return static_cast( - (cache + (cache >> (num_significand_bits() + 1))) >> - (64 - num_significand_bits() - 1 - beta)); + (cache + (cache >> (float_info::significand_bits + 1))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1)); } static carrier_uint compute_round_up_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { return (static_cast( - cache >> (64 - num_significand_bits() - 2 - beta)) + + cache >> + (64 - float_info::significand_bits - 2 - beta_minus_1)) + 1) / 2; } @@ -1011,7 +1128,7 @@ template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; using cache_entry_type = uint128_wrapper; - static uint128_wrapper get_cached_power(int k) noexcept { + static uint128_wrapper get_cached_power(int k) FMT_NOEXCEPT { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); @@ -1365,278 +1482,278 @@ template <> struct cache_accessor { {0x85a36366eb71f041, 0x47a6da2b7f864750}, {0xa70c3c40a64e6c51, 0x999090b65f67d924}, {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d}, - {0x82818f1281ed449f, 0xbff8f10e7a8921a5}, - {0xa321f2d7226895c7, 0xaff72d52192b6a0e}, - {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764491}, - {0xfee50b7025c36a08, 0x02f236d04753d5b5}, - {0x9f4f2726179a2245, 0x01d762422c946591}, - {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef6}, - {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb3}, - {0x9b934c3b330c8577, 0x63cc55f49f88eb30}, - {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fc}, - {0xf316271c7fc3908a, 0x8bef464e3945ef7b}, - {0x97edd871cfda3a56, 0x97758bf0e3cbb5ad}, - {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea318}, - {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bde}, - {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6b}, - {0xb975d6b6ee39e436, 0xb3e2fd538e122b45}, - {0xe7d34c64a9c85d44, 0x60dbbca87196b617}, - {0x90e40fbeea1d3a4a, 0xbc8955e946fe31ce}, - {0xb51d13aea4a488dd, 0x6babab6398bdbe42}, - {0xe264589a4dcdab14, 0xc696963c7eed2dd2}, - {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca3}, - {0xb0de65388cc8ada8, 0x3b25a55f43294bcc}, - {0xdd15fe86affad912, 0x49ef0eb713f39ebf}, - {0x8a2dbf142dfcc7ab, 0x6e3569326c784338}, - {0xacb92ed9397bf996, 0x49c2c37f07965405}, - {0xd7e77a8f87daf7fb, 0xdc33745ec97be907}, - {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a4}, - {0xa8acd7c0222311bc, 0xc40832ea0d68ce0d}, - {0xd2d80db02aabd62b, 0xf50a3fa490c30191}, - {0x83c7088e1aab65db, 0x792667c6da79e0fb}, - {0xa4b8cab1a1563f52, 0x577001b891185939}, - {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87}, - {0x80b05e5ac60b6178, 0x544f8158315b05b5}, - {0xa0dc75f1778e39d6, 0x696361ae3db1c722}, - {0xc913936dd571c84c, 0x03bc3a19cd1e38ea}, - {0xfb5878494ace3a5f, 0x04ab48a04065c724}, - {0x9d174b2dcec0e47b, 0x62eb0d64283f9c77}, - {0xc45d1df942711d9a, 0x3ba5d0bd324f8395}, - {0xf5746577930d6500, 0xca8f44ec7ee3647a}, - {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecc}, - {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67f}, - {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101f}, - {0x95d04aee3b80ece5, 0xbba1f1d158724a13}, - {0xbb445da9ca61281f, 0x2a8a6e45ae8edc98}, - {0xea1575143cf97226, 0xf52d09d71a3293be}, - {0x924d692ca61be758, 0x593c2626705f9c57}, - {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836d}, - {0xe498f455c38b997a, 0x0b6dfb9c0f956448}, - {0x8edf98b59a373fec, 0x4724bd4189bd5ead}, - {0xb2977ee300c50fe7, 0x58edec91ec2cb658}, - {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ee}, - {0x8b865b215899f46c, 0xbd79e0d20082ee75}, - {0xae67f1e9aec07187, 0xecd8590680a3aa12}, - {0xda01ee641a708de9, 0xe80e6f4820cc9496}, - {0x884134fe908658b2, 0x3109058d147fdcde}, - {0xaa51823e34a7eede, 0xbd4b46f0599fd416}, - {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91b}, - {0x850fadc09923329e, 0x03e2cf6bc604ddb1}, - {0xa6539930bf6bff45, 0x84db8346b786151d}, - {0xcfe87f7cef46ff16, 0xe612641865679a64}, - {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07f}, - {0xa26da3999aef7749, 0xe3be5e330f38f09e}, - {0xcb090c8001ab551c, 0x5cadf5bfd3072cc6}, - {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f7}, - {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afb}, - {0xc646d63501a1511d, 0xb281e1fd541501b9}, - {0xf7d88bc24209a565, 0x1f225a7ca91a4227}, - {0x9ae757596946075f, 0x3375788de9b06959}, - {0xc1a12d2fc3978937, 0x0052d6b1641c83af}, - {0xf209787bb47d6b84, 0xc0678c5dbd23a49b}, - {0x9745eb4d50ce6332, 0xf840b7ba963646e1}, - {0xbd176620a501fbff, 0xb650e5a93bc3d899}, - {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebf}, - {0x93ba47c980e98cdf, 0xc66f336c36b10138}, - {0xb8a8d9bbe123f017, 0xb80b0047445d4185}, - {0xe6d3102ad96cec1d, 0xa60dc059157491e6}, - {0x9043ea1ac7e41392, 0x87c89837ad68db30}, - {0xb454e4a179dd1877, 0x29babe4598c311fc}, - {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67b}, - {0x8ce2529e2734bb1d, 0x1899e4a65f58660d}, - {0xb01ae745b101e9e4, 0x5ec05dcff72e7f90}, - {0xdc21a1171d42645d, 0x76707543f4fa1f74}, - {0x899504ae72497eba, 0x6a06494a791c53a9}, - {0xabfa45da0edbde69, 0x0487db9d17636893}, - {0xd6f8d7509292d603, 0x45a9d2845d3c42b7}, - {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3}, - {0xa7f26836f282b732, 0x8e6cac7768d7141f}, - {0xd1ef0244af2364ff, 0x3207d795430cd927}, - {0x8335616aed761f1f, 0x7f44e6bd49e807b9}, - {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a7}, - {0xcd036837130890a1, 0x36dba887c37a8c10}, - {0x802221226be55a64, 0xc2494954da2c978a}, - {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6d}, - {0xc83553c5c8965d3d, 0x6f92829494e5acc8}, - {0xfa42a8b73abbf48c, 0xcb772339ba1f17fa}, - {0x9c69a97284b578d7, 0xff2a760414536efc}, - {0xc38413cf25e2d70d, 0xfef5138519684abb}, - {0xf46518c2ef5b8cd1, 0x7eb258665fc25d6a}, - {0x98bf2f79d5993802, 0xef2f773ffbd97a62}, - {0xbeeefb584aff8603, 0xaafb550ffacfd8fb}, - {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf39}, - {0x952ab45cfa97a0b2, 0xdd945a747bf26184}, - {0xba756174393d88df, 0x94f971119aeef9e5}, - {0xe912b9d1478ceb17, 0x7a37cd5601aab85e}, - {0x91abb422ccb812ee, 0xac62e055c10ab33b}, - {0xb616a12b7fe617aa, 0x577b986b314d600a}, - {0xe39c49765fdf9d94, 0xed5a7e85fda0b80c}, - {0x8e41ade9fbebc27d, 0x14588f13be847308}, - {0xb1d219647ae6b31c, 0x596eb2d8ae258fc9}, - {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bc}, - {0x8aec23d680043bee, 0x25de7bb9480d5855}, - {0xada72ccc20054ae9, 0xaf561aa79a10ae6b}, - {0xd910f7ff28069da4, 0x1b2ba1518094da05}, - {0x87aa9aff79042286, 0x90fb44d2f05d0843}, - {0xa99541bf57452b28, 0x353a1607ac744a54}, - {0xd3fa922f2d1675f2, 0x42889b8997915ce9}, - {0x847c9b5d7c2e09b7, 0x69956135febada12}, - {0xa59bc234db398c25, 0x43fab9837e699096}, - {0xcf02b2c21207ef2e, 0x94f967e45e03f4bc}, - {0x8161afb94b44f57d, 0x1d1be0eebac278f6}, - {0xa1ba1ba79e1632dc, 0x6462d92a69731733}, - {0xca28a291859bbf93, 0x7d7b8f7503cfdcff}, - {0xfcb2cb35e702af78, 0x5cda735244c3d43f}, - {0x9defbf01b061adab, 0x3a0888136afa64a8}, - {0xc56baec21c7a1916, 0x088aaa1845b8fdd1}, - {0xf6c69a72a3989f5b, 0x8aad549e57273d46}, - {0x9a3c2087a63f6399, 0x36ac54e2f678864c}, - {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7de}, - {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d6}, - {0x969eb7c47859e743, 0x9f644ae5a4b1b326}, - {0xbc4665b596706114, 0x873d5d9f0dde1fef}, - {0xeb57ff22fc0c7959, 0xa90cb506d155a7eb}, - {0x9316ff75dd87cbd8, 0x09a7f12442d588f3}, - {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb30}, - {0xe5d3ef282a242e81, 0x8f1668c8a86da5fb}, - {0x8fa475791a569d10, 0xf96e017d694487bd}, - {0xb38d92d760ec4455, 0x37c981dcc395a9ad}, - {0xe070f78d3927556a, 0x85bbe253f47b1418}, - {0x8c469ab843b89562, 0x93956d7478ccec8f}, - {0xaf58416654a6babb, 0x387ac8d1970027b3}, - {0xdb2e51bfe9d0696a, 0x06997b05fcc0319f}, - {0x88fcf317f22241e2, 0x441fece3bdf81f04}, - {0xab3c2fddeeaad25a, 0xd527e81cad7626c4}, - {0xd60b3bd56a5586f1, 0x8a71e223d8d3b075}, - {0x85c7056562757456, 0xf6872d5667844e4a}, - {0xa738c6bebb12d16c, 0xb428f8ac016561dc}, - {0xd106f86e69d785c7, 0xe13336d701beba53}, - {0x82a45b450226b39c, 0xecc0024661173474}, - {0xa34d721642b06084, 0x27f002d7f95d0191}, - {0xcc20ce9bd35c78a5, 0x31ec038df7b441f5}, - {0xff290242c83396ce, 0x7e67047175a15272}, - {0x9f79a169bd203e41, 0x0f0062c6e984d387}, - {0xc75809c42c684dd1, 0x52c07b78a3e60869}, - {0xf92e0c3537826145, 0xa7709a56ccdf8a83}, - {0x9bbcc7a142b17ccb, 0x88a66076400bb692}, - {0xc2abf989935ddbfe, 0x6acff893d00ea436}, - {0xf356f7ebf83552fe, 0x0583f6b8c4124d44}, - {0x98165af37b2153de, 0xc3727a337a8b704b}, - {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5d}, - {0xeda2ee1c7064130c, 0x1162def06f79df74}, - {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba9}, - {0xb9a74a0637ce2ee1, 0x6d953e2bd7173693}, - {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0438}, - {0x910ab1d4db9914a0, 0x1d9c9892400a22a3}, - {0xb54d5e4a127f59c8, 0x2503beb6d00cab4c}, - {0xe2a0b5dc971f303a, 0x2e44ae64840fd61e}, - {0x8da471a9de737e24, 0x5ceaecfed289e5d3}, - {0xb10d8e1456105dad, 0x7425a83e872c5f48}, - {0xdd50f1996b947518, 0xd12f124e28f7771a}, - {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa70}, - {0xace73cbfdc0bfb7b, 0x636cc64d1001550c}, - {0xd8210befd30efa5a, 0x3c47f7e05401aa4f}, - {0x8714a775e3e95c78, 0x65acfaec34810a72}, - {0xa8d9d1535ce3b396, 0x7f1839a741a14d0e}, - {0xd31045a8341ca07c, 0x1ede48111209a051}, - {0x83ea2b892091e44d, 0x934aed0aab460433}, - {0xa4e4b66b68b65d60, 0xf81da84d56178540}, - {0xce1de40642e3f4b9, 0x36251260ab9d668f}, - {0x80d2ae83e9ce78f3, 0xc1d72b7c6b42601a}, - {0xa1075a24e4421730, 0xb24cf65b8612f820}, - {0xc94930ae1d529cfc, 0xdee033f26797b628}, - {0xfb9b7cd9a4a7443c, 0x169840ef017da3b2}, - {0x9d412e0806e88aa5, 0x8e1f289560ee864f}, - {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e3}, - {0xf5b5d7ec8acb58a2, 0xae10af696774b1dc}, - {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef2a}, - {0xbff610b0cc6edd3f, 0x17fd090a58d32af4}, - {0xeff394dcff8a948e, 0xddfc4b4cef07f5b1}, - {0x95f83d0a1fb69cd9, 0x4abdaf101564f98f}, - {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f2}, - {0xea53df5fd18d5513, 0x84c86189216dc5ee}, - {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb5}, - {0xb7118682dbb66a77, 0x3fbc8c33221dc2a2}, - {0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, - {0x8f05b1163ba6832d, 0x29cb4d87f2a7400f}, - {0xb2c71d5bca9023f8, 0x743e20e9ef511013}, - {0xdf78e4b2bd342cf6, 0x914da9246b255417}, - {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548f}, - {0xae9672aba3d0c320, 0xa184ac2473b529b2}, - {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741f}, - {0x8865899617fb1871, 0x7e2fa67c7a658893}, - {0xaa7eebfb9df9de8d, 0xddbb901b98feeab8}, - {0xd51ea6fa85785631, 0x552a74227f3ea566}, - {0x8533285c936b35de, 0xd53a88958f872760}, - {0xa67ff273b8460356, 0x8a892abaf368f138}, - {0xd01fef10a657842c, 0x2d2b7569b0432d86}, - {0x8213f56a67f6b29b, 0x9c3b29620e29fc74}, - {0xa298f2c501f45f42, 0x8349f3ba91b47b90}, - {0xcb3f2f7642717713, 0x241c70a936219a74}, - {0xfe0efb53d30dd4d7, 0xed238cd383aa0111}, - {0x9ec95d1463e8a506, 0xf4363804324a40ab}, - {0xc67bb4597ce2ce48, 0xb143c6053edcd0d6}, - {0xf81aa16fdc1b81da, 0xdd94b7868e94050b}, - {0x9b10a4e5e9913128, 0xca7cf2b4191c8327}, - {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f1}, - {0xf24a01a73cf2dccf, 0xbc633b39673c8ced}, - {0x976e41088617ca01, 0xd5be0503e085d814}, - {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e19}, - {0xec9c459d51852ba2, 0xddf8e7d60ed1219f}, - {0x93e1ab8252f33b45, 0xcabb90e5c942b504}, - {0xb8da1662e7b00a17, 0x3d6a751f3b936244}, - {0xe7109bfba19c0c9d, 0x0cc512670a783ad5}, - {0x906a617d450187e2, 0x27fb2b80668b24c6}, - {0xb484f9dc9641e9da, 0xb1f9f660802dedf7}, - {0xe1a63853bbd26451, 0x5e7873f8a0396974}, - {0x8d07e33455637eb2, 0xdb0b487b6423e1e9}, - {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda63}, - {0xdc5c5301c56b75f7, 0x7641a140cc7810fc}, - {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9e}, - {0xac2820d9623bf429, 0x546345fa9fbdcd45}, - {0xd732290fbacaf133, 0xa97c177947ad4096}, - {0x867f59a9d4bed6c0, 0x49ed8eabcccc485e}, - {0xa81f301449ee8c70, 0x5c68f256bfff5a75}, - {0xd226fc195c6a2f8c, 0x73832eec6fff3112}, - {0x83585d8fd9c25db7, 0xc831fd53c5ff7eac}, - {0xa42e74f3d032f525, 0xba3e7ca8b77f5e56}, - {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35ec}, - {0x80444b5e7aa7cf85, 0x7980d163cf5b81b4}, - {0xa0555e361951c366, 0xd7e105bcc3326220}, - {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa8}, - {0xfa856334878fc150, 0xb14f98f6f0feb952}, - {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d4}, - {0xc3b8358109e84f07, 0x0a862f80ec4700c9}, - {0xf4a642e14c6262c8, 0xcd27bb612758c0fb}, - {0x98e7e9cccfbd7dbd, 0x8038d51cb897789d}, - {0xbf21e44003acdd2c, 0xe0470a63e6bd56c4}, - {0xeeea5d5004981478, 0x1858ccfce06cac75}, - {0x95527a5202df0ccb, 0x0f37801e0c43ebc9}, - {0xbaa718e68396cffd, 0xd30560258f54e6bb}, - {0xe950df20247c83fd, 0x47c6b82ef32a206a}, - {0x91d28b7416cdd27e, 0x4cdc331d57fa5442}, - {0xb6472e511c81471d, 0xe0133fe4adf8e953}, - {0xe3d8f9e563a198e5, 0x58180fddd97723a7}, - {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7649}, - {0xb201833b35d63f73, 0x2cd2cc6551e513db}, - {0xde81e40a034bcf4f, 0xf8077f7ea65e58d2}, - {0x8b112e86420f6191, 0xfb04afaf27faf783}, - {0xadd57a27d29339f6, 0x79c5db9af1f9b564}, - {0xd94ad8b1c7380874, 0x18375281ae7822bd}, - {0x87cec76f1c830548, 0x8f2293910d0b15b6}, - {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb23}, - {0xd433179d9c8cb841, 0x5fa60692a46151ec}, - {0x849feec281d7f328, 0xdbc7c41ba6bcd334}, - {0xa5c7ea73224deff3, 0x12b9b522906c0801}, - {0xcf39e50feae16bef, 0xd768226b34870a01}, - {0x81842f29f2cce375, 0xe6a1158300d46641}, - {0xa1e53af46f801c53, 0x60495ae3c1097fd1}, - {0xca5e89b18b602368, 0x385bb19cb14bdfc5}, - {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6}, - {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2}, - {0xc5a05277621be293, 0xc7098b7305241886}, + {0x82818f1281ed449f, 0xbff8f10e7a8921a4}, + {0xa321f2d7226895c7, 0xaff72d52192b6a0d}, + {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490}, + {0xfee50b7025c36a08, 0x02f236d04753d5b4}, + {0x9f4f2726179a2245, 0x01d762422c946590}, + {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5}, + {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2}, + {0x9b934c3b330c8577, 0x63cc55f49f88eb2f}, + {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb}, + {0xf316271c7fc3908a, 0x8bef464e3945ef7a}, + {0x97edd871cfda3a56, 0x97758bf0e3cbb5ac}, + {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317}, + {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd}, + {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a}, + {0xb975d6b6ee39e436, 0xb3e2fd538e122b44}, + {0xe7d34c64a9c85d44, 0x60dbbca87196b616}, + {0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd}, + {0xb51d13aea4a488dd, 0x6babab6398bdbe41}, + {0xe264589a4dcdab14, 0xc696963c7eed2dd1}, + {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2}, + {0xb0de65388cc8ada8, 0x3b25a55f43294bcb}, + {0xdd15fe86affad912, 0x49ef0eb713f39ebe}, + {0x8a2dbf142dfcc7ab, 0x6e3569326c784337}, + {0xacb92ed9397bf996, 0x49c2c37f07965404}, + {0xd7e77a8f87daf7fb, 0xdc33745ec97be906}, + {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3}, + {0xa8acd7c0222311bc, 0xc40832ea0d68ce0c}, + {0xd2d80db02aabd62b, 0xf50a3fa490c30190}, + {0x83c7088e1aab65db, 0x792667c6da79e0fa}, + {0xa4b8cab1a1563f52, 0x577001b891185938}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86}, + {0x80b05e5ac60b6178, 0x544f8158315b05b4}, + {0xa0dc75f1778e39d6, 0x696361ae3db1c721}, + {0xc913936dd571c84c, 0x03bc3a19cd1e38e9}, + {0xfb5878494ace3a5f, 0x04ab48a04065c723}, + {0x9d174b2dcec0e47b, 0x62eb0d64283f9c76}, + {0xc45d1df942711d9a, 0x3ba5d0bd324f8394}, + {0xf5746577930d6500, 0xca8f44ec7ee36479}, + {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb}, + {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e}, + {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e}, + {0x95d04aee3b80ece5, 0xbba1f1d158724a12}, + {0xbb445da9ca61281f, 0x2a8a6e45ae8edc97}, + {0xea1575143cf97226, 0xf52d09d71a3293bd}, + {0x924d692ca61be758, 0x593c2626705f9c56}, + {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c}, + {0xe498f455c38b997a, 0x0b6dfb9c0f956447}, + {0x8edf98b59a373fec, 0x4724bd4189bd5eac}, + {0xb2977ee300c50fe7, 0x58edec91ec2cb657}, + {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed}, + {0x8b865b215899f46c, 0xbd79e0d20082ee74}, + {0xae67f1e9aec07187, 0xecd8590680a3aa11}, + {0xda01ee641a708de9, 0xe80e6f4820cc9495}, + {0x884134fe908658b2, 0x3109058d147fdcdd}, + {0xaa51823e34a7eede, 0xbd4b46f0599fd415}, + {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a}, + {0x850fadc09923329e, 0x03e2cf6bc604ddb0}, + {0xa6539930bf6bff45, 0x84db8346b786151c}, + {0xcfe87f7cef46ff16, 0xe612641865679a63}, + {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e}, + {0xa26da3999aef7749, 0xe3be5e330f38f09d}, + {0xcb090c8001ab551c, 0x5cadf5bfd3072cc5}, + {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6}, + {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa}, + {0xc646d63501a1511d, 0xb281e1fd541501b8}, + {0xf7d88bc24209a565, 0x1f225a7ca91a4226}, + {0x9ae757596946075f, 0x3375788de9b06958}, + {0xc1a12d2fc3978937, 0x0052d6b1641c83ae}, + {0xf209787bb47d6b84, 0xc0678c5dbd23a49a}, + {0x9745eb4d50ce6332, 0xf840b7ba963646e0}, + {0xbd176620a501fbff, 0xb650e5a93bc3d898}, + {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe}, + {0x93ba47c980e98cdf, 0xc66f336c36b10137}, + {0xb8a8d9bbe123f017, 0xb80b0047445d4184}, + {0xe6d3102ad96cec1d, 0xa60dc059157491e5}, + {0x9043ea1ac7e41392, 0x87c89837ad68db2f}, + {0xb454e4a179dd1877, 0x29babe4598c311fb}, + {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a}, + {0x8ce2529e2734bb1d, 0x1899e4a65f58660c}, + {0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f}, + {0xdc21a1171d42645d, 0x76707543f4fa1f73}, + {0x899504ae72497eba, 0x6a06494a791c53a8}, + {0xabfa45da0edbde69, 0x0487db9d17636892}, + {0xd6f8d7509292d603, 0x45a9d2845d3c42b6}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2}, + {0xa7f26836f282b732, 0x8e6cac7768d7141e}, + {0xd1ef0244af2364ff, 0x3207d795430cd926}, + {0x8335616aed761f1f, 0x7f44e6bd49e807b8}, + {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6}, + {0xcd036837130890a1, 0x36dba887c37a8c0f}, + {0x802221226be55a64, 0xc2494954da2c9789}, + {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c}, + {0xc83553c5c8965d3d, 0x6f92829494e5acc7}, + {0xfa42a8b73abbf48c, 0xcb772339ba1f17f9}, + {0x9c69a97284b578d7, 0xff2a760414536efb}, + {0xc38413cf25e2d70d, 0xfef5138519684aba}, + {0xf46518c2ef5b8cd1, 0x7eb258665fc25d69}, + {0x98bf2f79d5993802, 0xef2f773ffbd97a61}, + {0xbeeefb584aff8603, 0xaafb550ffacfd8fa}, + {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38}, + {0x952ab45cfa97a0b2, 0xdd945a747bf26183}, + {0xba756174393d88df, 0x94f971119aeef9e4}, + {0xe912b9d1478ceb17, 0x7a37cd5601aab85d}, + {0x91abb422ccb812ee, 0xac62e055c10ab33a}, + {0xb616a12b7fe617aa, 0x577b986b314d6009}, + {0xe39c49765fdf9d94, 0xed5a7e85fda0b80b}, + {0x8e41ade9fbebc27d, 0x14588f13be847307}, + {0xb1d219647ae6b31c, 0x596eb2d8ae258fc8}, + {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb}, + {0x8aec23d680043bee, 0x25de7bb9480d5854}, + {0xada72ccc20054ae9, 0xaf561aa79a10ae6a}, + {0xd910f7ff28069da4, 0x1b2ba1518094da04}, + {0x87aa9aff79042286, 0x90fb44d2f05d0842}, + {0xa99541bf57452b28, 0x353a1607ac744a53}, + {0xd3fa922f2d1675f2, 0x42889b8997915ce8}, + {0x847c9b5d7c2e09b7, 0x69956135febada11}, + {0xa59bc234db398c25, 0x43fab9837e699095}, + {0xcf02b2c21207ef2e, 0x94f967e45e03f4bb}, + {0x8161afb94b44f57d, 0x1d1be0eebac278f5}, + {0xa1ba1ba79e1632dc, 0x6462d92a69731732}, + {0xca28a291859bbf93, 0x7d7b8f7503cfdcfe}, + {0xfcb2cb35e702af78, 0x5cda735244c3d43e}, + {0x9defbf01b061adab, 0x3a0888136afa64a7}, + {0xc56baec21c7a1916, 0x088aaa1845b8fdd0}, + {0xf6c69a72a3989f5b, 0x8aad549e57273d45}, + {0x9a3c2087a63f6399, 0x36ac54e2f678864b}, + {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd}, + {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5}, + {0x969eb7c47859e743, 0x9f644ae5a4b1b325}, + {0xbc4665b596706114, 0x873d5d9f0dde1fee}, + {0xeb57ff22fc0c7959, 0xa90cb506d155a7ea}, + {0x9316ff75dd87cbd8, 0x09a7f12442d588f2}, + {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb2f}, + {0xe5d3ef282a242e81, 0x8f1668c8a86da5fa}, + {0x8fa475791a569d10, 0xf96e017d694487bc}, + {0xb38d92d760ec4455, 0x37c981dcc395a9ac}, + {0xe070f78d3927556a, 0x85bbe253f47b1417}, + {0x8c469ab843b89562, 0x93956d7478ccec8e}, + {0xaf58416654a6babb, 0x387ac8d1970027b2}, + {0xdb2e51bfe9d0696a, 0x06997b05fcc0319e}, + {0x88fcf317f22241e2, 0x441fece3bdf81f03}, + {0xab3c2fddeeaad25a, 0xd527e81cad7626c3}, + {0xd60b3bd56a5586f1, 0x8a71e223d8d3b074}, + {0x85c7056562757456, 0xf6872d5667844e49}, + {0xa738c6bebb12d16c, 0xb428f8ac016561db}, + {0xd106f86e69d785c7, 0xe13336d701beba52}, + {0x82a45b450226b39c, 0xecc0024661173473}, + {0xa34d721642b06084, 0x27f002d7f95d0190}, + {0xcc20ce9bd35c78a5, 0x31ec038df7b441f4}, + {0xff290242c83396ce, 0x7e67047175a15271}, + {0x9f79a169bd203e41, 0x0f0062c6e984d386}, + {0xc75809c42c684dd1, 0x52c07b78a3e60868}, + {0xf92e0c3537826145, 0xa7709a56ccdf8a82}, + {0x9bbcc7a142b17ccb, 0x88a66076400bb691}, + {0xc2abf989935ddbfe, 0x6acff893d00ea435}, + {0xf356f7ebf83552fe, 0x0583f6b8c4124d43}, + {0x98165af37b2153de, 0xc3727a337a8b704a}, + {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c}, + {0xeda2ee1c7064130c, 0x1162def06f79df73}, + {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8}, + {0xb9a74a0637ce2ee1, 0x6d953e2bd7173692}, + {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437}, + {0x910ab1d4db9914a0, 0x1d9c9892400a22a2}, + {0xb54d5e4a127f59c8, 0x2503beb6d00cab4b}, + {0xe2a0b5dc971f303a, 0x2e44ae64840fd61d}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d2}, + {0xb10d8e1456105dad, 0x7425a83e872c5f47}, + {0xdd50f1996b947518, 0xd12f124e28f77719}, + {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f}, + {0xace73cbfdc0bfb7b, 0x636cc64d1001550b}, + {0xd8210befd30efa5a, 0x3c47f7e05401aa4e}, + {0x8714a775e3e95c78, 0x65acfaec34810a71}, + {0xa8d9d1535ce3b396, 0x7f1839a741a14d0d}, + {0xd31045a8341ca07c, 0x1ede48111209a050}, + {0x83ea2b892091e44d, 0x934aed0aab460432}, + {0xa4e4b66b68b65d60, 0xf81da84d5617853f}, + {0xce1de40642e3f4b9, 0x36251260ab9d668e}, + {0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019}, + {0xa1075a24e4421730, 0xb24cf65b8612f81f}, + {0xc94930ae1d529cfc, 0xdee033f26797b627}, + {0xfb9b7cd9a4a7443c, 0x169840ef017da3b1}, + {0x9d412e0806e88aa5, 0x8e1f289560ee864e}, + {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2}, + {0xf5b5d7ec8acb58a2, 0xae10af696774b1db}, + {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29}, + {0xbff610b0cc6edd3f, 0x17fd090a58d32af3}, + {0xeff394dcff8a948e, 0xddfc4b4cef07f5b0}, + {0x95f83d0a1fb69cd9, 0x4abdaf101564f98e}, + {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1}, + {0xea53df5fd18d5513, 0x84c86189216dc5ed}, + {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4}, + {0xb7118682dbb66a77, 0x3fbc8c33221dc2a1}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334a}, + {0x8f05b1163ba6832d, 0x29cb4d87f2a7400e}, + {0xb2c71d5bca9023f8, 0x743e20e9ef511012}, + {0xdf78e4b2bd342cf6, 0x914da9246b255416}, + {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e}, + {0xae9672aba3d0c320, 0xa184ac2473b529b1}, + {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e}, + {0x8865899617fb1871, 0x7e2fa67c7a658892}, + {0xaa7eebfb9df9de8d, 0xddbb901b98feeab7}, + {0xd51ea6fa85785631, 0x552a74227f3ea565}, + {0x8533285c936b35de, 0xd53a88958f87275f}, + {0xa67ff273b8460356, 0x8a892abaf368f137}, + {0xd01fef10a657842c, 0x2d2b7569b0432d85}, + {0x8213f56a67f6b29b, 0x9c3b29620e29fc73}, + {0xa298f2c501f45f42, 0x8349f3ba91b47b8f}, + {0xcb3f2f7642717713, 0x241c70a936219a73}, + {0xfe0efb53d30dd4d7, 0xed238cd383aa0110}, + {0x9ec95d1463e8a506, 0xf4363804324a40aa}, + {0xc67bb4597ce2ce48, 0xb143c6053edcd0d5}, + {0xf81aa16fdc1b81da, 0xdd94b7868e94050a}, + {0x9b10a4e5e9913128, 0xca7cf2b4191c8326}, + {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0}, + {0xf24a01a73cf2dccf, 0xbc633b39673c8cec}, + {0x976e41088617ca01, 0xd5be0503e085d813}, + {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18}, + {0xec9c459d51852ba2, 0xddf8e7d60ed1219e}, + {0x93e1ab8252f33b45, 0xcabb90e5c942b503}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936243}, + {0xe7109bfba19c0c9d, 0x0cc512670a783ad4}, + {0x906a617d450187e2, 0x27fb2b80668b24c5}, + {0xb484f9dc9641e9da, 0xb1f9f660802dedf6}, + {0xe1a63853bbd26451, 0x5e7873f8a0396973}, + {0x8d07e33455637eb2, 0xdb0b487b6423e1e8}, + {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62}, + {0xdc5c5301c56b75f7, 0x7641a140cc7810fb}, + {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d}, + {0xac2820d9623bf429, 0x546345fa9fbdcd44}, + {0xd732290fbacaf133, 0xa97c177947ad4095}, + {0x867f59a9d4bed6c0, 0x49ed8eabcccc485d}, + {0xa81f301449ee8c70, 0x5c68f256bfff5a74}, + {0xd226fc195c6a2f8c, 0x73832eec6fff3111}, + {0x83585d8fd9c25db7, 0xc831fd53c5ff7eab}, + {0xa42e74f3d032f525, 0xba3e7ca8b77f5e55}, + {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb}, + {0x80444b5e7aa7cf85, 0x7980d163cf5b81b3}, + {0xa0555e361951c366, 0xd7e105bcc332621f}, + {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7}, + {0xfa856334878fc150, 0xb14f98f6f0feb951}, + {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3}, + {0xc3b8358109e84f07, 0x0a862f80ec4700c8}, + {0xf4a642e14c6262c8, 0xcd27bb612758c0fa}, + {0x98e7e9cccfbd7dbd, 0x8038d51cb897789c}, + {0xbf21e44003acdd2c, 0xe0470a63e6bd56c3}, + {0xeeea5d5004981478, 0x1858ccfce06cac74}, + {0x95527a5202df0ccb, 0x0f37801e0c43ebc8}, + {0xbaa718e68396cffd, 0xd30560258f54e6ba}, + {0xe950df20247c83fd, 0x47c6b82ef32a2069}, + {0x91d28b7416cdd27e, 0x4cdc331d57fa5441}, + {0xb6472e511c81471d, 0xe0133fe4adf8e952}, + {0xe3d8f9e563a198e5, 0x58180fddd97723a6}, + {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648}, + {0xb201833b35d63f73, 0x2cd2cc6551e513da}, + {0xde81e40a034bcf4f, 0xf8077f7ea65e58d1}, + {0x8b112e86420f6191, 0xfb04afaf27faf782}, + {0xadd57a27d29339f6, 0x79c5db9af1f9b563}, + {0xd94ad8b1c7380874, 0x18375281ae7822bc}, + {0x87cec76f1c830548, 0x8f2293910d0b15b5}, + {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb22}, + {0xd433179d9c8cb841, 0x5fa60692a46151eb}, + {0x849feec281d7f328, 0xdbc7c41ba6bcd333}, + {0xa5c7ea73224deff3, 0x12b9b522906c0800}, + {0xcf39e50feae16bef, 0xd768226b34870a00}, + {0x81842f29f2cce375, 0xe6a1158300d46640}, + {0xa1e53af46f801c53, 0x60495ae3c1097fd0}, + {0xca5e89b18b602368, 0x385bb19cb14bdfc4}, + {0xfcf62c1dee382c42, 0x46729e03dd9ed7b5}, + {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d1}, + {0xc5a05277621be293, 0xc7098b7305241885}, { 0xf70867153aa2db38, - 0xb8cbee4fc66d1ea8 } + 0xb8cbee4fc66d1ea7 } #else {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, @@ -1651,17 +1768,17 @@ template <> struct cache_accessor { {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, {0xc350000000000000, 0x0000000000000000}, {0x9dc5ada82b70b59d, 0xf020000000000000}, - {0xfee50b7025c36a08, 0x02f236d04753d5b5}, - {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87}, - {0xa6539930bf6bff45, 0x84db8346b786151d}, - {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3}, - {0xd910f7ff28069da4, 0x1b2ba1518094da05}, - {0xaf58416654a6babb, 0x387ac8d1970027b3}, - {0x8da471a9de737e24, 0x5ceaecfed289e5d3}, - {0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, - {0xb8da1662e7b00a17, 0x3d6a751f3b936244}, + {0xfee50b7025c36a08, 0x02f236d04753d5b4}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86}, + {0xa6539930bf6bff45, 0x84db8346b786151c}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2}, + {0xd910f7ff28069da4, 0x1b2ba1518094da04}, + {0xaf58416654a6babb, 0x387ac8d1970027b2}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d2}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334a}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936243}, { 0x95527a5202df0ccb, - 0x0f37801e0c43ebc9 } + 0x0f37801e0c43ebc8 } #endif }; @@ -1679,6 +1796,15 @@ template <> struct cache_accessor { 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, 0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9}; + static constexpr const uint32_t pow10_recovery_errors[] = { + 0x50001400, 0x54044100, 0x54014555, 0x55954415, 0x54115555, 0x00000001, + 0x50000000, 0x00104000, 0x54010004, 0x05004001, 0x55555544, 0x41545555, + 0x54040551, 0x15445545, 0x51555514, 0x10000015, 0x00101100, 0x01100015, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04450514, 0x45414110, + 0x55555145, 0x50544050, 0x15040155, 0x11054140, 0x50111514, 0x11451454, + 0x00400541, 0x00000000, 0x55555450, 0x10056551, 0x10054011, 0x55551014, + 0x69514555, 0x05151109, 0x00155555}; + static const int compression_ratio = 27; // Compute base index. @@ -1697,7 +1823,8 @@ template <> struct cache_accessor { // Try to recover the real cache. uint64_t pow5 = powers_of_5_64[offset]; uint128_wrapper recovered_cache = umul128(base_cache.high(), pow5); - uint128_wrapper middle_low = umul128(base_cache.low(), pow5); + uint128_wrapper middle_low = + umul128(base_cache.low() - (kb < 0 ? 1u : 0u), pow5); recovered_cache += middle_low.high(); @@ -1707,58 +1834,58 @@ template <> struct cache_accessor { recovered_cache = uint128_wrapper{(recovered_cache.low() >> alpha) | high_to_middle, ((middle_low.low() >> alpha) | middle_to_low)}; - FMT_ASSERT(recovered_cache.low() + 1 != 0, ""); - return {recovered_cache.high(), recovered_cache.low() + 1}; + + if (kb < 0) recovered_cache += 1; + + // Get error. + int error_idx = (k - float_info::min_k) / 16; + uint32_t error = (pow10_recovery_errors[error_idx] >> + ((k - float_info::min_k) % 16) * 2) & + 0x3; + + // Add the error back. + FMT_ASSERT(recovered_cache.low() + error >= recovered_cache.low(), ""); + return {recovered_cache.high(), recovered_cache.low() + error}; #endif } - struct compute_mul_result { - carrier_uint result; - bool is_integer; - }; - struct compute_mul_parity_result { - bool parity; - bool is_integer; - }; - - static compute_mul_result compute_mul( - carrier_uint u, const cache_entry_type& cache) noexcept { - auto r = umul192_upper128(u, cache); - return {r.high(), r.low() == 0}; + static carrier_uint compute_mul(carrier_uint u, + const cache_entry_type& cache) FMT_NOEXCEPT { + return umul192_upper64(u, cache); } static uint32_t compute_delta(cache_entry_type const& cache, - int beta) noexcept { - return static_cast(cache.high() >> (64 - 1 - beta)); + int beta_minus_1) FMT_NOEXCEPT { + return static_cast(cache.high() >> (64 - 1 - beta_minus_1)); } - static compute_mul_parity_result compute_mul_parity( - carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { - FMT_ASSERT(beta >= 1, ""); - FMT_ASSERT(beta < 64, ""); + static bool compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta_minus_1) FMT_NOEXCEPT { + FMT_ASSERT(beta_minus_1 >= 1, ""); + FMT_ASSERT(beta_minus_1 < 64, ""); - auto r = umul192_lower128(two_f, cache); - return {((r.high() >> (64 - beta)) & 1) != 0, - ((r.high() << beta) | (r.low() >> (64 - beta))) == 0}; + return ((umul192_middle64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0; } static carrier_uint compute_left_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { return (cache.high() - - (cache.high() >> (num_significand_bits() + 2))) >> - (64 - num_significand_bits() - 1 - beta); + (cache.high() >> (float_info::significand_bits + 2))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1); } static carrier_uint compute_right_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { return (cache.high() + - (cache.high() >> (num_significand_bits() + 1))) >> - (64 - num_significand_bits() - 1 - beta); + (cache.high() >> (float_info::significand_bits + 1))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1); } static carrier_uint compute_round_up_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { - return ((cache.high() >> (64 - num_significand_bits() - 2 - beta)) + + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return ((cache.high() >> + (64 - float_info::significand_bits - 2 - beta_minus_1)) + 1) / 2; } @@ -1766,104 +1893,166 @@ template <> struct cache_accessor { // Various integer checks template -bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept { - const int case_shorter_interval_left_endpoint_lower_threshold = 2; - const int case_shorter_interval_left_endpoint_upper_threshold = 3; - return exponent >= case_shorter_interval_left_endpoint_lower_threshold && - exponent <= case_shorter_interval_left_endpoint_upper_threshold; +bool is_left_endpoint_integer_shorter_interval(int exponent) FMT_NOEXCEPT { + return exponent >= + float_info< + T>::case_shorter_interval_left_endpoint_lower_threshold && + exponent <= + float_info::case_shorter_interval_left_endpoint_upper_threshold; +} +template +bool is_endpoint_integer(typename float_info::carrier_uint two_f, + int exponent, int minus_k) FMT_NOEXCEPT { + if (exponent < float_info::case_fc_pm_half_lower_threshold) return false; + // For k >= 0. + if (exponent <= float_info::case_fc_pm_half_upper_threshold) return true; + // For k < 0. + if (exponent > float_info::divisibility_check_by_5_threshold) return false; + return divisible_by_power_of_5(two_f, minus_k); +} + +template +bool is_center_integer(typename float_info::carrier_uint two_f, int exponent, + int minus_k) FMT_NOEXCEPT { + // Exponent for 5 is negative. + if (exponent > float_info::divisibility_check_by_5_threshold) return false; + if (exponent > float_info::case_fc_upper_threshold) + return divisible_by_power_of_5(two_f, minus_k); + // Both exponents are nonnegative. + if (exponent >= float_info::case_fc_lower_threshold) return true; + // Exponent for 2 is negative. + return divisible_by_power_of_2(two_f, minus_k - exponent + 1); } // Remove trailing zeros from n and return the number of zeros removed (float) -FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept { - FMT_ASSERT(n != 0, ""); - const uint32_t mod_inv_5 = 0xcccccccd; - const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5; +FMT_INLINE int remove_trailing_zeros(uint32_t& n) FMT_NOEXCEPT { +#ifdef FMT_BUILTIN_CTZ + int t = FMT_BUILTIN_CTZ(n); +#else + int t = ctz(n); +#endif + if (t > float_info::max_trailing_zeros) + t = float_info::max_trailing_zeros; + + const uint32_t mod_inv1 = 0xcccccccd; + const uint32_t max_quotient1 = 0x33333333; + const uint32_t mod_inv2 = 0xc28f5c29; + const uint32_t max_quotient2 = 0x0a3d70a3; int s = 0; - while (true) { - auto q = rotr(n * mod_inv_25, 2); - if (q > max_value() / 100) break; - n = q; - s += 2; + for (; s < t - 1; s += 2) { + if (n * mod_inv2 > max_quotient2) break; + n *= mod_inv2; } - auto q = rotr(n * mod_inv_5, 1); - if (q <= max_value() / 10) { - n = q; - s |= 1; + if (s < t && n * mod_inv1 <= max_quotient1) { + n *= mod_inv1; + ++s; } - + n >>= s; return s; } // Removes trailing zeros and returns the number of zeros removed (double) -FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { - FMT_ASSERT(n != 0, ""); - - // This magic number is ceil(2^90 / 10^8). - constexpr uint64_t magic_number = 12379400392853802749ull; - auto nm = umul128(n, magic_number); - - // Is n is divisible by 10^8? - if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) { - // If yes, work with the quotient. - auto n32 = static_cast(nm.high() >> (90 - 64)); - - const uint32_t mod_inv_5 = 0xcccccccd; - const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5; - - int s = 8; - while (true) { - auto q = rotr(n32 * mod_inv_25, 2); - if (q > max_value() / 100) break; - n32 = q; - s += 2; - } - auto q = rotr(n32 * mod_inv_5, 1); - if (q <= max_value() / 10) { - n32 = q; - s |= 1; +FMT_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT { +#ifdef FMT_BUILTIN_CTZLL + int t = FMT_BUILTIN_CTZLL(n); +#else + int t = ctzll(n); +#endif + if (t > float_info::max_trailing_zeros) + t = float_info::max_trailing_zeros; + // Divide by 10^8 and reduce to 32-bits + // Since ret_value.significand <= (2^64 - 1) / 1000 < 10^17, + // both of the quotient and the r should fit in 32-bits + + const uint32_t mod_inv1 = 0xcccccccd; + const uint32_t max_quotient1 = 0x33333333; + const uint64_t mod_inv8 = 0xc767074b22e90e21; + const uint64_t max_quotient8 = 0x00002af31dc46118; + + // If the number is divisible by 1'0000'0000, work with the quotient + if (t >= 8) { + auto quotient_candidate = n * mod_inv8; + + if (quotient_candidate <= max_quotient8) { + auto quotient = static_cast(quotient_candidate >> 8); + + int s = 8; + for (; s < t; ++s) { + if (quotient * mod_inv1 > max_quotient1) break; + quotient *= mod_inv1; + } + quotient >>= (s - 8); + n = quotient; + return s; } + } - n = n32; - return s; + // Otherwise, work with the remainder + auto quotient = static_cast(n / 100000000); + auto remainder = static_cast(n - 100000000 * quotient); + + if (t == 0 || remainder * mod_inv1 > max_quotient1) { + return 0; } + remainder *= mod_inv1; - // If n is not divisible by 10^8, work with n itself. - const uint64_t mod_inv_5 = 0xcccccccccccccccd; - const uint64_t mod_inv_25 = mod_inv_5 * mod_inv_5; + if (t == 1 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 1) + quotient * 10000000ull; + return 1; + } + remainder *= mod_inv1; - int s = 0; - while (true) { - auto q = rotr(n * mod_inv_25, 2); - if (q > max_value() / 100) break; - n = q; - s += 2; + if (t == 2 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 2) + quotient * 1000000ull; + return 2; + } + remainder *= mod_inv1; + + if (t == 3 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 3) + quotient * 100000ull; + return 3; } - auto q = rotr(n * mod_inv_5, 1); - if (q <= max_value() / 10) { - n = q; - s |= 1; + remainder *= mod_inv1; + + if (t == 4 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 4) + quotient * 10000ull; + return 4; } + remainder *= mod_inv1; - return s; + if (t == 5 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 5) + quotient * 1000ull; + return 5; + } + remainder *= mod_inv1; + + if (t == 6 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 6) + quotient * 100ull; + return 6; + } + remainder *= mod_inv1; + + n = (remainder >> 7) + quotient * 10ull; + return 7; } // The main algorithm for shorter interval case template -FMT_INLINE decimal_fp shorter_interval_case(int exponent) noexcept { +FMT_INLINE decimal_fp shorter_interval_case(int exponent) FMT_NOEXCEPT { decimal_fp ret_value; // Compute k and beta const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); - const int beta = exponent + floor_log2_pow10(-minus_k); + const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k); // Compute xi and zi using cache_entry_type = typename cache_accessor::cache_entry_type; const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); auto xi = cache_accessor::compute_left_endpoint_for_shorter_interval_case( - cache, beta); + cache, beta_minus_1); auto zi = cache_accessor::compute_right_endpoint_for_shorter_interval_case( - cache, beta); + cache, beta_minus_1); // If the left endpoint is not an integer, increase it if (!is_left_endpoint_integer_shorter_interval(exponent)) ++xi; @@ -1880,8 +2069,8 @@ FMT_INLINE decimal_fp shorter_interval_case(int exponent) noexcept { // Otherwise, compute the round-up of y ret_value.significand = - cache_accessor::compute_round_up_for_shorter_interval_case(cache, - beta); + cache_accessor::compute_round_up_for_shorter_interval_case( + cache, beta_minus_1); ret_value.exponent = minus_k; // When tie occurs, choose one of them according to the rule @@ -1896,7 +2085,7 @@ FMT_INLINE decimal_fp shorter_interval_case(int exponent) noexcept { return ret_value; } -template decimal_fp to_decimal(T x) noexcept { +template decimal_fp to_decimal(T x) FMT_NOEXCEPT { // Step 1: integer promotion & Schubfach multiplier calculation. using carrier_uint = typename float_info::carrier_uint; @@ -1905,25 +2094,23 @@ template decimal_fp to_decimal(T x) noexcept { // Extract significand bits and exponent bits. const carrier_uint significand_mask = - (static_cast(1) << num_significand_bits()) - 1; + (static_cast(1) << float_info::significand_bits) - 1; carrier_uint significand = (br & significand_mask); - int exponent = - static_cast((br & exponent_mask()) >> num_significand_bits()); + int exponent = static_cast((br & exponent_mask()) >> + float_info::significand_bits); if (exponent != 0) { // Check if normal. - exponent -= exponent_bias() + num_significand_bits(); + exponent += float_info::exponent_bias - float_info::significand_bits; // Shorter interval case; proceed like Schubfach. - // In fact, when exponent == 1 and significand == 0, the interval is - // regular. However, it can be shown that the end-results are anyway same. if (significand == 0) return shorter_interval_case(exponent); - significand |= (static_cast(1) << num_significand_bits()); + significand |= + (static_cast(1) << float_info::significand_bits); } else { // Subnormal case; the interval is always regular. if (significand == 0) return {0, 0}; - exponent = - std::numeric_limits::min_exponent - num_significand_bits() - 1; + exponent = float_info::min_exponent - float_info::significand_bits; } const bool include_left_endpoint = (significand % 2 == 0); @@ -1932,121 +2119,110 @@ template decimal_fp to_decimal(T x) noexcept { // Compute k and beta. const int minus_k = floor_log10_pow2(exponent) - float_info::kappa; const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); - const int beta = exponent + floor_log2_pow10(-minus_k); + const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k); - // Compute zi and deltai. + // Compute zi and deltai // 10^kappa <= deltai < 10^(kappa + 1) - const uint32_t deltai = cache_accessor::compute_delta(cache, beta); + const uint32_t deltai = cache_accessor::compute_delta(cache, beta_minus_1); const carrier_uint two_fc = significand << 1; + const carrier_uint two_fr = two_fc | 1; + const carrier_uint zi = + cache_accessor::compute_mul(two_fr << beta_minus_1, cache); - // For the case of binary32, the result of integer check is not correct for - // 29711844 * 2^-82 - // = 6.1442653300000000008655037797566933477355632930994033813476... * 10^-18 - // and 29711844 * 2^-81 - // = 1.2288530660000000001731007559513386695471126586198806762695... * 10^-17, - // and they are the unique counterexamples. However, since 29711844 is even, - // this does not cause any problem for the endpoints calculations; it can only - // cause a problem when we need to perform integer check for the center. - // Fortunately, with these inputs, that branch is never executed, so we are - // fine. - const typename cache_accessor::compute_mul_result z_mul = - cache_accessor::compute_mul((two_fc | 1) << beta, cache); - - // Step 2: Try larger divisor; remove trailing zeros if necessary. + // Step 2: Try larger divisor; remove trailing zeros if necessary // Using an upper bound on zi, we might be able to optimize the division - // better than the compiler; we are computing zi / big_divisor here. + // better than the compiler; we are computing zi / big_divisor here decimal_fp ret_value; - ret_value.significand = divide_by_10_to_kappa_plus_1(z_mul.result); - uint32_t r = static_cast(z_mul.result - float_info::big_divisor * - ret_value.significand); + ret_value.significand = divide_by_10_to_kappa_plus_1(zi); + uint32_t r = static_cast(zi - float_info::big_divisor * + ret_value.significand); - if (r < deltai) { - // Exclude the right endpoint if necessary. - if (r == 0 && z_mul.is_integer && !include_right_endpoint) { + if (r > deltai) { + goto small_divisor_case_label; + } else if (r < deltai) { + // Exclude the right endpoint if necessary + if (r == 0 && !include_right_endpoint && + is_endpoint_integer(two_fr, exponent, minus_k)) { --ret_value.significand; r = float_info::big_divisor; goto small_divisor_case_label; } - } else if (r > deltai) { - goto small_divisor_case_label; } else { - // r == deltai; compare fractional parts. + // r == deltai; compare fractional parts + // Check conditions in the order different from the paper + // to take advantage of short-circuiting const carrier_uint two_fl = two_fc - 1; - - 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 ((!include_left_endpoint || + !is_endpoint_integer(two_fl, exponent, minus_k)) && + !cache_accessor::compute_mul_parity(two_fl, cache, beta_minus_1)) { + goto small_divisor_case_label; } } ret_value.exponent = minus_k + float_info::kappa + 1; - // We may need to remove trailing zeros. + // We may need to remove trailing zeros ret_value.exponent += remove_trailing_zeros(ret_value.significand); return ret_value; - // Step 3: Find the significand with the smaller divisor. + // Step 3: Find the significand with the smaller divisor small_divisor_case_label: ret_value.significand *= 10; ret_value.exponent = minus_k + float_info::kappa; - uint32_t dist = r - (deltai / 2) + (float_info::small_divisor / 2); - const bool approx_y_parity = - ((dist ^ (float_info::small_divisor / 2)) & 1) != 0; - - // Is dist divisible by 10^kappa? - const bool divisible_by_small_divisor = - check_divisibility_and_divide_by_pow10::kappa>(dist); - - // Add dist / 10^kappa to the significand. - ret_value.significand += dist; - - if (!divisible_by_small_divisor) return ret_value; - - // Check z^(f) >= epsilon^(f). - // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, - // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f). - // Since there are only 2 possibilities, we only need to care about the - // parity. Also, zi and r should have the same parity since the divisor - // is an even number. - const auto y_mul = cache_accessor::compute_mul_parity(two_fc, cache, beta); - - // If z^(f) >= epsilon^(f), we might have a tie when z^(f) == epsilon^(f), - // or equivalently, when y is an integer. - if (y_mul.parity != approx_y_parity) - --ret_value.significand; - else if (y_mul.is_integer && ret_value.significand % 2 != 0) - --ret_value.significand; + const uint32_t mask = (1u << float_info::kappa) - 1; + auto dist = r - (deltai / 2) + (float_info::small_divisor / 2); + + // Is dist divisible by 2^kappa? + if ((dist & mask) == 0) { + const bool approx_y_parity = + ((dist ^ (float_info::small_divisor / 2)) & 1) != 0; + dist >>= float_info::kappa; + + // Is dist divisible by 5^kappa? + if (check_divisibility_and_divide_by_pow5::kappa>(dist)) { + ret_value.significand += dist; + + // Check z^(f) >= epsilon^(f) + // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, + // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f) + // Since there are only 2 possibilities, we only need to care about the + // parity. Also, zi and r should have the same parity since the divisor + // is an even number + if (cache_accessor::compute_mul_parity(two_fc, cache, beta_minus_1) != + approx_y_parity) { + --ret_value.significand; + } else { + // If z^(f) >= epsilon^(f), we might have a tie + // when z^(f) == epsilon^(f), or equivalently, when y is an integer + if (is_center_integer(two_fc, exponent, minus_k)) { + ret_value.significand = ret_value.significand % 2 == 0 + ? ret_value.significand + : ret_value.significand - 1; + } + } + } + // Is dist not divisible by 5^kappa? + else { + ret_value.significand += dist; + } + } + // Is dist not divisible by 2^kappa? + else { + // Since we know dist is small, we might be able to optimize the division + // better than the compiler; we are computing dist / small_divisor here + ret_value.significand += + small_division_by_pow10::kappa>(dist); + } 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(fp value, unsigned flags, +FMT_CONSTEXPR20 inline void format_dragon(fp value, bool is_predecessor_closer, int num_digits, buffer& buf, int& exp10) { bigint numerator; // 2 * R in (FPP)^2. @@ -2058,14 +2234,14 @@ FMT_CONSTEXPR20 inline void format_dragon(fp value, unsigned flags, // 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; + uint64_t significand = value.f << shift; if (value.e >= 0) { - numerator.assign(value.f); - numerator <<= value.e + shift; + numerator.assign(significand); + numerator <<= value.e; lower.assign(1); lower <<= value.e; - if (is_predecessor_closer) { + if (shift != 1) { upper_store.assign(1); upper_store <<= value.e + 1; upper = &upper_store; @@ -2075,42 +2251,29 @@ FMT_CONSTEXPR20 inline void format_dragon(fp value, unsigned flags, } else if (exp10 < 0) { numerator.assign_pow10(-exp10); lower.assign(numerator); - if (is_predecessor_closer) { + if (shift != 1) { upper_store.assign(numerator); upper_store <<= 1; upper = &upper_store; } - numerator *= value.f; - numerator <<= shift; + numerator *= significand; denominator.assign(1); denominator <<= shift - value.e; } else { - numerator.assign(value.f); - numerator <<= shift; + numerator.assign(significand); denominator.assign_pow10(exp10); denominator <<= shift - value.e; lower.assign(1); - if (is_predecessor_closer) { + if (shift != 1) { upper_store.assign(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. + if (!upper) upper = &lower; + bool even = (value.f & 1) == 0; num_digits = 0; char* data = buf.data(); for (;;) { @@ -2173,17 +2336,6 @@ FMT_CONSTEXPR20 inline void format_dragon(fp value, unsigned flags, buf[num_digits - 1] = static_cast('0' + digit); } -#ifdef _MSC_VER -FMT_FUNC auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...) - -> int { - auto args = va_list(); - va_start(args, fmt); - int result = vsnprintf_s(buf, size, _TRUNCATE, fmt, args); - va_end(args); - return result; -} -#endif - template FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, float_specs specs, @@ -2191,7 +2343,6 @@ FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, // 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. @@ -2204,24 +2355,9 @@ FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, 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) - const auto e = basic_fp::carrier_uint>(converted_value) - .e; - // 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. - auto num_fraction_bits = - num_significand_bits() - (has_implicit_bit() ? 0 : 1); - exp = static_cast( - std::ceil((e + num_fraction_bits) * inv_log2_10 - 1e-10)); - dragon_flags = dragon::fixup; - } else if (!is_constant_evaluated() && precision < 0) { + if (specs.fallback) return snprintf_float(value, precision, specs, buf); + + if (!is_constant_evaluated() && precision < 0) { // Use Dragonbox for the shortest format. if (specs.binary32) { auto dec = dragonbox::to_decimal(static_cast(value)); @@ -2231,12 +2367,16 @@ FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, auto dec = dragonbox::to_decimal(static_cast(value)); write(buffer_appender(buf), dec.significand); return dec.exponent; - } else { + } + + int exp = 0; + bool use_dragon = true; + if (is_fast_float()) { // Use Grisu + Dragon4 for the given precision: // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. const int min_exp = -60; // alpha in Grisu. int cached_exp10 = 0; // K in Grisu. - fp normalized = normalize(fp(converted_value)); + fp normalized = normalize(fp(value)); const auto cached_pow = get_cached_power( min_exp - (normalized.e + fp::num_significand_bits), cached_exp10); normalized = normalized * cached_pow; @@ -2253,16 +2393,13 @@ FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, } if (use_dragon) { auto f = 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; + bool is_predecessor_closer = + specs.binary32 ? f.assign(static_cast(value)) : f.assign(value); // Limit precision to the maximum possible number of significant digits in // an IEEE754 double because we don't need to generate zeros. const int max_double_digits = 767; if (precision > max_double_digits) precision = max_double_digits; - format_dragon(f, dragon_flags, precision, buf, exp); + format_dragon(f, is_predecessor_closer, precision, buf, exp); } if (!fixed && !specs.showpoint) { // Remove trailing zeros. @@ -2275,6 +2412,110 @@ FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, } return exp; } + +template +int snprintf_float(T value, int precision, float_specs specs, + buffer& buf) { + // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. + FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); + static_assert(!std::is_same::value, ""); + + // Subtract 1 to account for the difference in precision since we use %e for + // both general and exponent format. + if (specs.format == float_format::general || + specs.format == float_format::exp) + precision = (precision >= 0 ? precision : 6) - 1; + + // Build the format string. + enum { max_format_size = 7 }; // The longest format is "%#.*Le". + char format[max_format_size]; + char* format_ptr = format; + *format_ptr++ = '%'; + if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#'; + if (precision >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + if (std::is_same()) *format_ptr++ = 'L'; + *format_ptr++ = specs.format != float_format::hex + ? (specs.format == float_format::fixed ? 'f' : 'e') + : (specs.upper ? 'A' : 'a'); + *format_ptr = '\0'; + + // Format using snprintf. + auto offset = buf.size(); + for (;;) { + auto begin = buf.data() + offset; + auto capacity = buf.capacity() - offset; +#ifdef FMT_FUZZ + if (precision > 100000) + throw std::runtime_error( + "fuzz mode - avoid large allocation inside snprintf"); +#endif + // Suppress the warning about a nonliteral format string. + // Cannot use auto because of a bug in MinGW (#1532). + int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; + int result = precision >= 0 + ? snprintf_ptr(begin, capacity, format, precision, value) + : snprintf_ptr(begin, capacity, format, value); + if (result < 0) { + // The buffer will grow exponentially. + buf.try_reserve(buf.capacity() + 1); + continue; + } + auto size = to_unsigned(result); + // Size equal to capacity means that the last character was truncated. + if (size >= capacity) { + buf.try_reserve(size + offset + 1); // Add 1 for the terminating '\0'. + continue; + } + auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; + if (specs.format == float_format::fixed) { + if (precision == 0) { + buf.try_resize(size); + return 0; + } + // Find and remove the decimal point. + auto end = begin + size, p = end; + do { + --p; + } while (is_digit(*p)); + int fraction_size = static_cast(end - p - 1); + std::memmove(p, p + 1, to_unsigned(fraction_size)); + buf.try_resize(size - 1); + return -fraction_size; + } + if (specs.format == float_format::hex) { + buf.try_resize(size + offset); + return 0; + } + // Find and parse the exponent. + auto end = begin + size, exp_pos = end; + do { + --exp_pos; + } while (*exp_pos != 'e'); + char sign = exp_pos[1]; + FMT_ASSERT(sign == '+' || sign == '-', ""); + int exp = 0; + auto p = exp_pos + 2; // Skip 'e' and sign. + do { + FMT_ASSERT(is_digit(*p), ""); + exp = exp * 10 + (*p++ - '0'); + } while (p != end); + if (sign == '-') exp = -exp; + int fraction_size = 0; + if (exp_pos != begin + 1) { + // Remove trailing zeros. + auto fraction_end = exp_pos - 1; + while (*fraction_end == '0') --fraction_end; + // Move the fractional part left to get rid of the decimal point. + fraction_size = static_cast(fraction_end - begin - 1); + std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size)); + } + buf.try_resize(to_unsigned(fraction_size) + offset + 1); + return exp - fraction_size; + } +} } // namespace detail template <> struct formatter { @@ -2319,7 +2560,7 @@ FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { } FMT_FUNC void format_system_error(detail::buffer& out, int error_code, - const char* message) noexcept { + const char* message) FMT_NOEXCEPT { FMT_TRY { auto ec = std::error_code(error_code, std::generic_category()); write(std::back_inserter(out), std::system_error(ec, message).what()); @@ -2330,7 +2571,7 @@ FMT_FUNC void format_system_error(detail::buffer& out, int error_code, } FMT_FUNC void report_system_error(int error_code, - const char* message) noexcept { + const char* message) FMT_NOEXCEPT { report_error(format_system_error, error_code, message); } @@ -2397,197 +2638,6 @@ FMT_FUNC void vprint(string_view format_str, format_args args) { vprint(stdout, format_str, args); } -namespace detail { - -struct singleton { - unsigned char upper; - unsigned char lower_count; -}; - -inline auto is_printable(uint16_t x, const singleton* singletons, - size_t singletons_size, - const unsigned char* singleton_lowers, - const unsigned char* normal, size_t normal_size) - -> bool { - auto upper = x >> 8; - auto lower_start = 0; - for (size_t i = 0; i < singletons_size; ++i) { - auto s = singletons[i]; - auto lower_end = lower_start + s.lower_count; - if (upper < s.upper) break; - if (upper == s.upper) { - for (auto j = lower_start; j < lower_end; ++j) { - if (singleton_lowers[j] == (x & 0xff)) return false; - } - } - lower_start = lower_end; - } - - auto xsigned = static_cast(x); - auto current = true; - for (size_t i = 0; i < normal_size; ++i) { - auto v = static_cast(normal[i]); - auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v; - xsigned -= len; - if (xsigned < 0) break; - current = !current; - } - return current; -} - -// This code is generated by support/printable.py. -FMT_FUNC auto is_printable(uint32_t cp) -> bool { - static constexpr singleton singletons0[] = { - {0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8}, - {0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13}, - {0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5}, - {0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22}, - {0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3}, - {0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8}, - {0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9}, - }; - static constexpr unsigned char singletons0_lower[] = { - 0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90, - 0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f, - 0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1, - 0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04, - 0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d, - 0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf, - 0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, - 0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d, - 0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d, - 0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d, - 0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, - 0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7, - 0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, - 0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7, - 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7, - 0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e, - 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16, - 0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e, - 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f, - 0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf, - 0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0, - 0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, - 0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91, - 0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, - 0xfe, 0xff, - }; - static constexpr singleton singletons1[] = { - {0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2}, - {0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5}, - {0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5}, - {0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2}, - {0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5}, - {0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2}, - {0xfa, 2}, {0xfb, 1}, - }; - static constexpr unsigned char singletons1_lower[] = { - 0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07, - 0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36, - 0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87, - 0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, - 0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b, - 0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9, - 0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66, - 0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27, - 0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc, - 0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, - 0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6, - 0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c, - 0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66, - 0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0, - 0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93, - }; - static constexpr unsigned char normal0[] = { - 0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04, - 0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0, - 0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01, - 0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03, - 0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03, - 0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a, - 0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15, - 0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f, - 0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80, - 0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07, - 0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06, - 0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04, - 0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac, - 0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c, - 0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11, - 0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c, - 0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b, - 0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6, - 0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03, - 0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80, - 0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06, - 0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c, - 0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17, - 0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80, - 0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80, - 0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d, - }; - static constexpr unsigned char normal1[] = { - 0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f, - 0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e, - 0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04, - 0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09, - 0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16, - 0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f, - 0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36, - 0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33, - 0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08, - 0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e, - 0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41, - 0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03, - 0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22, - 0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04, - 0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45, - 0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03, - 0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81, - 0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75, - 0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1, - 0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a, - 0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11, - 0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09, - 0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89, - 0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6, - 0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09, - 0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50, - 0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05, - 0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83, - 0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05, - 0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80, - 0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80, - 0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07, - 0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e, - 0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07, - 0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06, - }; - auto lower = static_cast(cp); - if (cp < 0x10000) { - return is_printable(lower, singletons0, - sizeof(singletons0) / sizeof(*singletons0), - singletons0_lower, normal0, sizeof(normal0)); - } - if (cp < 0x20000) { - return is_printable(lower, singletons1, - sizeof(singletons1) / sizeof(*singletons1), - singletons1_lower, normal1, sizeof(normal1)); - } - if (0x2a6de <= cp && cp < 0x2a700) return false; - if (0x2b735 <= cp && cp < 0x2b740) return false; - if (0x2b81e <= cp && cp < 0x2b820) return false; - if (0x2cea2 <= cp && cp < 0x2ceb0) return false; - if (0x2ebe1 <= cp && cp < 0x2f800) return false; - if (0x2fa1e <= cp && cp < 0x30000) return false; - if (0x3134b <= cp && cp < 0xe0100) return false; - if (0xe01f0 <= cp && cp < 0x110000) return false; - return cp < 0x110000; -} - -} // namespace detail - FMT_END_NAMESPACE #endif // FMT_FORMAT_INL_H_ diff --git a/include/fmt/format.h b/include/fmt/format.h index 57fc059c..ee69651c 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1,46 +1,45 @@ /* - Formatting library for C++ - - Copyright (c) 2012 - present, Victor Zverovich - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - --- Optional exception to the license --- - - As an exception, if, as a result of your compiling your source code, portions - of this Software are embedded into a machine-executable object form of such - source code, you may redistribute such embedded portions in such object form - without including the above copyright and permission notices. + Formatting library for C++ + + Copyright (c) 2012 - present, Victor Zverovich + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + --- Optional exception to the license --- + + As an exception, if, as a result of your compiling your source code, portions + of this Software are embedded into a machine-executable object form of such + source code, you may redistribute such embedded portions in such object form + without including the above copyright and permission notices. */ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ #include // std::signbit -#include // std::byte #include // uint32_t -#include // std::memcpy #include // std::numeric_limits #include // std::uninitialized_copy #include // std::runtime_error #include // std::system_error +#include // std::swap #ifdef __cpp_lib_bit_cast # include // std::bitcast @@ -159,12 +158,10 @@ FMT_END_NAMESPACE // __builtin_ctz is broken in Intel Compiler Classic on Windows: // https://github.com/fmtlib/fmt/issues/2510. #ifndef __ICL -# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \ - FMT_NVCOMPILER_VERSION +# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION # define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) # endif -# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \ - FMT_ICC_VERSION || FMT_NVCOMPILER_VERSION +# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || FMT_ICC_VERSION # define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) # endif #endif @@ -255,13 +252,6 @@ FMT_END_NAMESPACE FMT_BEGIN_NAMESPACE namespace detail { -FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { - ignore_unused(condition); -#ifdef FMT_FUZZ - if (condition) throw std::runtime_error("fuzzing limit reached"); -#endif -} - template class formatbuf : public Streambuf { private: using char_type = typename Streambuf::char_type; @@ -294,8 +284,9 @@ template class formatbuf : public Streambuf { }; // Implementation of std::bit_cast for pre-C++20. -template +template FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { + static_assert(sizeof(To) == sizeof(From), "size mismatch"); #ifdef __cpp_lib_bit_cast if (is_constant_evaluated()) return std::bit_cast(from); #endif @@ -319,63 +310,29 @@ inline auto is_big_endian() -> bool { #endif } -class uint128_fallback { - private: - uint64_t lo_, hi_; - constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {} - - public: - constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {} +// A fallback implementation of uintptr_t for systems that lack it. +struct fallback_uintptr { + unsigned char value[sizeof(void*)]; - template ::value)> - constexpr explicit operator T() const { - return static_cast(lo_); - } - - friend auto operator==(const uint128_fallback& lhs, - const uint128_fallback& rhs) -> bool { - return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_; - } - friend auto operator!=(const uint128_fallback& lhs, - const uint128_fallback& rhs) -> bool { - return !(lhs == rhs); - } - friend auto operator|(const uint128_fallback& lhs, - const uint128_fallback& rhs) -> uint128_fallback { - return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_}; - } - friend auto operator&(const uint128_fallback& lhs, - const uint128_fallback& rhs) -> uint128_fallback { - return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_}; - } - friend auto operator-(const uint128_fallback& lhs, uint64_t rhs) - -> uint128_fallback { - FMT_ASSERT(lhs.lo_ >= rhs, ""); - return {lhs.hi_, lhs.lo_ - rhs}; - } - FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback { - if (shift == 64) return {0, hi_}; - return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)}; - } - FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback { - if (shift == 64) return {lo_, 0}; - return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)}; - } - FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& { - return *this = *this >> shift; - } - FMT_CONSTEXPR void operator+=(uint64_t n) { - lo_ += n; - if (lo_ < n) ++hi_; + fallback_uintptr() = default; + explicit fallback_uintptr(const void* p) { + *this = bit_cast(p); + if (const_check(is_big_endian())) { + for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j) + std::swap(value[i], value[j]); + } } }; - -using uint128_t = conditional_t; - #ifdef UINTPTR_MAX using uintptr_t = ::uintptr_t; +inline auto to_uintptr(const void* p) -> uintptr_t { + return bit_cast(p); +} #else -using uintptr_t = uint128_t; +using uintptr_t = fallback_uintptr; +inline auto to_uintptr(const void* p) -> fallback_uintptr { + return fallback_uintptr(p); +} #endif // Returns the largest possible value for type T. Same as @@ -387,31 +344,16 @@ template constexpr auto num_bits() -> int { return std::numeric_limits::digits; } // std::numeric_limits::digits may return 0 for 128-bit ints. -template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return 128; } template <> constexpr auto num_bits() -> int { return 128; } - -// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t -// and 128-bit pointers to uint128_fallback. -template sizeof(From))> -inline auto bit_cast(const From& from) -> To { - constexpr auto size = static_cast(sizeof(From) / sizeof(unsigned)); - struct data_t { - unsigned value[static_cast(size)]; - } data = bit_cast(from); - auto result = To(); - if (const_check(is_big_endian())) { - for (int i = 0; i < size; ++i) - result = (result << num_bits()) | data.value[i]; - } else { - for (int i = size - 1; i >= 0; --i) - result = (result << num_bits()) | data.value[i]; - } - return result; +template <> constexpr auto num_bits() -> int { + return static_cast(sizeof(void*) * + std::numeric_limits::digits); } FMT_INLINE void assume(bool condition) { (void)condition; -#if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION +#if FMT_HAS_BUILTIN(__builtin_assume) __builtin_assume(condition); #endif } @@ -674,24 +616,6 @@ inline auto code_point_index(basic_string_view s, size_t n) return s.size(); } -#ifndef FMT_USE_FLOAT128 -# ifdef __SIZEOF_FLOAT128__ -# define FMT_USE_FLOAT128 1 -# else -# define FMT_USE_FLOAT128 0 -# endif -#endif -#if FMT_USE_FLOAT128 -using float128 = __float128; -#else -using float128 = void; -#endif -template using is_float128 = std::is_same; - -template -using is_floating_point = - bool_constant::value || is_float128::value>; - template ::value> struct is_fast_float : bool_constant::is_iec559 && sizeof(T) <= sizeof(double)> {}; @@ -774,7 +698,9 @@ class basic_memory_buffer final : public detail::buffer { const Allocator& alloc = Allocator()) : alloc_(alloc) { this->set(store_, SIZE); - if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T()); + if (detail::is_constant_evaluated()) { + detail::fill_n(store_, SIZE, T{}); + } } FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); } @@ -786,14 +712,18 @@ class basic_memory_buffer final : public detail::buffer { size_t size = other.size(), capacity = other.capacity(); if (data == other.store_) { this->set(store_, capacity); - detail::copy_str(other.store_, other.store_ + size, - detail::make_checked(store_, capacity)); + if (detail::is_constant_evaluated()) { + detail::copy_str(other.store_, other.store_ + size, + detail::make_checked(store_, capacity)); + } else { + std::uninitialized_copy(other.store_, other.store_ + size, + detail::make_checked(store_, capacity)); + } } else { this->set(data, capacity); // Set pointer to the inline array so that delete is not called // when deallocating. other.set(other.store_, 0); - other.clear(); } this->resize(size); } @@ -805,7 +735,8 @@ class basic_memory_buffer final : public detail::buffer { of the other object to it. \endrst */ - FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept { + FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) + FMT_NOEXCEPT { move(other); } @@ -814,7 +745,8 @@ class basic_memory_buffer final : public detail::buffer { Moves the content of the other ``basic_memory_buffer`` object to this one. \endrst */ - auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& { + auto operator=(basic_memory_buffer&& other) FMT_NOEXCEPT + -> basic_memory_buffer& { FMT_ASSERT(this != &other, ""); deallocate(); move(other); @@ -844,7 +776,9 @@ class basic_memory_buffer final : public detail::buffer { template FMT_CONSTEXPR20 void basic_memory_buffer::grow( size_t size) { - detail::abort_fuzzing_if(size > 5000); +#ifdef FMT_FUZZ + if (size > 5000) throw std::runtime_error("fuzz mode - won't grow that much"); +#endif const size_t max_size = std::allocator_traits::max_size(alloc_); size_t old_capacity = this->capacity(); size_t new_capacity = old_capacity + old_capacity / 2; @@ -886,7 +820,7 @@ class FMT_API format_error : public std::runtime_error { format_error& operator=(const format_error&) = default; format_error(format_error&&) = default; format_error& operator=(format_error&&) = default; - ~format_error() noexcept override FMT_MSC_DEFAULT; + ~format_error() FMT_NOEXCEPT override FMT_MSC_DEFAULT; }; /** @@ -898,8 +832,8 @@ class FMT_API format_error : public std::runtime_error { \endrst */ template > -FMT_DEPRECATED FMT_INLINE auto make_args_checked( - const S& fmt, const remove_reference_t&... args) +FMT_INLINE auto make_args_checked(const S& fmt, + const remove_reference_t&... args) -> format_arg_store, remove_reference_t...> { static_assert( detail::count<( @@ -910,6 +844,7 @@ FMT_DEPRECATED FMT_INLINE auto make_args_checked( return {args...}; } +// compile-time support namespace detail_exported { #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS template struct fixed_string { @@ -917,7 +852,7 @@ template struct fixed_string { detail::copy_str(static_cast(str), str + N, data); } - Char data[N] = {}; + Char data[N]{}; }; #endif @@ -939,30 +874,30 @@ constexpr auto compile_string_to_view(detail::std_string_view s) FMT_BEGIN_DETAIL_NAMESPACE template struct is_integral : std::is_integral {}; -template <> struct is_integral : std::true_type {}; +template <> struct is_integral : std::true_type {}; template <> struct is_integral : std::true_type {}; template using is_signed = std::integral_constant::is_signed || - std::is_same::value>; + std::is_same::value>; // Returns true if value is negative, false otherwise. // Same as `value < 0` but doesn't produce warnings if T is an unsigned type. template ::value)> -constexpr auto is_negative(T value) -> bool { +FMT_CONSTEXPR auto is_negative(T value) -> bool { return value < 0; } template ::value)> -constexpr auto is_negative(T) -> bool { +FMT_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 ::value)> +FMT_CONSTEXPR auto is_supported_floating_point(T) -> uint16_t { + return (std::is_same::value && FMT_USE_FLOAT) || + (std::is_same::value && FMT_USE_DOUBLE) || + (std::is_same::value && FMT_USE_LONG_DOUBLE); } // Smallest of uint32_t, uint64_t, uint128_t that is large enough to @@ -971,10 +906,9 @@ template using uint32_or_64_or_128_t = conditional_t() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS, uint32_t, - conditional_t() <= 64, uint64_t, uint128_opt>>; + conditional_t() <= 64, uint64_t, uint128_t>>; template -using uint64_or_128_t = - conditional_t() <= 64, uint64_t, uint128_opt>; +using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; #define FMT_POWERS_OF_10(factor) \ factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ @@ -1014,7 +948,7 @@ template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { } } #if FMT_USE_INT128 -FMT_CONSTEXPR inline auto count_digits(uint128_opt n) -> int { +FMT_CONSTEXPR inline auto count_digits(uint128_t n) -> int { return count_digits_fallback(n); } #endif @@ -1055,7 +989,7 @@ FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { template FMT_CONSTEXPR auto count_digits(UInt n) -> int { #ifdef FMT_BUILTIN_CLZ - if (!is_constant_evaluated() && num_bits() == 32) + if (num_bits() == 32) return (FMT_BUILTIN_CLZ(static_cast(n) | 1) ^ 31) / BITS + 1; #endif // Lambda avoids unreachable code warnings from NVHPC. @@ -1068,6 +1002,8 @@ FMT_CONSTEXPR auto count_digits(UInt n) -> int { }(n); } +template <> auto count_digits<4>(detail::fallback_uintptr n) -> int; + #ifdef FMT_BUILTIN_CLZ // It is a separate function rather than a part of count_digits to workaround // the lack of static constexpr in constexpr functions. @@ -1103,11 +1039,15 @@ FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { return count_digits_fallback(n); } -template constexpr auto digits10() noexcept -> int { +template constexpr auto digits10() FMT_NOEXCEPT -> int { return std::numeric_limits::digits10; } -template <> constexpr auto digits10() noexcept -> int { return 38; } -template <> constexpr auto digits10() noexcept -> int { return 38; } +template <> constexpr auto digits10() FMT_NOEXCEPT -> int { + return 38; +} +template <> constexpr auto digits10() FMT_NOEXCEPT -> int { + return 38; +} template struct thousands_sep_result { std::string grouping; @@ -1202,13 +1142,35 @@ FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, Char* end = buffer; do { const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; - unsigned digit = static_cast(value & ((1 << BASE_BITS) - 1)); + unsigned digit = (value & ((1 << BASE_BITS) - 1)); *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) : digits[digit]); } while ((value >>= BASE_BITS) != 0); return end; } +template +auto format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, + bool = false) -> Char* { + auto char_digits = std::numeric_limits::digits / 4; + int start = (num_digits + char_digits - 1) / char_digits - 1; + if (int start_digits = num_digits % char_digits) { + unsigned value = n.value[start--]; + buffer = format_uint(buffer, value, start_digits); + } + for (; start >= 0; --start) { + unsigned value = n.value[start]; + buffer += char_digits; + auto p = buffer; + for (int i = 0; i < char_digits; ++i) { + unsigned digit = (value & ((1 << BASE_BITS) - 1)); + *--p = static_cast("0123456789abcdef"[digit]); + value >>= BASE_BITS; + } + } + return buffer; +} + template inline auto format_uint(It out, UInt value, int num_digits, bool upper = false) -> It { @@ -1238,43 +1200,58 @@ class utf8_to_utf16 { namespace dragonbox { // Type-specific information that Dragonbox uses. -template struct float_info; +template struct float_info; template <> struct float_info { using carrier_uint = uint32_t; + static const int significand_bits = 23; static const int exponent_bits = 8; + static const int min_exponent = -126; + static const int max_exponent = 127; + static const int exponent_bias = -127; + static const int decimal_digits = 9; static const int kappa = 1; static const int big_divisor = 100; static const int small_divisor = 10; static const int min_k = -31; static const int max_k = 46; + static const int cache_bits = 64; static const int divisibility_check_by_5_threshold = 39; static const int case_fc_pm_half_lower_threshold = -1; + static const int case_fc_pm_half_upper_threshold = 6; + static const int case_fc_lower_threshold = -2; + static const int case_fc_upper_threshold = 6; + static const int case_shorter_interval_left_endpoint_lower_threshold = 2; + static const int case_shorter_interval_left_endpoint_upper_threshold = 3; static const int shorter_interval_tie_lower_threshold = -35; static const int shorter_interval_tie_upper_threshold = -35; + static const int max_trailing_zeros = 7; }; template <> struct float_info { using carrier_uint = uint64_t; + static const int significand_bits = 52; static const int exponent_bits = 11; + static const int min_exponent = -1022; + static const int max_exponent = 1023; + static const int exponent_bias = -1023; + static const int decimal_digits = 17; static const int kappa = 2; static const int big_divisor = 1000; static const int small_divisor = 100; static const int min_k = -292; static const int max_k = 326; + static const int cache_bits = 128; static const int divisibility_check_by_5_threshold = 86; static const int case_fc_pm_half_lower_threshold = -2; + static const int case_fc_pm_half_upper_threshold = 9; + static const int case_fc_lower_threshold = -4; + static const int case_fc_upper_threshold = 9; + static const int case_shorter_interval_left_endpoint_lower_threshold = 2; + static const int case_shorter_interval_left_endpoint_upper_threshold = 3; static const int shorter_interval_tie_lower_threshold = -77; static const int shorter_interval_tie_upper_threshold = -77; -}; - -// An 80- or 128-bit floating point number. -template -struct float_info::digits == 64 || - std::numeric_limits::digits == 113 || - is_float128::value>> { - using carrier_uint = detail::uint128_t; - static const int exponent_bits = 15; + static const int max_trailing_zeros = 16; }; template struct decimal_fp { @@ -1283,35 +1260,16 @@ template struct decimal_fp { int exponent; }; -template FMT_API auto to_decimal(T x) noexcept -> decimal_fp; +template +FMT_API auto to_decimal(T x) FMT_NOEXCEPT -> decimal_fp; } // namespace dragonbox -// Returns true iff Float has the implicit bit which is not stored. -template constexpr bool has_implicit_bit() { - // An 80-bit FP number has a 64-bit significand an no implicit bit. - return std::numeric_limits::digits != 64; -} - -// Returns the number of significand bits stored in Float. The implicit bit is -// not counted since it is not stored. -template constexpr int num_significand_bits() { - // std::numeric_limits may not support __float128. - return is_float128() ? 112 - : (std::numeric_limits::digits - - (has_implicit_bit() ? 1 : 0)); -} - -template +template constexpr auto exponent_mask() -> - typename dragonbox::float_info::carrier_uint { - using uint = typename dragonbox::float_info::carrier_uint; - return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) - << num_significand_bits(); -} -template constexpr auto exponent_bias() -> int { - // std::numeric_limits may not support __float128. - return is_float128() ? 16383 - : std::numeric_limits::max_exponent - 1; + typename dragonbox::float_info::carrier_uint { + using uint = typename dragonbox::float_info::carrier_uint; + return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) + << dragonbox::float_info::significand_bits; } // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. @@ -1341,122 +1299,16 @@ FMT_HEADER_ONLY_CONSTEXPR20 auto format_float(T value, int precision, float_specs specs, buffer& buf) -> int; -#ifndef _MSC_VER -# define FMT_SNPRINTF snprintf -#else -FMT_API auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...) -> int; -# define FMT_SNPRINTF fmt_snprintf -#endif // _MSC_VER - // Formats a floating-point number with snprintf. 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"); - 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 (precision >= 0) { - *format_ptr++ = '.'; - *format_ptr++ = '*'; - } - if (std::is_same()) *format_ptr++ = 'L'; - *format_ptr++ = specs.format != float_format::hex - ? (specs.format == float_format::fixed ? 'f' : 'e') - : (specs.upper ? 'A' : 'a'); - *format_ptr = '\0'; - - // Format using snprintf. - auto offset = buf.size(); - for (;;) { - auto begin = buf.data() + offset; - auto capacity = buf.capacity() - offset; - abort_fuzzing_if(precision > 100000); - // Suppress the warning about a nonliteral format string. - // Cannot use auto because of a bug in MinGW (#1532). - int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; - int result = precision >= 0 - ? snprintf_ptr(begin, capacity, format, precision, value) - : snprintf_ptr(begin, capacity, format, value); - if (result < 0) { - // The buffer will grow exponentially. - buf.try_reserve(buf.capacity() + 1); - continue; - } - auto size = to_unsigned(result); - // Size equal to capacity means that the last character was truncated. - if (size >= capacity) { - buf.try_reserve(size + offset + 1); // Add 1 for the terminating '\0'. - continue; - } - auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; - if (specs.format == float_format::fixed) { - if (precision == 0) { - buf.try_resize(size); - return 0; - } - // Find and remove the decimal point. - auto end = begin + size, p = end; - do { - --p; - } while (is_digit(*p)); - int fraction_size = static_cast(end - p - 1); - std::memmove(p, p + 1, to_unsigned(fraction_size)); - buf.try_resize(size - 1); - return -fraction_size; - } - if (specs.format == float_format::hex) { - buf.try_resize(size + offset); - return 0; - } - // Find and parse the exponent. - auto end = begin + size, exp_pos = end; - do { - --exp_pos; - } while (*exp_pos != 'e'); - char sign = exp_pos[1]; - FMT_ASSERT(sign == '+' || sign == '-', ""); - int exp = 0; - auto p = exp_pos + 2; // Skip 'e' and sign. - do { - FMT_ASSERT(is_digit(*p), ""); - exp = exp * 10 + (*p++ - '0'); - } while (p != end); - if (sign == '-') exp = -exp; - int fraction_size = 0; - if (exp_pos != begin + 1) { - // Remove trailing zeros. - auto fraction_end = exp_pos - 1; - while (*fraction_end == '0') --fraction_end; - // Move the fractional part left to get rid of the decimal point. - fraction_size = static_cast(fraction_end - begin - 1); - std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size)); - } - buf.try_resize(to_unsigned(fraction_size) + offset + 1); - return exp - fraction_size; - } -} - -template -using convert_float_result = - conditional_t::value || sizeof(T) == sizeof(double), - double, T>; + buffer& buf) -> int; -template -constexpr auto convert_float(T value) -> convert_float_result { - return static_cast>(value); +template constexpr auto promote_float(T value) -> T { + return value; +} +constexpr auto promote_float(float value) -> double { + return static_cast(value); } template @@ -1525,173 +1377,11 @@ auto write_ptr(OutputIt out, UIntPtr value, : base_iterator(out, write(reserve(out, size))); } -// Returns true iff the code point cp is printable. -FMT_API auto is_printable(uint32_t cp) -> bool; - -inline auto needs_escape(uint32_t cp) -> bool { - return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' || - !is_printable(cp); -} - -template struct find_escape_result { - const Char* begin; - const Char* end; - uint32_t cp; -}; - -template -using make_unsigned_char = - typename conditional_t::value, - std::make_unsigned, - type_identity>::type; - -template -auto find_escape(const Char* begin, const Char* end) - -> find_escape_result { - for (; begin != end; ++begin) { - uint32_t cp = static_cast>(*begin); - if (sizeof(Char) == 1 && cp >= 0x80) continue; - if (needs_escape(cp)) return {begin, begin + 1, cp}; - } - return {begin, nullptr, 0}; -} - -inline auto find_escape(const char* begin, const char* end) - -> find_escape_result { - if (!is_utf8()) return find_escape(begin, end); - auto result = find_escape_result{end, nullptr, 0}; - for_each_codepoint(string_view(begin, to_unsigned(end - begin)), - [&](uint32_t cp, string_view sv) { - if (needs_escape(cp)) { - result = {sv.begin(), sv.end(), cp}; - return false; - } - return true; - }); - return result; -} - -#define FMT_STRING_IMPL(s, base, explicit) \ - [] { \ - /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ - /* Use a macro-like name to avoid shadowing warnings. */ \ - struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \ - using char_type = fmt::remove_cvref_t; \ - FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ - operator fmt::basic_string_view() const { \ - return fmt::detail_exported::compile_string_to_view(s); \ - } \ - }; \ - return FMT_COMPILE_STRING(); \ - }() - -/** - \rst - Constructs a compile-time format string from a string literal *s*. - - **Example**:: - - // A compile-time error because 'd' is an invalid specifier for strings. - std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); - \endrst - */ -#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::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_escaped_cp(OutputIt out, const find_escape_result& escape) - -> OutputIt { - auto c = static_cast(escape.cp); - switch (escape.cp) { - case '\n': - *out++ = '\\'; - c = 'n'; - break; - case '\r': - *out++ = '\\'; - c = 'r'; - break; - case '\t': - *out++ = '\\'; - c = 't'; - break; - case '"': - FMT_FALLTHROUGH; - case '\'': - FMT_FALLTHROUGH; - case '\\': - *out++ = '\\'; - break; - default: - if (is_utf8()) { - if (escape.cp < 0x100) { - return format_to(out, FMT_STRING("\\x{:02x}"), escape.cp); - } - if (escape.cp < 0x10000) { - return format_to(out, FMT_STRING("\\u{:04x}"), escape.cp); - } - if (escape.cp < 0x110000) { - return format_to(out, FMT_STRING("\\U{:08x}"), escape.cp); - } - } - 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)); - } - return out; - } - *out++ = c; - return out; -} - -template -auto write_escaped_string(OutputIt out, basic_string_view str) - -> OutputIt { - *out++ = '"'; - auto begin = str.begin(), end = str.end(); - do { - auto escape = find_escape(begin, end); - out = copy_str(begin, escape.begin, out); - begin = escape.end; - if (!begin) break; - out = write_escaped_cp(out, escape); - } while (begin != end); - *out++ = '"'; - 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 = write_escaped_cp( - out, find_escape_result{&v, &v + 1, static_cast(v)}); - } else { - *out++ = v; - } - *out++ = '\''; - return out; -} - template FMT_CONSTEXPR auto write_char(OutputIt out, Char value, const basic_format_specs& specs) -> OutputIt { - bool is_debug = specs.type == presentation_type::debug; return write_padded(out, specs, 1, [=](reserve_iterator it) { - if (is_debug) return write_escaped_char(it, value); *it++ = value; return it; }); @@ -1957,45 +1647,6 @@ FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); } -// An output iterator that counts the number of objects written to it and -// discards them. -class counting_iterator { - private: - size_t count_; - - public: - using iterator_category = std::output_iterator_tag; - using difference_type = std::ptrdiff_t; - using pointer = void; - using reference = void; - using _Unchecked_type = counting_iterator; // Mark iterator as checked. - - struct value_type { - template void operator=(const T&) {} - }; - - counting_iterator() : count_(0) {} - - size_t count() const { return count_; } - - counting_iterator& operator++() { - ++count_; - return *this; - } - counting_iterator operator++(int) { - auto it = *this; - ++*this; - return it; - } - - friend counting_iterator operator+(counting_iterator it, difference_type n) { - it.count_ += static_cast(n); - return it; - } - - value_type operator*() const { return {}; } -}; - template FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, const basic_format_specs& specs) -> OutputIt { @@ -2003,17 +1654,10 @@ FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, auto size = s.size(); if (specs.precision >= 0 && to_unsigned(specs.precision) < size) size = code_point_index(s, to_unsigned(specs.precision)); - bool is_debug = specs.type == presentation_type::debug; - size_t width = 0; - if (specs.width != 0) { - if (is_debug) - width = write_escaped_string(counting_iterator{}, s).count(); - else - width = compute_width(basic_string_view(data, size)); - } + auto width = + specs.width != 0 ? compute_width(basic_string_view(data, size)) : 0; return write_padded(out, specs, size, width, [=](reserve_iterator it) { - if (is_debug) return write_escaped_string(it, s); return copy_str(data, data + size, it); }); } @@ -2031,15 +1675,15 @@ FMT_CONSTEXPR auto write(OutputIt out, const Char* s, -> OutputIt { return check_cstring_type_spec(specs.type) ? write(out, basic_string_view(s), specs, {}) - : write_ptr(out, bit_cast(s), &specs); + : write_ptr(out, to_uintptr(s), &specs); } template -FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, +FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isinf, basic_format_specs specs, const float_specs& fspecs) -> OutputIt { auto str = - isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf"); + isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan"); constexpr size_t str_size = 3; auto sign = fspecs.sign; auto size = str_size + (sign ? 1 : 0); @@ -2103,7 +1747,7 @@ inline auto write_significand(Char* out, UInt significand, int significand_size, int floating_size = significand_size - integral_size; for (int i = floating_size / 2; i > 0; --i) { out -= 2; - copy2(out, digits2(static_cast(significand % 100))); + copy2(out, digits2(significand % 100)); significand /= 100; } if (floating_size % 2 != 0) { @@ -2216,11 +1860,13 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp, // 1234e5 -> 123400000[.0+] size += to_unsigned(fp.exponent); int num_zeros = fspecs.precision - exp; - abort_fuzzing_if(num_zeros > 5000); +#ifdef FMT_FUZZ + if (num_zeros > 5000) + throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); +#endif if (fspecs.showpoint) { - ++size; if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1; - if (num_zeros > 0) size += to_unsigned(num_zeros); + if (num_zeros > 0) size += to_unsigned(num_zeros) + 1; } auto grouping = Grouping(loc, fspecs.locale); size += to_unsigned(grouping.count_separators(significand_size)); @@ -2291,36 +1937,50 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& fp, } } -template ::value && - !is_float128::value)> -FMT_CONSTEXPR20 bool isfinite(T value) { - if (is_constant_evaluated()) return value - value == 0; - return std::isfinite(value); -} -template ::value)> -constexpr bool isfinite(T value) { - return value - value == 0; // std::isfinite doesn't support __float128. +template ::value)> +FMT_CONSTEXPR20 bool isinf(T value) { + if (is_constant_evaluated()) { +#if defined(__cpp_if_constexpr) + if constexpr (std::numeric_limits::is_iec559) { + auto bits = detail::bit_cast(static_cast(value)); + constexpr auto significand_bits = + dragonbox::float_info::significand_bits; + return (bits & exponent_mask()) && + !(bits & ((uint64_t(1) << significand_bits) - 1)); + } +#endif + } + return std::isinf(value); } -template constexpr bool isnan(T value) { - return value != value; // std::isnan doesn't support __float128. +template ::value)> +FMT_CONSTEXPR20 bool isfinite(T value) { + if (is_constant_evaluated()) { +#if defined(__cpp_if_constexpr) + if constexpr (std::numeric_limits::is_iec559) { + auto bits = detail::bit_cast(static_cast(value)); + return (bits & exponent_mask()) != exponent_mask(); + } +#endif + } + return std::isfinite(value); } -template ::value)> +template ::value)> FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { if (is_constant_evaluated()) { #ifdef __cpp_if_constexpr if constexpr (std::numeric_limits::is_iec559) { auto bits = detail::bit_cast(static_cast(value)); - return (bits >> (num_bits() - 1)) != 0; + return (bits & (uint64_t(1) << (num_bits() - 1))) != 0; } #endif } - return std::signbit(static_cast(value)); + return std::signbit(value); } template ::value)> + FMT_ENABLE_IF(std::is_floating_point::value)> FMT_CONSTEXPR20 auto write(OutputIt out, T value, basic_format_specs specs, locale_ref loc = {}) -> OutputIt { @@ -2335,7 +1995,7 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value, } if (!detail::isfinite(value)) - return write_nonfinite(out, detail::isnan(value), specs, fspecs); + return write_nonfinite(out, detail::isinf(value), specs, fspecs); if (specs.align == align::numeric && fspecs.sign) { auto it = reserve(out, 1); @@ -2348,7 +2008,7 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value, memory_buffer buffer; if (fspecs.format == float_format::hex) { if (fspecs.sign) buffer.push_back(detail::sign(fspecs.sign)); - snprintf_float(convert_float(value), specs.precision, fspecs, buffer); + snprintf_float(promote_float(value), specs.precision, fspecs, buffer); return write_bytes(out, {buffer.data(), buffer.size()}, specs); } @@ -2360,11 +2020,10 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value, throw_format_error("number is too big"); else ++precision; - } else if (fspecs.format != float_format::fixed && precision == 0) { - precision = 1; } if (const_check(std::is_same())) fspecs.binary32 = true; - int exp = format_float(convert_float(value), precision, fspecs, buffer); + if (!is_fast_float()) fspecs.fallback = true; + int exp = format_float(promote_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); @@ -2373,10 +2032,16 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value, template ::value)> FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { - if (is_constant_evaluated()) + if (is_constant_evaluated()) { return write(out, value, basic_format_specs()); + } + if (const_check(!is_supported_floating_point(value))) return out; + using floaty = conditional_t::value, double, T>; + using uint = typename dragonbox::float_info::carrier_uint; + auto bits = bit_cast(value); + auto fspecs = float_specs(); if (detail::signbit(value)) { fspecs.sign = sign::minus; @@ -2384,18 +2049,16 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { } constexpr auto specs = basic_format_specs(); - using floaty = conditional_t::value, double, T>; - using uint = typename dragonbox::float_info::carrier_uint; uint mask = exponent_mask(); - if ((bit_cast(value) & mask) == mask) - return write_nonfinite(out, std::isnan(value), specs, fspecs); + if ((bits & mask) == mask) + return write_nonfinite(out, std::isinf(value), specs, fspecs); auto dec = dragonbox::to_decimal(static_cast(value)); return write_float(out, dec, specs, fspecs, {}); } template ::value && + FMT_ENABLE_IF(std::is_floating_point::value && !is_fast_float::value)> inline auto write(OutputIt out, T value) -> OutputIt { return write(out, value, basic_format_specs()); @@ -2453,7 +2116,8 @@ template < type::custom_type, FMT_ENABLE_IF(check)> FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { - return write(out, static_cast>(value)); + return write( + out, static_cast::type>(value)); } template & specs = {}, locale_ref = {}) -> OutputIt { check_pointer_type_spec(specs.type, error_handler()); - return write_ptr(out, bit_cast(value), &specs); + return write_ptr(out, to_uintptr(value), &specs); } // A write overload that handles implicit conversions. @@ -2692,13 +2356,39 @@ FMT_CONSTEXPR void handle_dynamic_spec(int& value, } } +#define FMT_STRING_IMPL(s, base, explicit) \ + [] { \ + /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ + /* Use a macro-like name to avoid shadowing warnings. */ \ + struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \ + using char_type = fmt::remove_cvref_t; \ + FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ + operator fmt::basic_string_view() const { \ + return fmt::detail_exported::compile_string_to_view(s); \ + } \ + }; \ + return FMT_COMPILE_STRING(); \ + }() + +/** + \rst + Constructs a compile-time format string from a string literal *s*. + + **Example**:: + + // A compile-time error because 'd' is an invalid specifier for strings. + std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); + \endrst + */ +#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string, ) + #if FMT_USE_USER_DEFINED_LITERALS template struct udl_formatter { basic_string_view str; template auto operator()(T&&... args) const -> std::basic_string { - return vformat(str, fmt::make_format_args>(args...)); + return vformat(str, fmt::make_args_checked(str, args...)); } }; @@ -2751,10 +2441,10 @@ auto vformat(const Locale& loc, basic_string_view format_str, using format_func = void (*)(detail::buffer&, int, const char*); FMT_API void format_error_code(buffer& out, int error_code, - string_view message) noexcept; + string_view message) FMT_NOEXCEPT; FMT_API void report_error(format_func func, int error_code, - const char* message) noexcept; + const char* message) FMT_NOEXCEPT; FMT_END_DETAIL_NAMESPACE FMT_API auto vsystem_error(int error_code, string_view format_str, @@ -2800,11 +2490,12 @@ auto system_error(int error_code, format_string fmt, T&&... args) \endrst */ FMT_API void format_system_error(detail::buffer& out, int error_code, - const char* message) noexcept; + const char* message) FMT_NOEXCEPT; // Reports a system error without throwing an exception. // Can be used to report errors from destructors. -FMT_API void report_system_error(int error_code, const char* message) noexcept; +FMT_API void report_system_error(int error_code, + const char* message) FMT_NOEXCEPT; /** Fast integer formatter. */ class format_int { @@ -2886,6 +2577,28 @@ formatter(ctx.out(), val, specs_, ctx.locale()); } +#define FMT_FORMAT_AS(Type, Base) \ + template \ + struct formatter : formatter { \ + template \ + auto format(Type const& val, FormatContext& ctx) const \ + -> decltype(ctx.out()) { \ + return formatter::format(static_cast(val), ctx); \ + } \ + } + +FMT_FORMAT_AS(signed char, int); +FMT_FORMAT_AS(unsigned char, unsigned); +FMT_FORMAT_AS(short, int); +FMT_FORMAT_AS(unsigned short, unsigned); +FMT_FORMAT_AS(long, long long); +FMT_FORMAT_AS(unsigned long, unsigned long long); +FMT_FORMAT_AS(Char*, const Char*); +FMT_FORMAT_AS(std::basic_string, basic_string_view); +FMT_FORMAT_AS(std::nullptr_t, const void*); +FMT_FORMAT_AS(detail::byte, unsigned char); +FMT_FORMAT_AS(detail::std_string_view, basic_string_view); + template struct formatter : formatter { template @@ -2975,33 +2688,6 @@ template auto ptr(const std::shared_ptr& p) -> const void* { return p.get(); } -/** - \rst - Converts ``e`` to the underlying type. - - **Example**:: - - enum class color { red, green, blue }; - auto s = fmt::format("{}", fmt::underlying(color::red)); - \endrst - */ -template -constexpr auto underlying(Enum e) noexcept -> underlying_t { - return static_cast>(e); -} - -namespace enums { -template ::value)> -constexpr auto format_as(Enum e) noexcept -> underlying_t { - return static_cast>(e); -} -} // namespace enums - -#ifdef __cpp_lib_byte -inline auto format_as(std::byte b) -> unsigned char { return underlying(b); } -FMT_FORMAT_AS(std::byte, unsigned char); -#endif - class bytes { private: string_view data_; @@ -3132,8 +2818,8 @@ struct formatter, Char> { } template - auto format(const join_view& value, - FormatContext& ctx) const -> decltype(ctx.out()) { + auto format(const join_view& value, FormatContext& ctx) + -> decltype(ctx.out()) { auto it = value.begin; auto out = ctx.out(); if (it != value.end) { @@ -3250,10 +2936,9 @@ void vformat_to( basic_format_parse_context parse_context; buffer_context context; - format_handler(buffer_appender p_out, basic_string_view str, - basic_format_args> p_args, - locale_ref p_loc) - : parse_context(str), context(p_out, p_args, p_loc) {} + format_handler(buffer_appender out, basic_string_view str, + basic_format_args> args, locale_ref loc) + : parse_context(str), context(out, args, loc) {} void on_text(const Char* begin, const Char* end) { auto text = basic_string_view(begin, to_unsigned(end - begin)); @@ -3316,6 +3001,14 @@ extern template auto format_float(double value, int precision, extern template auto format_float(long double value, int precision, float_specs specs, buffer& buf) -> int; +void snprintf_float(float, int, float_specs, buffer&) = delete; +extern template auto snprintf_float(double value, int precision, + float_specs specs, + buffer& buf) -> int; +extern template auto snprintf_float(long double value, + int precision, + float_specs specs, + buffer& buf) -> int; #endif // FMT_HEADER_ONLY FMT_END_DETAIL_NAMESPACE @@ -3333,9 +3026,11 @@ inline namespace literals { \endrst */ # if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -template constexpr auto operator""_a() { - using char_t = remove_cvref_t; - return detail::udl_arg(); +template +constexpr auto operator""_a() + -> detail::udl_arg, + sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> { + return {}; } # else constexpr auto operator"" _a(const char* s, size_t) -> detail::udl_arg { diff --git a/include/fmt/os.h b/include/fmt/os.h index d7ba5f4c..b64f8bbf 100644 --- a/include/fmt/os.h +++ b/include/fmt/os.h @@ -9,8 +9,10 @@ #define FMT_OS_H_ #include +#include // locale_t #include #include +#include // strtod_l #include // std::system_error #if defined __APPLE__ || defined(__FreeBSD__) @@ -139,7 +141,7 @@ template struct formatter { }; #ifdef _WIN32 -FMT_API const std::error_category& system_category() noexcept; +FMT_API const std::error_category& system_category() FMT_NOEXCEPT; FMT_BEGIN_DETAIL_NAMESPACE // A converter from UTF-16 to UTF-8. @@ -163,7 +165,7 @@ class utf16_to_utf8 { }; FMT_API void format_windows_error(buffer& out, int error_code, - const char* message) noexcept; + const char* message) FMT_NOEXCEPT; FMT_END_DETAIL_NAMESPACE FMT_API std::system_error vwindows_error(int error_code, string_view format_str, @@ -205,9 +207,10 @@ std::system_error windows_error(int error_code, string_view message, // Reports a Windows error without throwing an exception. // Can be used to report errors from destructors. -FMT_API void report_windows_error(int error_code, const char* message) noexcept; +FMT_API void report_windows_error(int error_code, + const char* message) FMT_NOEXCEPT; #else -inline const std::error_category& system_category() noexcept { +inline const std::error_category& system_category() FMT_NOEXCEPT { return std::system_category(); } #endif // _WIN32 @@ -234,13 +237,13 @@ class buffered_file { void operator=(const buffered_file&) = delete; // Constructs a buffered_file object which doesn't represent any file. - buffered_file() noexcept : file_(nullptr) {} + buffered_file() FMT_NOEXCEPT : file_(nullptr) {} // Destroys the object closing the file it represents if any. - FMT_API ~buffered_file() noexcept; + FMT_API ~buffered_file() FMT_NOEXCEPT; public: - buffered_file(buffered_file&& other) noexcept : file_(other.file_) { + buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) { other.file_ = nullptr; } @@ -258,11 +261,10 @@ class buffered_file { FMT_API void close(); // Returns the pointer to a FILE object representing this file. - FILE* get() const noexcept { return file_; } + FILE* get() const FMT_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; void vprint(string_view format_str, format_args args) { @@ -277,12 +279,12 @@ class buffered_file { #if FMT_USE_FCNTL // A file. Closed file is represented by a file object with descriptor -1. -// Methods that are not declared with noexcept may throw +// Methods that are not declared with FMT_NOEXCEPT may throw // fmt::system_error in case of failure. Note that some errors such as // closing the file multiple times will cause a crash on Windows rather // than an exception. You can get standard behavior by overriding the // invalid parameter handler with _set_invalid_parameter_handler. -class FMT_API file { +class file { private: int fd_; // File descriptor. @@ -301,16 +303,16 @@ class FMT_API file { }; // Constructs a file object which doesn't represent any file. - file() noexcept : fd_(-1) {} + file() FMT_NOEXCEPT : fd_(-1) {} // Opens a file and constructs a file object representing this file. - file(cstring_view path, int oflag); + FMT_API file(cstring_view path, int oflag); public: file(const file&) = delete; void operator=(const file&) = delete; - file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } + file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } // Move assignment is not noexcept because close may throw. file& operator=(file&& other) { @@ -321,43 +323,43 @@ class FMT_API file { } // Destroys the object closing the file it represents if any. - ~file() noexcept; + FMT_API ~file() FMT_NOEXCEPT; // Returns the file descriptor. - int descriptor() const noexcept { return fd_; } + int descriptor() const FMT_NOEXCEPT { return fd_; } // Closes the file. - void close(); + FMT_API void close(); // Returns the file size. The size has signed type for consistency with // stat::st_size. - long long size() const; + FMT_API long long size() const; // Attempts to read count bytes from the file into the specified buffer. - size_t read(void* buffer, size_t count); + FMT_API size_t read(void* buffer, size_t count); // Attempts to write count bytes from the specified buffer to the file. - size_t write(const void* buffer, size_t count); + FMT_API size_t write(const void* buffer, size_t count); // Duplicates a file descriptor with the dup function and returns // the duplicate as a file object. - static file dup(int fd); + FMT_API static file dup(int fd); // Makes fd be the copy of this file descriptor, closing fd first if // necessary. - void dup2(int fd); + FMT_API void dup2(int fd); // Makes fd be the copy of this file descriptor, closing fd first if // necessary. - void dup2(int fd, std::error_code& ec) noexcept; + FMT_API void dup2(int fd, std::error_code& ec) FMT_NOEXCEPT; // Creates a pipe setting up read_end and write_end file objects for reading // and writing respectively. - static void pipe(file& read_end, file& write_end); + FMT_API static void pipe(file& read_end, file& write_end); // Creates a buffered_file object associated with this file and detaches // this file object from the file. - buffered_file fdopen(const char* mode); + FMT_API buffered_file fdopen(const char* mode); }; // Returns the memory page size. @@ -460,7 +462,7 @@ class FMT_API ostream final : private detail::buffer { * ````: Flags passed to `open `_ - (``file::WRONLY | file::CREATE | file::TRUNC`` by default) + (``file::WRONLY | file::CREATE`` by default) * ``buffer_size=``: Output buffer size **Example**:: @@ -475,6 +477,50 @@ inline ostream output_file(cstring_view path, T... params) { } #endif // FMT_USE_FCNTL +#ifdef FMT_LOCALE +// A "C" numeric locale. +class locale { + private: +# ifdef _WIN32 + using locale_t = _locale_t; + + static void freelocale(locale_t loc) { _free_locale(loc); } + + static double strtod_l(const char* nptr, char** endptr, _locale_t loc) { + return _strtod_l(nptr, endptr, loc); + } +# endif + + locale_t locale_; + + public: + using type = locale_t; + locale(const locale&) = delete; + void operator=(const locale&) = delete; + + locale() { +# ifndef _WIN32 + locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr)); +# else + locale_ = _create_locale(LC_NUMERIC, "C"); +# endif + if (!locale_) FMT_THROW(system_error(errno, "cannot create locale")); + } + ~locale() { freelocale(locale_); } + + type get() const { return locale_; } + + // Converts string to floating-point number and advances str past the end + // of the parsed input. + FMT_DEPRECATED double strtod(const char*& str) const { + char* end = nullptr; + double result = strtod_l(str, &end, locale_); + str = end; + return result; + } +}; +using Locale FMT_DEPRECATED_ALIAS = locale; +#endif // FMT_LOCALE FMT_MODULE_EXPORT_END FMT_END_NAMESPACE diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index 470a4df6..3d716ece 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -45,7 +45,7 @@ struct is_streamable< enable_if_t< std::is_arithmetic::value || std::is_array::value || std::is_pointer::value || std::is_same::value || - std::is_convertible>::value || + std::is_same>::value || std::is_same>::value || (std::is_convertible::value && !std::is_enum::value)>> : std::false_type {}; @@ -78,33 +78,25 @@ void format_value(buffer& buf, const T& value, output.exceptions(std::ios_base::failbit | std::ios_base::badbit); buf.try_resize(buf.size()); } -} // namespace detail // Formats an object of type T that has an overloaded ostream operator<<. -template -struct basic_ostream_formatter : formatter, Char> { - template - auto format(const T& value, basic_format_context& ctx) const +template +struct fallback_formatter::value>> + : private formatter, Char> { + using formatter, Char>::parse; + + template + auto format(const T& value, basic_format_context& ctx) -> OutputIt { auto buffer = basic_memory_buffer(); format_value(buffer, value, ctx.locale()); return formatter, Char>::format( {buffer.data(), buffer.size()}, ctx); } -}; -using ostream_formatter = basic_ostream_formatter; - -namespace detail { - -// Formats an object of type T that has an overloaded ostream operator<<. -template -struct fallback_formatter::value>> - : basic_ostream_formatter { - using basic_ostream_formatter::format; // DEPRECATED! template - auto format(const T& value, basic_printf_context& ctx) const + auto format(const T& value, basic_printf_context& ctx) -> OutputIt { auto buffer = basic_memory_buffer(); format_value(buffer, value, ctx.locale()); @@ -115,8 +107,7 @@ struct fallback_formatter::value>> FMT_MODULE_EXPORT template -void vprint(std::basic_ostream& os, - basic_string_view> format_str, +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); @@ -133,19 +124,12 @@ void vprint(std::basic_ostream& os, \endrst */ FMT_MODULE_EXPORT -template -void print(std::ostream& os, format_string fmt, T&&... args) { - vprint(os, fmt, fmt::make_format_args(args...)); +template ::value, char_t>> +void print(std::basic_ostream& os, const S& format_str, Args&&... args) { + vprint(os, to_string_view(format_str), + fmt::make_args_checked(format_str, args...)); } - -FMT_MODULE_EXPORT -template -void print(std::wostream& os, - basic_format_string...> fmt, - Args&&... args) { - vprint(os, fmt, fmt::make_format_args>(args...)); -} - FMT_END_NAMESPACE #endif // FMT_OSTREAM_H_ diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index edff8f96..eb9fb8a9 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -203,7 +203,7 @@ using make_index_sequence = make_integer_sequence; #endif template -void for_each(index_sequence, Tuple&& tup, F&& f) noexcept { +void for_each(index_sequence, Tuple&& tup, F&& f) FMT_NOEXCEPT { using std::get; // using free function get(T) now. const int _[] = {0, ((void)f(get(tup)), 0)...}; @@ -221,28 +221,9 @@ template void for_each(Tuple&& tup, F&& f) { for_each(indexes, std::forward(tup), std::forward(f)); } -#if FMT_MSC_VER -// Older MSVC doesn't get the reference type correctly for arrays. -template struct range_reference_type_impl { - using type = decltype(*detail::range_begin(std::declval())); -}; - -template struct range_reference_type_impl { - using type = T&; -}; - -template -using range_reference_type = typename range_reference_type_impl::type; -#else -template -using range_reference_type = - decltype(*detail::range_begin(std::declval())); -#endif - -// We don't use the Range's value_type for anything, but we do need the Range's -// reference type, with cv-ref stripped. template -using uncvref_type = remove_cvref_t>; +using value_type = + remove_cvref_t()))>; template OutputIt write_delimiter(OutputIt out) { *out++ = ','; @@ -250,9 +231,286 @@ template OutputIt write_delimiter(OutputIt out) { return out; } +struct singleton { + unsigned char upper; + unsigned char lower_count; +}; + +inline auto is_printable(uint16_t x, const singleton* singletons, + size_t singletons_size, + const unsigned char* singleton_lowers, + const unsigned char* normal, size_t normal_size) + -> bool { + auto upper = x >> 8; + auto lower_start = 0; + for (size_t i = 0; i < singletons_size; ++i) { + auto s = singletons[i]; + auto lower_end = lower_start + s.lower_count; + if (upper < s.upper) break; + if (upper == s.upper) { + for (auto j = lower_start; j < lower_end; ++j) { + if (singleton_lowers[j] == (x & 0xff)) return false; + } + } + lower_start = lower_end; + } + + auto xsigned = static_cast(x); + auto current = true; + for (size_t i = 0; i < normal_size; ++i) { + auto v = static_cast(normal[i]); + auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v; + xsigned -= len; + if (xsigned < 0) break; + current = !current; + } + return current; +} + +// Returns true iff the code point cp is printable. +// This code is generated by support/printable.py. +inline auto is_printable(uint32_t cp) -> bool { + static constexpr singleton singletons0[] = { + {0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8}, + {0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13}, + {0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5}, + {0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22}, + {0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3}, + {0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8}, + {0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9}, + }; + static constexpr unsigned char singletons0_lower[] = { + 0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90, + 0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f, + 0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1, + 0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04, + 0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d, + 0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf, + 0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, + 0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d, + 0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d, + 0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d, + 0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, + 0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7, + 0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, + 0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7, + 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7, + 0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e, + 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16, + 0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e, + 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f, + 0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf, + 0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0, + 0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, + 0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91, + 0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, + 0xfe, 0xff, + }; + static constexpr singleton singletons1[] = { + {0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2}, + {0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5}, + {0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5}, + {0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2}, + {0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5}, + {0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2}, + {0xfa, 2}, {0xfb, 1}, + }; + static constexpr unsigned char singletons1_lower[] = { + 0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07, + 0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36, + 0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87, + 0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, + 0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b, + 0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9, + 0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66, + 0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27, + 0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc, + 0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, + 0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6, + 0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c, + 0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66, + 0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0, + 0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93, + }; + static constexpr unsigned char normal0[] = { + 0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04, + 0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0, + 0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01, + 0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03, + 0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03, + 0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a, + 0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15, + 0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f, + 0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80, + 0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07, + 0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06, + 0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04, + 0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac, + 0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c, + 0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11, + 0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c, + 0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b, + 0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6, + 0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03, + 0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80, + 0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06, + 0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c, + 0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17, + 0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80, + 0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80, + 0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d, + }; + static constexpr unsigned char normal1[] = { + 0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f, + 0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e, + 0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04, + 0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09, + 0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16, + 0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f, + 0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36, + 0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33, + 0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08, + 0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e, + 0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41, + 0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03, + 0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22, + 0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04, + 0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45, + 0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03, + 0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81, + 0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75, + 0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1, + 0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a, + 0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11, + 0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09, + 0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89, + 0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6, + 0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09, + 0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50, + 0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05, + 0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83, + 0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05, + 0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80, + 0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80, + 0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07, + 0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e, + 0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07, + 0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06, + }; + auto lower = static_cast(cp); + if (cp < 0x10000) { + return is_printable(lower, singletons0, + sizeof(singletons0) / sizeof(*singletons0), + singletons0_lower, normal0, sizeof(normal0)); + } + if (cp < 0x20000) { + return is_printable(lower, singletons1, + sizeof(singletons1) / sizeof(*singletons1), + singletons1_lower, normal1, sizeof(normal1)); + } + if (0x2a6de <= cp && cp < 0x2a700) return false; + if (0x2b735 <= cp && cp < 0x2b740) return false; + if (0x2b81e <= cp && cp < 0x2b820) return false; + if (0x2cea2 <= cp && cp < 0x2ceb0) return false; + if (0x2ebe1 <= cp && cp < 0x2f800) return false; + if (0x2fa1e <= cp && cp < 0x30000) return false; + if (0x3134b <= cp && cp < 0xe0100) return false; + if (0xe01f0 <= cp && cp < 0x110000) return false; + return cp < 0x110000; +} + +inline auto needs_escape(uint32_t cp) -> bool { + return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' || + !is_printable(cp); +} + +template struct find_escape_result { + const Char* begin; + const Char* end; + uint32_t cp; +}; + +template +auto find_escape(const Char* begin, const Char* end) + -> find_escape_result { + for (; begin != end; ++begin) { + auto cp = static_cast::type>(*begin); + if (sizeof(Char) == 1 && cp >= 0x80) continue; + if (needs_escape(cp)) return {begin, begin + 1, cp}; + } + return {begin, nullptr, 0}; +} + +inline auto find_escape(const char* begin, const char* end) + -> find_escape_result { + if (!is_utf8()) return find_escape(begin, end); + auto result = find_escape_result{end, nullptr, 0}; + for_each_codepoint(string_view(begin, to_unsigned(end - begin)), + [&](uint32_t cp, string_view sv) { + if (needs_escape(cp)) { + result = {sv.begin(), sv.end(), cp}; + return false; + } + return true; + }); + return result; +} + template auto write_range_entry(OutputIt out, basic_string_view str) -> OutputIt { - return write_escaped_string(out, str); + *out++ = '"'; + auto begin = str.begin(), end = str.end(); + do { + auto escape = find_escape(begin, end); + out = copy_str(begin, escape.begin, out); + begin = escape.end; + if (!begin) break; + auto c = static_cast(escape.cp); + switch (escape.cp) { + case '\n': + *out++ = '\\'; + c = 'n'; + break; + case '\r': + *out++ = '\\'; + c = 'r'; + break; + case '\t': + *out++ = '\\'; + c = 't'; + break; + case '"': + FMT_FALLTHROUGH; + case '\\': + *out++ = '\\'; + break; + default: + if (is_utf8()) { + if (escape.cp < 0x100) { + out = format_to(out, "\\x{:02x}", escape.cp); + continue; + } + if (escape.cp < 0x10000) { + out = format_to(out, "\\u{:04x}", escape.cp); + continue; + } + if (escape.cp < 0x110000) { + out = format_to(out, "\\U{:08x}", escape.cp); + continue; + } + } + for (Char escape_char : basic_string_view( + escape.begin, to_unsigned(escape.end - escape.begin))) { + out = format_to( + out, "\\x{:02x}", + static_cast::type>(escape_char)); + } + continue; + } + *out++ = c; + } while (begin != end); + *out++ = '"'; + return out; } template OutputIt { template ::value)> OutputIt write_range_entry(OutputIt out, const Arg v) { - return write_escaped_char(out, v); + *out++ = '\''; + *out++ = v; + *out++ = '\''; + return out; } template < @@ -304,8 +565,7 @@ struct formatter::value>> { } template - auto format(const TupleT& values, FormatContext& ctx) const - -> decltype(ctx.out()) { + auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { auto out = ctx.out(); *out++ = '('; detail::for_each(values, format_each{0, out}); @@ -322,96 +582,43 @@ template struct is_range { !std::is_constructible, T>::value; }; -namespace detail { -template struct range_mapper { - using mapper = arg_mapper; - - template , Context>::value)> - static auto map(T&& value) -> T&& { - return static_cast(value); - } - template , Context>::value)> - static auto map(T&& value) - -> decltype(mapper().map(static_cast(value))) { - return mapper().map(static_cast(value)); - } -}; - -template -using range_formatter_type = conditional_t< - is_formattable::value, - formatter>{}.map( - std::declval()))>, - Char>, - fallback_formatter>; - -template -using maybe_const_range = - conditional_t::value, const R, R>; -} // namespace detail - -template +template struct formatter< - R, Char, + T, Char, enable_if_t< - fmt::is_range::value + 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) + && (is_formattable, Char>::value || + detail::has_fallback_formatter, Char>::value) #endif >> { - - using range_type = detail::maybe_const_range; - using formatter_type = - detail::range_formatter_type>; - formatter_type underlying_; - bool custom_specs_ = false; - template FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - auto it = ctx.begin(); - auto end = ctx.end(); - if (it == end || *it == '}') return it; - - if (*it != ':') - FMT_THROW(format_error("no top-level range formatters supported")); - - custom_specs_ = true; - ++it; - ctx.advance_to(it); - return underlying_.parse(ctx); + return ctx.begin(); } - template - auto format(range_type& range, FormatContext& ctx) const - -> decltype(ctx.out()) { + template < + typename FormatContext, typename U, + FMT_ENABLE_IF( + std::is_same::value, + const T, T>>::value)> + auto format(U& range, FormatContext& ctx) -> decltype(ctx.out()) { #ifdef FMT_DEPRECATED_BRACED_RANGES Char prefix = '{'; Char postfix = '}'; #else - Char prefix = detail::is_set::value ? '{' : '['; - Char postfix = detail::is_set::value ? '}' : ']'; + Char prefix = detail::is_set::value ? '{' : '['; + Char postfix = detail::is_set::value ? '}' : ']'; #endif - detail::range_mapper> mapper; auto out = ctx.out(); *out++ = prefix; int i = 0; - auto it = detail::range_begin(range); - auto end = detail::range_end(range); + auto it = std::begin(range); + auto end = std::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); - } + out = detail::write_range_entry(out, *it); ++i; } *out++ = postfix; @@ -422,14 +629,14 @@ struct formatter< template struct formatter< T, Char, - enable_if_t::value + enable_if_t< + detail::is_map::value // Workaround a bug in MSVC 2019 and earlier. #if !FMT_MSC_VER - && (is_formattable, Char>::value || - detail::has_fallback_formatter, - Char>::value) + && (is_formattable, Char>::value || + detail::has_fallback_formatter, Char>::value) #endif - >> { + >> { template FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { return ctx.begin(); @@ -440,7 +647,7 @@ struct formatter< FMT_ENABLE_IF( std::is_same::value, const T, T>>::value)> - auto format(U& map, FormatContext& ctx) const -> decltype(ctx.out()) { + auto format(U& map, FormatContext& ctx) -> decltype(ctx.out()) { auto out = ctx.out(); *out++ = '{'; int i = 0; diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h index 9b57815d..55825077 100644 --- a/include/fmt/xchar.h +++ b/include/fmt/xchar.h @@ -92,8 +92,8 @@ auto vformat(basic_string_view format_str, template , FMT_ENABLE_IF(!std::is_same::value)> auto format(const S& format_str, Args&&... args) -> std::basic_string { - return vformat(to_string_view(format_str), - fmt::make_format_args>(args...)); + const auto& vargs = fmt::make_args_checked(format_str, args...); + return vformat(to_string_view(format_str), vargs); } template , @@ -113,7 +113,7 @@ template std::basic_string { return detail::vformat(loc, to_string_view(format_str), - fmt::make_format_args>(args...)); + fmt::make_args_checked(format_str, args...)); } template , @@ -132,8 +132,8 @@ 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), - fmt::make_format_args>(args...)); + const auto& vargs = fmt::make_args_checked(fmt, args...); + return vformat_to(out, to_string_view(fmt), vargs); } template & 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...), {}); + const auto& vargs = fmt::make_args_checked(format_str, args...); + detail::vformat_to(buf, to_string_view(format_str), vargs, {}); return detail::buffer_appender(buf); } @@ -167,8 +167,8 @@ template < inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, Args&&... args) -> typename std::enable_if::type { - return vformat_to(out, loc, to_string_view(format_str), - fmt::make_format_args>(args...)); + const auto& vargs = fmt::make_args_checked(format_str, args...); + return vformat_to(out, loc, to_string_view(format_str), vargs); } 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), - fmt::make_format_args>(args...)); + const auto& vargs = fmt::make_args_checked(fmt, args...); + return vformat_to_n(out, n, to_string_view(fmt), vargs); } 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), - fmt::make_format_args>(args...)); + const auto& vargs = fmt::make_args_checked(fmt, args...); + detail::vformat_to(buf, to_string_view(fmt), vargs); return buf.count(); } diff --git a/src/format.cc b/src/format.cc index 70206cf4..ecb8cc79 100644 --- a/src/format.cc +++ b/src/format.cc @@ -56,10 +56,24 @@ 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; +template +int format_float(char* buf, std::size_t size, const char* format, int precision, + T value) { +#ifdef FMT_FUZZ + if (precision > 100000) + throw std::runtime_error( + "fuzz mode - avoid large allocation inside snprintf"); +#endif + // Suppress the warning about nonliteral format string. + int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; + return precision < 0 ? snprintf_ptr(buf, size, format, value) + : snprintf_ptr(buf, size, format, precision, value); +} + +template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(float x) + FMT_NOEXCEPT; +template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(double x) + FMT_NOEXCEPT; } // namespace detail // Workaround a bug in MSVC2013 that prevents instantiation of format_float. @@ -86,6 +100,11 @@ template FMT_API void detail::vformat_to( detail::buffer&, string_view, basic_format_args, detail::locale_ref); +template FMT_API int detail::snprintf_float(double, int, detail::float_specs, + detail::buffer&); +template FMT_API int detail::snprintf_float(long double, int, + detail::float_specs, + detail::buffer&); template FMT_API int detail::format_float(double, int, detail::float_specs, detail::buffer&); template FMT_API int detail::format_float(long double, int, detail::float_specs, diff --git a/src/os.cc b/src/os.cc index faa84c49..04b4dc50 100644 --- a/src/os.cc +++ b/src/os.cc @@ -35,15 +35,9 @@ # ifndef S_IRGRP # define S_IRGRP 0 # endif -# ifndef S_IWGRP -# define S_IWGRP 0 -# endif # ifndef S_IROTH # define S_IROTH 0 # endif -# ifndef S_IWOTH -# define S_IWOTH 0 -# endif # endif // _WIN32 #endif // FMT_USE_FCNTL @@ -113,7 +107,7 @@ class system_message { unsigned long result_; wchar_t* message_; - static bool is_whitespace(wchar_t c) noexcept { + static bool is_whitespace(wchar_t c) FMT_NOEXCEPT { return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0'; } @@ -132,15 +126,15 @@ class system_message { } } ~system_message() { LocalFree(message_); } - explicit operator bool() const noexcept { return result_ != 0; } - operator basic_string_view() const noexcept { + explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; } + operator basic_string_view() const FMT_NOEXCEPT { return basic_string_view(message_, result_); } }; class utf8_system_category final : public std::error_category { public: - const char* name() const noexcept override { return "system"; } + const char* name() const FMT_NOEXCEPT override { return "system"; } std::string message(int error_code) const override { system_message msg(error_code); if (msg) { @@ -155,7 +149,7 @@ class utf8_system_category final : public std::error_category { } // namespace detail -FMT_API const std::error_category& system_category() noexcept { +FMT_API const std::error_category& system_category() FMT_NOEXCEPT { static const detail::utf8_system_category category; return category; } @@ -167,7 +161,7 @@ std::system_error vwindows_error(int err_code, string_view format_str, } void detail::format_windows_error(detail::buffer& out, int error_code, - const char* message) noexcept { + const char* message) FMT_NOEXCEPT { FMT_TRY { system_message msg(error_code); if (msg) { @@ -182,12 +176,12 @@ void detail::format_windows_error(detail::buffer& out, int error_code, format_error_code(out, error_code, message); } -void report_windows_error(int error_code, const char* message) noexcept { +void report_windows_error(int error_code, const char* message) FMT_NOEXCEPT { report_error(detail::format_windows_error, error_code, message); } #endif // _WIN32 -buffered_file::~buffered_file() noexcept { +buffered_file::~buffered_file() FMT_NOEXCEPT { if (file_ && FMT_SYSTEM(fclose(file_)) != 0) report_system_error(errno, "cannot close file"); } @@ -220,8 +214,7 @@ file::file(cstring_view path, int oflag) { # ifdef _WIN32 using mode_t = int; # endif - constexpr mode_t mode = - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; # if defined(_WIN32) && !defined(__MINGW32__) fd_ = -1; FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); @@ -232,7 +225,7 @@ file::file(cstring_view path, int oflag) { FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); } -file::~file() noexcept { +file::~file() FMT_NOEXCEPT { // Don't retry close in case of EINTR! // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) @@ -306,7 +299,7 @@ void file::dup2(int fd) { } } -void file::dup2(int fd, std::error_code& ec) noexcept { +void file::dup2(int fd, std::error_code& ec) FMT_NOEXCEPT { int result = 0; FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); if (result == -1) ec = std::error_code(errno, std::generic_category()); diff --git a/support/bazel/.bazelversion b/support/bazel/.bazelversion index 0062ac97..fae6e3d0 100644 --- a/support/bazel/.bazelversion +++ b/support/bazel/.bazelversion @@ -1 +1 @@ -5.0.0 +4.2.1 diff --git a/support/printable.py b/support/printable.py index 8fa86b30..7d23d3bb 100755 --- a/support/printable.py +++ b/support/printable.py @@ -171,7 +171,7 @@ def main(): normal1 = compress_normal(normal1) print("""\ -FMT_FUNC auto is_printable(uint32_t cp) -> bool {\ +inline auto is_printable(uint32_t cp) -> bool {\ """) print_singletons(singletons0u, singletons0l, 'singletons0', 'singletons0_lower') print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower') diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c6101fac..7ed8d5f7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,7 +4,7 @@ set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc) add_library(test-main STATIC ${TEST_MAIN_SRC}) target_include_directories(test-main PUBLIC $) -target_link_libraries(test-main gtest fmt) +target_link_libraries(test-main gtest) include(CheckCXXCompilerFlag) @@ -128,6 +128,9 @@ if (NOT MSVC_STATIC_RUNTIME) if (FMT_PEDANTIC) target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) endif () + if (HAVE_STRTOD_L) + target_compile_definitions(posix-mock-test PRIVATE FMT_LOCALE) + endif () add_test(NAME posix-mock-test COMMAND posix-mock-test) add_fmt_test(os-test) endif () diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 959fb65a..42360e5f 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -557,9 +557,6 @@ TEST(chrono_test, special_durations) { "03:33"); EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration{2}), "03:33:20"); - EXPECT_EQ("44.000000000000", - fmt::format("{:%S}", std::chrono::duration( - 1.54213895E+26))); } TEST(chrono_test, unsigned_duration) { @@ -623,10 +620,6 @@ TEST(chrono_test, cpp20_duration_subsecond_support) { // fixed precision, and print zeros even if there is no fractional part. EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}), "07.000000"); - EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration>(1)), - "00.333333"); - EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration>(1)), - "00.142857"); } #endif // FMT_STATIC_THOUSANDS_SEPARATOR diff --git a/test/color-test.cc b/test/color-test.cc index c2ba13a9..af8f1494 100644 --- a/test/color-test.cc +++ b/test/color-test.cc @@ -50,12 +50,6 @@ TEST(color_test, format) { "\x1b[105mtbmagenta\x1b[0m"); EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"), "\x1b[31mfoo\x1b[0m"); - EXPECT_EQ(fmt::format("{}{}", fmt::styled("red", fg(fmt::color::red)), - fmt::styled("bold", fmt::emphasis::bold)), - "\x1b[38;2;255;000;000mred\x1b[0m\x1b[1mbold\x1b[0m"); - EXPECT_EQ(fmt::format("{}", fmt::styled("bar", fg(fmt::color::blue) | - fmt::emphasis::underline)), - "\x1b[4m\x1b[38;2;000;000;255mbar\x1b[0m"); } TEST(color_test, format_to) { diff --git a/test/compile-error-test/CMakeLists.txt b/test/compile-error-test/CMakeLists.txt index 3847a5d0..db7a9429 100644 --- a/test/compile-error-test/CMakeLists.txt +++ b/test/compile-error-test/CMakeLists.txt @@ -6,8 +6,6 @@ project(compile-error-test CXX) set(fmt_headers " #include #include - #include - #include ") set(error_test_names "") @@ -156,16 +154,6 @@ expect_compile(format-function-error " fmt::format(\"{}\", f); " ERROR) -# Formatting an unformattable argument should always be a compile time error -expect_compile(format-lots-of-arguments-with-unformattable " - struct E {}; - fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, E()); -" ERROR) -expect_compile(format-lots-of-arguments-with-function " - void (*f)(); - fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, f); -" ERROR) - # Make sure that compiler features detected in the header # match the features detected in CMake. if (SUPPORTS_USER_DEFINED_LITERALS) @@ -193,20 +181,6 @@ if (CMAKE_CXX_STANDARD GREATER_EQUAL 20) #error #endif " ERROR) - expect_compile(print-string-number-spec-error " - #ifdef FMT_HAS_CONSTEVAL - fmt::print(\"{:d}\", \"I am not a number\"); - #else - #error - #endif - " ERROR) - expect_compile(print-stream-string-number-spec-error " - #ifdef FMT_HAS_CONSTEVAL - fmt::print(std::cout, \"{:d}\", \"I am not a number\"); - #else - #error - #endif - " ERROR) # Compile-time argument name check expect_compile(format-string-name " diff --git a/test/compile-test.cc b/test/compile-test.cc index 2c156847..1765961b 100644 --- a/test/compile-test.cc +++ b/test/compile-test.cc @@ -201,11 +201,6 @@ TEST(compile_test, named) { # endif } -TEST(compile_test, join) { - unsigned char data[] = {0x1, 0x2, 0xaf}; - EXPECT_EQ("0102af", fmt::format(FMT_COMPILE("{:02x}"), fmt::join(data, ""))); -} - TEST(compile_test, format_to) { char buf[8]; auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42); diff --git a/test/core-test.cc b/test/core-test.cc index a040d241..b2f2097e 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -737,34 +737,7 @@ struct convertible_to_pointer { operator const int*() const { return nullptr; } }; -struct convertible_to_pointer_formattable { - operator const int*() const { return nullptr; } -}; - -FMT_BEGIN_NAMESPACE -template <> struct formatter { - auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); - } - - auto format(convertible_to_pointer_formattable, format_context& ctx) const - -> decltype(ctx.out()) { - auto test = string_view("test"); - return std::copy_n(test.data(), test.size(), ctx.out()); - } -}; -FMT_END_NAMESPACE - -enum class unformattable_scoped_enum {}; - -namespace test { -enum class formattable_scoped_enum {}; -auto format_as(formattable_scoped_enum) -> int { return 42; } - -struct convertible_to_enum { - operator formattable_scoped_enum() const { return {}; } -}; -} // namespace test +enum class test_scoped_enum {}; TEST(core_test, is_formattable) { #if 0 @@ -797,17 +770,14 @@ TEST(core_test, is_formattable) { #endif static_assert(!fmt::is_formattable::value, ""); - const auto f = convertible_to_pointer_formattable(); - EXPECT_EQ(fmt::format("{}", f), "test"); static_assert(!fmt::is_formattable::value, ""); struct s; + static_assert(!fmt::is_formattable::value, ""); static_assert(!fmt::is_formattable::value, ""); - static_assert(!fmt::is_formattable::value, ""); - static_assert(fmt::is_formattable::value, ""); - static_assert(!fmt::is_formattable::value, ""); + static_assert(!fmt::is_formattable::value, ""); } TEST(core_test, format) { EXPECT_EQ(fmt::format("{}", 42), "42"); } @@ -818,10 +788,6 @@ TEST(core_test, format_to) { EXPECT_EQ(s, "42"); } -TEST(core_test, format_as) { - EXPECT_EQ(fmt::format("{}", test::formattable_scoped_enum()), "42"); -} - struct convertible_to_int { operator int() const { return 42; } }; @@ -890,10 +856,7 @@ struct explicitly_convertible_to_string_view { }; TEST(core_test, format_explicitly_convertible_to_string_view) { - // Types explicitly convertible to string_view are not formattable by - // default because it may introduce ODR violations. - static_assert( - !fmt::is_formattable::value, ""); + EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_view())); } # ifdef FMT_USE_STRING_VIEW @@ -902,11 +865,8 @@ struct explicitly_convertible_to_std_string_view { }; TEST(core_test, format_explicitly_convertible_to_std_string_view) { - // Types explicitly convertible to string_view are not formattable by - // default because it may introduce ODR violations. - static_assert( - !fmt::is_formattable::value, - ""); + EXPECT_EQ("foo", + fmt::format("{}", explicitly_convertible_to_std_string_view())); } # endif #endif diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc index e718150a..a012306f 100644 --- a/test/format-impl-test.cc +++ b/test/format-impl-test.cc @@ -96,6 +96,23 @@ TEST(bigint_test, multiply) { EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax)); } +TEST(bigint_test, accumulator) { + fmt::detail::accumulator acc; + EXPECT_EQ(acc.lower, 0); + EXPECT_EQ(acc.upper, 0); + acc.upper = 12; + acc.lower = 34; + EXPECT_EQ(static_cast(acc), 34); + acc += 56; + EXPECT_EQ(acc.lower, 90); + acc += max_value(); + EXPECT_EQ(acc.upper, 13); + EXPECT_EQ(acc.lower, 89); + acc >>= 32; + EXPECT_EQ(acc.upper, 0); + EXPECT_EQ(acc.lower, 13 * 0x100000000); +} + TEST(bigint_test, square) { bigint n0(0); n0.square(); @@ -190,14 +207,14 @@ TEST(fp_test, get_cached_power) { using limits = std::numeric_limits; for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) { int dec_exp = 0; - auto power = fmt::detail::get_cached_power(exp, dec_exp); - bigint exact, cache(power.f); + auto fp = fmt::detail::get_cached_power(exp, dec_exp); + bigint exact, cache(fp.f); if (dec_exp >= 0) { exact.assign_pow10(dec_exp); - if (power.e <= 0) - exact <<= -power.e; + if (fp.e <= 0) + exact <<= -fp.e; else - cache <<= power.e; + cache <<= fp.e; exact.align(cache); cache.align(exact); auto exact_str = fmt::format("{}", exact); @@ -211,9 +228,9 @@ TEST(fp_test, get_cached_power) { EXPECT_EQ(diff, 0); } else { cache.assign_pow10(-dec_exp); - cache *= power.f + 1; // Inexact check. + cache *= fp.f + 1; // Inexact check. exact.assign(1); - exact <<= -power.e; + exact <<= -fp.e; exact.align(cache); auto exact_str = fmt::format("{}", exact); auto cache_str = fmt::format("{}", cache); @@ -226,17 +243,14 @@ TEST(fp_test, get_cached_power) { TEST(fp_test, dragonbox_max_k) { using fmt::detail::dragonbox::floor_log10_pow2; using float_info = fmt::detail::dragonbox::float_info; - EXPECT_EQ( - fmt::detail::const_check(float_info::max_k), - float_info::kappa - - floor_log10_pow2(std::numeric_limits::min_exponent - - fmt::detail::num_significand_bits() - 1)); + EXPECT_EQ(fmt::detail::const_check(float_info::max_k), + float_info::kappa - floor_log10_pow2(float_info::min_exponent - + float_info::significand_bits)); using double_info = fmt::detail::dragonbox::float_info; EXPECT_EQ( fmt::detail::const_check(double_info::max_k), - double_info::kappa - - floor_log10_pow2(std::numeric_limits::min_exponent - - fmt::detail::num_significand_bits() - 1)); + double_info::kappa - floor_log10_pow2(double_info::min_exponent - + double_info::significand_bits)); } TEST(fp_test, get_round_direction) { @@ -343,6 +357,14 @@ TEST(format_impl_test, count_digits) { test_count_digits(); } +TEST(format_impl_test, write_fallback_uintptr) { + std::string s; + fmt::detail::write_ptr( + std::back_inserter(s), + fmt::detail::fallback_uintptr(reinterpret_cast(0xface)), nullptr); + EXPECT_EQ(s, "0xface"); +} + #ifdef _WIN32 # include #endif diff --git a/test/format-test.cc b/test/format-test.cc index 09553af4..a8592ef0 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -39,75 +39,6 @@ using testing::StrictMock; enum { buffer_size = 256 }; -TEST(uint128_test, ctor) { - using fmt::detail::uint128_fallback; - auto n = uint128_fallback(); - EXPECT_EQ(n, 0); - n = uint128_fallback(42); - EXPECT_EQ(n, 42); - EXPECT_EQ(static_cast(n), 42); -} - -TEST(uint128_test, shift) { - auto n = fmt::detail::uint128_fallback(42); - n = n << 64; - EXPECT_EQ(static_cast(n), 0); - n = n >> 64; - EXPECT_EQ(static_cast(n), 42); - n = n << 62; - EXPECT_EQ(static_cast(n >> 64), 0xa); - EXPECT_EQ(static_cast(n), 0x8000000000000000); - n = n >> 62; - EXPECT_EQ(static_cast(n), 42); -} - -TEST(uint128_test, minus) { - auto n = fmt::detail::uint128_fallback(42); - EXPECT_EQ(n - 2, 40); -} - -template void check_isfinite() { - using fmt::detail::isfinite; - EXPECT_TRUE(isfinite(Float(0.0))); - EXPECT_TRUE(isfinite(Float(42.0))); - EXPECT_TRUE(isfinite(Float(-42.0))); - EXPECT_TRUE(isfinite(Float(fmt::detail::max_value()))); - // Use double because std::numeric_limits is broken for __float128. - using limits = std::numeric_limits; - EXPECT_FALSE(isfinite(Float(limits::infinity()))); - EXPECT_FALSE(isfinite(Float(-limits::infinity()))); - EXPECT_FALSE(isfinite(Float(limits::quiet_NaN()))); - EXPECT_FALSE(isfinite(Float(-limits::quiet_NaN()))); -} - -TEST(float_test, isfinite) { - check_isfinite(); -#ifdef __SIZEOF_FLOAT128__ - check_isfinite(); -#endif -} - -template void check_isnan() { - using fmt::detail::isnan; - EXPECT_FALSE(isnan(Float(0.0))); - EXPECT_FALSE(isnan(Float(42.0))); - EXPECT_FALSE(isnan(Float(-42.0))); - EXPECT_FALSE(isnan(Float(fmt::detail::max_value()))); - // Use double because std::numeric_limits is broken for __float128. - using limits = std::numeric_limits; - EXPECT_FALSE(isnan(Float(limits::infinity()))); - EXPECT_FALSE(isnan(Float(-limits::infinity()))); - EXPECT_TRUE(isnan(Float(limits::quiet_NaN()))); - EXPECT_TRUE(isnan(Float(-limits::quiet_NaN()))); -} - -TEST(float_test, isnan) { - check_isnan(); -#ifdef __SIZEOF_FLOAT128__ - check_isnan(); -#endif -} - struct uint32_pair { uint32_t u[2]; }; @@ -292,9 +223,8 @@ TEST(memory_buffer_test, move_ctor_dynamic_buffer) { buffer.push_back('a'); basic_memory_buffer buffer2(std::move(buffer)); // Move should rip the guts of the first buffer. - EXPECT_EQ(&buffer[0], inline_buffer_ptr); - EXPECT_EQ(buffer.size(), 0); - EXPECT_EQ(std::string(&buffer2[0], buffer2.size()), "testa"); + EXPECT_EQ(inline_buffer_ptr, &buffer[0]); + EXPECT_EQ("testa", std::string(&buffer2[0], buffer2.size())); EXPECT_GT(buffer2.capacity(), 4u); } @@ -395,7 +325,7 @@ template class max_size_allocator : public Allocator { public: using typename Allocator::value_type; - size_t max_size() const noexcept { return MaxSize; } + size_t max_size() const FMT_NOEXCEPT { return MaxSize; } value_type* allocate(size_t n) { if (n > max_size()) { throw std::length_error("size > max_size"); @@ -640,9 +570,6 @@ TEST(format_test, plus_sign) { EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ul), format_error, "format specifier requires signed argument"); EXPECT_EQ("+42", fmt::format("{0:+}", 42ll)); -#if FMT_USE_INT128 - EXPECT_EQ("+42", fmt::format("{0:+}", __int128_t(42))); -#endif EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ull), format_error, "format specifier requires signed argument"); EXPECT_EQ("+42", fmt::format("{0:+}", 42.0)); @@ -991,11 +918,6 @@ TEST(format_test, precision) { EXPECT_THAT(outputs, testing::Contains(fmt::format("{:.838A}", -2.14001164E+38))); - if (std::numeric_limits::digits == 64) { - auto ld = (std::numeric_limits::min)(); - EXPECT_EQ(fmt::format("{:.0}", ld), "3e-4932"); - } - EXPECT_EQ("123.", fmt::format("{:#.0f}", 123.0)); EXPECT_EQ("1.23", fmt::format("{:.02f}", 1.234)); EXPECT_EQ("0.001", fmt::format("{:.1g}", 0.001)); @@ -1138,7 +1060,7 @@ TEST(format_test, format_short) { template void check_unknown_types(const T& value, const char* types, const char*) { char format_str[buffer_size]; - const char* special = ".0123456789L?}"; + const char* special = ".0123456789L}"; for (int i = CHAR_MIN; i <= CHAR_MAX; ++i) { char c = static_cast(i); if (std::strchr(types, c) || std::strchr(special, c) || !c) continue; @@ -1307,30 +1229,32 @@ TEST(format_test, format_float) { } TEST(format_test, format_double) { - EXPECT_EQ(fmt::format("{}", 0.0), "0"); + EXPECT_EQ("0", fmt::format("{}", 0.0)); check_unknown_types(1.2, "eEfFgGaAnL%", "double"); - EXPECT_EQ(fmt::format("{:}", 0.0), "0"); - EXPECT_EQ(fmt::format("{:f}", 0.0), "0.000000"); - EXPECT_EQ(fmt::format("{:g}", 0.0), "0"); - EXPECT_EQ(fmt::format("{:}", 392.65), "392.65"); - EXPECT_EQ(fmt::format("{:g}", 392.65), "392.65"); - EXPECT_EQ(fmt::format("{:G}", 392.65), "392.65"); - EXPECT_EQ(fmt::format("{:g}", 4.9014e6), "4.9014e+06"); - EXPECT_EQ(fmt::format("{:f}", 392.65), "392.650000"); - EXPECT_EQ(fmt::format("{:F}", 392.65), "392.650000"); - EXPECT_EQ(fmt::format("{:L}", 42.0), "42"); - EXPECT_EQ(fmt::format("{:24a}", 4.2), " 0x1.0cccccccccccdp+2"); - EXPECT_EQ(fmt::format("{:<24a}", 4.2), "0x1.0cccccccccccdp+2 "); - EXPECT_EQ(fmt::format("{0:e}", 392.65), "3.926500e+02"); - EXPECT_EQ(fmt::format("{0:E}", 392.65), "3.926500E+02"); - EXPECT_EQ(fmt::format("{0:+010.4g}", 392.65), "+0000392.6"); + EXPECT_EQ("0", fmt::format("{:}", 0.0)); + EXPECT_EQ("0.000000", fmt::format("{:f}", 0.0)); + EXPECT_EQ("0", fmt::format("{:g}", 0.0)); + EXPECT_EQ("392.65", fmt::format("{:}", 392.65)); + EXPECT_EQ("392.65", fmt::format("{:g}", 392.65)); + EXPECT_EQ("392.65", fmt::format("{:G}", 392.65)); + EXPECT_EQ("4.9014e+06", fmt::format("{:g}", 4.9014e6)); + EXPECT_EQ("392.650000", fmt::format("{:f}", 392.65)); + EXPECT_EQ("392.650000", fmt::format("{:F}", 392.65)); + EXPECT_EQ("42", fmt::format("{:L}", 42.0)); + EXPECT_EQ(" 0x1.0cccccccccccdp+2", fmt::format("{:24a}", 4.2)); + EXPECT_EQ("0x1.0cccccccccccdp+2 ", fmt::format("{:<24a}", 4.2)); char buffer[buffer_size]; + safe_sprintf(buffer, "%e", 392.65); + EXPECT_EQ(buffer, fmt::format("{0:e}", 392.65)); + safe_sprintf(buffer, "%E", 392.65); + EXPECT_EQ(buffer, fmt::format("{0:E}", 392.65)); + EXPECT_EQ("+0000392.6", fmt::format("{0:+010.4g}", 392.65)); safe_sprintf(buffer, "%a", -42.0); - EXPECT_EQ(fmt::format("{:a}", -42.0), buffer); + EXPECT_EQ(buffer, fmt::format("{:a}", -42.0)); safe_sprintf(buffer, "%A", -42.0); - EXPECT_EQ(fmt::format("{:A}", -42.0), buffer); - EXPECT_EQ(fmt::format("{:f}", 9223372036854775807.0), - "9223372036854775808.000000"); + EXPECT_EQ(buffer, fmt::format("{:A}", -42.0)); + EXPECT_EQ("9223372036854775808.000000", + fmt::format("{:f}", 9223372036854775807.0)); } TEST(format_test, precision_rounding) { @@ -1439,9 +1363,6 @@ TEST(format_test, format_char) { << format_str; } EXPECT_EQ(fmt::format("{:02X}", n), fmt::format("{:02X}", 'x')); - - EXPECT_EQ("\n", fmt::format("{}", '\n')); - EXPECT_EQ("'\\n'", fmt::format("{:?}", '\n')); } TEST(format_test, format_volatile_char) { @@ -1488,43 +1409,14 @@ TEST(format_test, format_pointer) { EXPECT_EQ("0x0", fmt::format("{}", nullptr)); } -TEST(format_test, write_uintptr_fallback) { - // Test that formatting a pointer by converting it to uint128_fallback works. - // This is needed to support systems without uintptr_t. - auto s = std::string(); - fmt::detail::write_ptr( - std::back_inserter(s), - fmt::detail::bit_cast( - reinterpret_cast(0xface)), - nullptr); - EXPECT_EQ(s, "0xface"); -} - -enum class color { red, green, blue }; - -namespace test_ns { -enum class color { red, green, blue }; -using fmt::enums::format_as; -} // namespace test_ns - -TEST(format_test, format_enum_class) { - EXPECT_EQ(fmt::format("{}", fmt::underlying(color::red)), "0"); - EXPECT_EQ(fmt::format("{}", test_ns::color::red), "0"); -} - TEST(format_test, format_string) { - EXPECT_EQ(fmt::format("{0}", std::string("test")), "test"); - EXPECT_EQ(fmt::format("{0}", std::string("test")), "test"); - EXPECT_EQ(fmt::format("{:?}", std::string("test")), "\"test\""); - EXPECT_EQ(fmt::format("{:*^10?}", std::string("test")), "**\"test\"**"); - EXPECT_EQ(fmt::format("{:?}", std::string("\test")), "\"\\test\""); + EXPECT_EQ("test", fmt::format("{0}", std::string("test"))); EXPECT_THROW((void)fmt::format(fmt::runtime("{:x}"), std::string("test")), fmt::format_error); } TEST(format_test, format_string_view) { EXPECT_EQ("test", fmt::format("{}", string_view("test"))); - EXPECT_EQ("\"t\\nst\"", fmt::format("{:?}", string_view("t\nst"))); EXPECT_EQ("", fmt::format("{}", string_view())); } @@ -1816,7 +1708,6 @@ TEST(format_test, compile_time_string) { "foo"_a = "foo")); EXPECT_EQ("", fmt::format(FMT_STRING(""))); EXPECT_EQ("", fmt::format(FMT_STRING(""), "arg"_a = 42)); - EXPECT_EQ("42", fmt::format(FMT_STRING("{answer}"), "answer"_a = Answer())); #endif (void)static_with_null; @@ -1886,8 +1777,6 @@ TEST(format_test, named_arg_udl) { fmt::format("{first}{second}{first}{third}", fmt::arg("first", "abra"), fmt::arg("second", "cad"), fmt::arg("third", 99)), udl_a); - - EXPECT_EQ("42", fmt::format("{answer}", "answer"_a = Answer())); } #endif // FMT_USE_USER_DEFINED_LITERALS diff --git a/test/gtest-extra.cc b/test/gtest-extra.cc index 542e4b5e..1d48a173 100644 --- a/test/gtest-extra.cc +++ b/test/gtest-extra.cc @@ -23,7 +23,7 @@ output_redirect::output_redirect(FILE* f) : file_(f) { write_end.dup2(fd); } -output_redirect::~output_redirect() noexcept { +output_redirect::~output_redirect() FMT_NOEXCEPT { try { restore(); } catch (const std::exception& e) { diff --git a/test/gtest-extra.h b/test/gtest-extra.h index ef2a04e7..df2b1d8c 100644 --- a/test/gtest-extra.h +++ b/test/gtest-extra.h @@ -83,7 +83,7 @@ class output_redirect { public: explicit output_redirect(FILE* file); - ~output_redirect() noexcept; + ~output_redirect() FMT_NOEXCEPT; output_redirect(const output_redirect&) = delete; void operator=(const output_redirect&) = delete; diff --git a/test/module-test.cc b/test/module-test.cc index 62e5fe8b..39c83983 100644 --- a/test/module-test.cc +++ b/test/module-test.cc @@ -36,6 +36,7 @@ #else # define FMT_USE_FCNTL 0 #endif +#define FMT_NOEXCEPT noexcept #if defined(_WIN32) && !defined(__MINGW32__) # define FMT_POSIX(call) _##call #else @@ -195,6 +196,13 @@ TEST(module_test, wformat_args) { EXPECT_TRUE(args.get(0)); } +TEST(module_test, checked_format_args) { + fmt::basic_format_args args = fmt::make_args_checked("{}", 42); + EXPECT_TRUE(args.get(0)); + fmt::basic_format_args wargs = fmt::make_args_checked(L"{}", 42); + EXPECT_TRUE(wargs.get(0)); +} + TEST(module_test, dynamic_format_args) { fmt::dynamic_format_arg_store dyn_store; dyn_store.push_back(fmt::arg("a42", 42)); diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 3834c707..f81039e5 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -53,16 +53,6 @@ std::ostream& operator<<(std::ostream& os, streamable_enum) { enum unstreamable_enum {}; -struct empty_test {}; -std::ostream& operator<<(std::ostream& os, empty_test) { return os << ""; } - -namespace fmt { -template <> struct formatter : ostream_formatter {}; -template <> struct formatter : ostream_formatter {}; -template <> struct formatter : ostream_formatter {}; -template <> struct formatter : ostream_formatter {}; -} // namespace fmt - TEST(ostream_test, enum) { EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_enum())); EXPECT_EQ("0", fmt::format("{}", unstreamable_enum())); @@ -96,6 +86,9 @@ TEST(ostream_test, format_specs) { EXPECT_EQ("te", fmt::format("{0:.{1}}", test_string("test"), 2)); } +struct empty_test {}; +std::ostream& operator<<(std::ostream& os, empty_test) { return os << ""; } + TEST(ostream_test, empty_custom_output) { EXPECT_EQ("", fmt::format("{}", empty_test())); } @@ -191,8 +184,6 @@ template struct formatter> : formatter { return formatter::format(2, ctx); } }; - -template <> struct formatter : ostream_formatter {}; } // namespace fmt TEST(ostream_test, template) { @@ -223,32 +214,52 @@ TEST(ostream_test, disable_builtin_ostream_operators) { EXPECT_EQ("foo", fmt::format("{}", convertible("foo"))); } -struct streamable_and_convertible_to_bool { - operator bool() const { return true; } +struct explicitly_convertible_to_string_like { + template ::value>::type> + explicit operator String() const { + return String("foo", 3u); + } }; -std::ostream& operator<<(std::ostream& os, streamable_and_convertible_to_bool) { - return os << "foo"; +std::ostream& operator<<(std::ostream& os, + explicitly_convertible_to_string_like) { + return os << "bar"; } -TEST(ostream_test, format_convertible_to_bool) { - // operator<< is intentionally not used because of potential ODR violations. - EXPECT_EQ(fmt::format("{}", streamable_and_convertible_to_bool()), "true"); +TEST(ostream_test, format_explicitly_convertible_to_string_like) { + EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like())); } -struct streamable_and_convertible_to_string_view { - operator fmt::string_view() const { return "foo"; } +#ifdef FMT_USE_STRING_VIEW +struct explicitly_convertible_to_std_string_view { + explicit operator fmt::detail::std_string_view() const { + return {"foo", 3u}; + } }; std::ostream& operator<<(std::ostream& os, - streamable_and_convertible_to_string_view) { + explicitly_convertible_to_std_string_view) { return os << "bar"; } -TEST(ostream_test, format_convertible_to_string_vew) { +TEST(ostream_test, format_explicitly_convertible_to_std_string_view) { + EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like())); +} +#endif // FMT_USE_STRING_VIEW + +struct streamable_and_convertible_to_bool { + operator bool() const { return true; } +}; + +std::ostream& operator<<(std::ostream& os, streamable_and_convertible_to_bool) { + return os << "foo"; +} + +TEST(ostream_test, format_convertible_to_bool) { // operator<< is intentionally not used because of potential ODR violations. - EXPECT_EQ(fmt::format("{}", streamable_and_convertible_to_string_view()), - "foo"); + EXPECT_EQ(fmt::format("{}", streamable_and_convertible_to_bool()), "true"); } struct copyfmt_test {}; @@ -259,10 +270,6 @@ std::ostream& operator<<(std::ostream& os, copyfmt_test) { return os << "foo"; } -namespace fmt { -template <> struct formatter : ostream_formatter {}; -} // namespace fmt - TEST(ostream_test, copyfmt) { EXPECT_EQ("foo", fmt::format("{}", copyfmt_test())); } @@ -284,10 +291,6 @@ struct abstract { } }; -namespace fmt { -template <> struct formatter : ostream_formatter {}; -} // namespace fmt - void format_abstract_compiles(const abstract& a) { fmt::format(FMT_COMPILE("{}"), a); } diff --git a/test/posix-mock-test.cc b/test/posix-mock-test.cc index 8a2214e8..255e216c 100644 --- a/test/posix-mock-test.cc +++ b/test/posix-mock-test.cc @@ -457,3 +457,84 @@ TEST(scoped_mock, scope) { } EXPECT_EQ(nullptr, test_mock::instance); } + +#ifdef FMT_LOCALE + +using locale_type = fmt::locale::type; + +struct locale_mock { + static locale_mock* instance; + MOCK_METHOD3(newlocale, locale_type(int category_mask, const char* locale, + locale_type base)); + MOCK_METHOD1(freelocale, void(locale_type locale)); +} * locale_mock::instance; + +# ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4273) +# ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Winconsistent-dllimport" +# endif + +_locale_t _create_locale(int category, const char* locale) { + return locale_mock::instance->newlocale(category, locale, 0); +} + +void _free_locale(_locale_t locale) { + locale_mock::instance->freelocale(locale); +} +# ifdef __clang__ +# pragma clang diagnostic pop +# endif +# pragma warning(pop) +# endif + +# if defined(__THROW) && \ + ((FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408) || defined(__e2k__)) +# define FMT_LOCALE_THROW __THROW +# else +# define FMT_LOCALE_THROW +# endif + +# if defined(__APPLE__) || \ + (defined(__FreeBSD__) && __FreeBSD_version < 1200002) +typedef int FreeLocaleResult; +# else +typedef void FreeLocaleResult; +# endif + +FreeLocaleResult freelocale(locale_type locale) FMT_LOCALE_THROW { + locale_mock::instance->freelocale(locale); + return FreeLocaleResult(); +} + +# undef FMT_LOCALE_THROW + +# ifndef _WIN32 +locale_t test::newlocale(int category_mask, const char* locale, locale_t base) { + return locale_mock::instance->newlocale(category_mask, locale, base); +} + +TEST(locale_test, locale_mock) { + scoped_mock mock; + auto locale = reinterpret_cast(11); + EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale)); + FMT_SYSTEM(newlocale(222, "foo", locale)); +} +# endif + +TEST(locale_test, locale) { +# ifndef LC_NUMERIC_MASK + enum { LC_NUMERIC_MASK = LC_NUMERIC }; +# endif + scoped_mock mock; + auto impl = reinterpret_cast(42); + EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr)) + .WillOnce(Return(impl)); + EXPECT_CALL(mock, freelocale(impl)); + fmt::locale loc; + EXPECT_EQ(impl, loc.get()); +} + +#endif // FMT_LOCALE diff --git a/test/printf-test.cc b/test/printf-test.cc index 91869c2f..0bb9ccda 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -11,6 +11,7 @@ #include #include +#include "fmt/ostream.h" #include "fmt/xchar.h" #include "gtest-extra.h" #include "util.h" @@ -532,6 +533,10 @@ TEST(printf_test, wide_string) { EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); } +TEST(printf_test, printf_custom) { + EXPECT_EQ("abc", test_sprintf("%s", test_string("abc"))); +} + TEST(printf_test, vprintf) { fmt::format_arg_store as{42}; fmt::basic_format_args args(as); diff --git a/test/ranges-test.cc b/test/ranges-test.cc index aa910e79..63cb8c8b 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -46,13 +46,11 @@ TEST(ranges_test, format_array_of_literals) { TEST(ranges_test, format_vector) { auto v = std::vector{1, 2, 3, 5, 7, 11}; EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]"); - EXPECT_EQ(fmt::format("{::#x}", v), "[0x1, 0x2, 0x3, 0x5, 0x7, 0xb]"); } TEST(ranges_test, format_vector2) { auto v = std::vector>{{1, 2}, {3, 5}, {7, 11}}; EXPECT_EQ(fmt::format("{}", v), "[[1, 2], [3, 5], [7, 11]]"); - EXPECT_EQ(fmt::format("{:::#x}", v), "[[0x1, 0x2], [0x3, 0x5], [0x7, 0xb]]"); } TEST(ranges_test, format_map) { @@ -65,25 +63,6 @@ TEST(ranges_test, format_set) { "{\"one\", \"two\"}"); } -namespace adl { -struct box { - int value; -}; - -auto begin(const box& b) -> const int* { - return &b.value; -} - -auto end(const box& b) -> const int* { - return &b.value + 1; -} -} // namespace adl - -TEST(ranges_test, format_adl_begin_end) { - auto b = adl::box{42}; - EXPECT_EQ(fmt::format("{}", b), "[42]"); -} - TEST(ranges_test, format_pair) { auto p = std::pair(42, 1.5f); EXPECT_EQ(fmt::format("{}", p), "(42, 1.5)"); @@ -152,8 +131,8 @@ TEST(ranges_test, path_like) { struct string_like { const char* begin(); const char* end(); - operator fmt::string_view() const { return "foo"; } - operator std::string_view() const { return "foo"; } + explicit operator fmt::string_view() const { return "foo"; } + explicit operator std::string_view() const { return "foo"; } }; TEST(ranges_test, format_string_like) { @@ -317,7 +296,6 @@ static_assert(std::input_iterator); TEST(ranges_test, join_sentinel) { auto hello = zstring{"hello"}; EXPECT_EQ(fmt::format("{}", hello), "['h', 'e', 'l', 'l', 'o']"); - EXPECT_EQ(fmt::format("{::}", hello), "[h, e, l, l, o]"); EXPECT_EQ(fmt::format("{}", fmt::join(hello, "_")), "h_e_l_l_o"); } @@ -383,18 +361,3 @@ TEST(ranges_test, escape_convertible_to_string_view) { "[\"foo\"]"); } #endif // FMT_USE_STRING_VIEW - -template struct fmt_ref_view { - R* r; - - auto begin() const -> decltype(r->begin()) { return r->begin(); } - auto end() const -> decltype(r->end()) { return r->end(); } -}; - -TEST(ranges_test, range_of_range_of_mixed_const) { - std::vector> v = {{1, 2, 3}, {4, 5}}; - EXPECT_EQ(fmt::format("{}", v), "[[1, 2, 3], [4, 5]]"); - - fmt_ref_view r{&v}; - EXPECT_EQ(fmt::format("{}", r), "[[1, 2, 3], [4, 5]]"); -} diff --git a/test/xchar-test.cc b/test/xchar-test.cc index 4386e8b9..346cd212 100644 --- a/test/xchar-test.cc +++ b/test/xchar-test.cc @@ -72,10 +72,8 @@ struct explicitly_convertible_to_wstring_view { }; TEST(xchar_test, format_explicitly_convertible_to_wstring_view) { - // Types explicitly convertible to wstring_view are not formattable by - // default because it may introduce ODR violations. - static_assert( - !fmt::is_formattable::value, ""); + EXPECT_EQ(L"foo", + fmt::format(L"{}", explicitly_convertible_to_wstring_view())); } #endif @@ -220,12 +218,6 @@ std::wostream& operator<<(std::wostream& os, streamable_enum) { return os << L"streamable_enum"; } -namespace fmt { -template <> -struct formatter : basic_ostream_formatter { -}; -} // namespace fmt - enum unstreamable_enum {}; TEST(xchar_test, enum) { @@ -244,14 +236,14 @@ namespace fake_qt { class QString { public: QString(const wchar_t* s) : s_(s) {} - const wchar_t* utf16() const noexcept { return s_.data(); } - int size() const noexcept { return static_cast(s_.size()); } + const wchar_t* utf16() const FMT_NOEXCEPT { return s_.data(); } + int size() const FMT_NOEXCEPT { return static_cast(s_.size()); } private: std::wstring s_; }; -fmt::basic_string_view to_string_view(const QString& s) noexcept { +fmt::basic_string_view to_string_view(const QString& s) FMT_NOEXCEPT { return {s.utf16(), static_cast(s.size())}; } } // namespace fake_qt @@ -330,11 +322,9 @@ TEST(xchar_test, color) { } TEST(xchar_test, ostream) { -#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 409 std::wostringstream wos; fmt::print(wos, L"Don't {}!", L"panic"); - EXPECT_EQ(wos.str(), L"Don't panic!"); -#endif + EXPECT_EQ(L"Don't panic!", wos.str()); } TEST(xchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); }