Externals: update fmtlib to latest version (10.0.0)
The library is used by OBJ/PLY exporters, and asset_catalog_tree_view. Performance of OBJ/PLY export seems to be the same. Blender executable gets a tiny bit smaller (-5kb) on windows.
This commit is contained in:
2
extern/fmtlib/LICENSE.rst
vendored
2
extern/fmtlib/LICENSE.rst
vendored
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2012 - present, Victor Zverovich
|
||||
Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
2
extern/fmtlib/README.blender
vendored
2
extern/fmtlib/README.blender
vendored
@@ -1,7 +1,7 @@
|
||||
Project: {fmt}
|
||||
URL: https://github.com/fmtlib/fmt
|
||||
License: MIT
|
||||
Upstream version: 8.1.1 (b6f4cea)
|
||||
Upstream version: 10.0.0 (a0b8a92, 2023 May 10)
|
||||
Local modifications:
|
||||
|
||||
- Took only files needed for Blender: LICENSE, README and include/fmt
|
||||
|
||||
56
extern/fmtlib/README.rst
vendored
56
extern/fmtlib/README.rst
vendored
@@ -1,5 +1,7 @@
|
||||
{fmt}
|
||||
=====
|
||||
.. image:: https://user-images.githubusercontent.com/
|
||||
576385/156254208-f5b743a9-88cf-439d-b0c0-923d53e8d551.png
|
||||
:width: 25%
|
||||
:alt: {fmt}
|
||||
|
||||
.. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg
|
||||
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux
|
||||
@@ -10,9 +12,6 @@
|
||||
.. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg
|
||||
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows
|
||||
|
||||
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v?svg=true
|
||||
:target: https://ci.appveyor.com/project/vitaut/fmt
|
||||
|
||||
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg
|
||||
:alt: fmt is continuously fuzzed at oss-fuzz
|
||||
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\
|
||||
@@ -26,12 +25,13 @@
|
||||
**{fmt}** is an open-source formatting library providing a fast and safe
|
||||
alternative to C stdio and C++ iostreams.
|
||||
|
||||
If you like this project, please consider donating to the BYSOL
|
||||
Foundation that helps victims of political repressions in Belarus:
|
||||
https://bysol.org/en/bs/general/.
|
||||
If you like this project, please consider donating to one of the funds that
|
||||
help victims of the war in Ukraine: https://www.stopputin.net/.
|
||||
|
||||
`Documentation <https://fmt.dev>`__
|
||||
|
||||
`Cheat Sheets <https://hackingcpp.com/cpp/libs/fmt.html>`__
|
||||
|
||||
Q&A: ask questions on `StackOverflow with the tag fmt
|
||||
<https://stackoverflow.com/questions/tagged/fmt>`_.
|
||||
|
||||
@@ -47,7 +47,8 @@ Features
|
||||
* `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar to Python's
|
||||
`format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
|
||||
* Fast IEEE 754 floating-point formatter with correct rounding, shortness and
|
||||
round-trip guarantees
|
||||
round-trip guarantees using the `Dragonbox <https://github.com/jk-jeon/dragonbox>`_
|
||||
algorithm
|
||||
* Safe `printf implementation
|
||||
<https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX
|
||||
extension for positional arguments
|
||||
@@ -123,7 +124,7 @@ Output::
|
||||
Default format: 42s 100ms
|
||||
strftime-like format: 03:15:30
|
||||
|
||||
**Print a container** (`run <https://godbolt.org/z/MjsY7c>`_)
|
||||
**Print a container** (`run <https://godbolt.org/z/MxM1YqjE7>`_)
|
||||
|
||||
.. code:: c++
|
||||
|
||||
@@ -191,24 +192,24 @@ Speed tests
|
||||
================= ============= ===========
|
||||
Library Method Run Time, s
|
||||
================= ============= ===========
|
||||
libc printf 1.04
|
||||
libc++ std::ostream 3.05
|
||||
{fmt} 6.1.1 fmt::print 0.75
|
||||
Boost Format 1.67 boost::format 7.24
|
||||
Folly Format folly::format 2.23
|
||||
libc printf 0.91
|
||||
libc++ std::ostream 2.49
|
||||
{fmt} 9.1 fmt::print 0.74
|
||||
Boost Format 1.80 boost::format 6.26
|
||||
Folly Format folly::format 1.87
|
||||
================= ============= ===========
|
||||
|
||||
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``.
|
||||
{fmt} is the fastest of the benchmarked methods, ~20% faster than ``printf``.
|
||||
|
||||
The above results were generated by building ``tinyformat_test.cpp`` on macOS
|
||||
10.14.6 with ``clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT``, and taking the
|
||||
12.6.1 with ``clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT``, and taking the
|
||||
best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
|
||||
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
|
||||
further details refer to the `source
|
||||
<https://github.com/fmtlib/format-benchmark/blob/master/src/tinyformat-test.cc>`_.
|
||||
|
||||
{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on
|
||||
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
|
||||
IEEE754 ``float`` and ``double`` formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
|
||||
and faster than `double-conversion <https://github.com/google/double-conversion>`_ and
|
||||
`ryu <https://github.com/ulfjack/ryu>`_:
|
||||
|
||||
@@ -322,8 +323,10 @@ Projects using this library
|
||||
|
||||
* `ccache <https://ccache.dev/>`_: a compiler cache
|
||||
|
||||
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database
|
||||
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: an analytical database
|
||||
management system
|
||||
|
||||
* `Contour <https://github.com/contour-terminal/contour/>`_: a modern terminal emulator
|
||||
|
||||
* `CUAUV <https://cuauv.org/>`_: Cornell University's autonomous underwater
|
||||
vehicle
|
||||
@@ -341,9 +344,12 @@ Projects using this library
|
||||
|
||||
* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library
|
||||
|
||||
* `GemRB <https://gemrb.org/>`_: a portable open-source implementation of
|
||||
Bioware’s Infinity Engine
|
||||
|
||||
* `Grand Mountain Adventure
|
||||
<https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/>`_:
|
||||
A beautiful open-world ski & snowboarding game
|
||||
a beautiful open-world ski & snowboarding game
|
||||
|
||||
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
||||
Player vs Player Gaming Network with tweaks
|
||||
@@ -357,6 +363,10 @@ Projects using this library
|
||||
|
||||
* `Knuth <https://kth.cash/>`_: high-performance Bitcoin full-node
|
||||
|
||||
* `libunicode <https://github.com/contour-terminal/libunicode/>`_: a modern C++17 Unicode library
|
||||
|
||||
* `MariaDB <https://mariadb.org/>`_: relational database management system
|
||||
|
||||
* `Microsoft Verona <https://github.com/microsoft/verona>`_:
|
||||
research programming language for concurrent ownership
|
||||
|
||||
@@ -410,6 +420,9 @@ Projects using this library
|
||||
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: open-source
|
||||
MMORPG framework
|
||||
|
||||
* `🐙 userver framework <https://userver.tech/>`_: open-source asynchronous
|
||||
framework with a rich set of abstractions and database drivers
|
||||
|
||||
* `Windows Terminal <https://github.com/microsoft/terminal>`_: the new Windows
|
||||
terminal
|
||||
|
||||
@@ -520,8 +533,7 @@ Maintainers
|
||||
-----------
|
||||
|
||||
The {fmt} library is maintained by Victor Zverovich (`vitaut
|
||||
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan
|
||||
<https://github.com/foonathan>`_) with contributions from many other people.
|
||||
<https://github.com/vitaut>`_) with contributions from many other people.
|
||||
See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and
|
||||
`Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names.
|
||||
Let us know if your contribution is not listed or mentioned incorrectly and
|
||||
|
||||
2013
extern/fmtlib/include/fmt/core.h
vendored
2013
extern/fmtlib/include/fmt/core.h
vendored
@@ -10,14 +10,14 @@
|
||||
|
||||
#include <cstddef> // std::byte
|
||||
#include <cstdio> // std::FILE
|
||||
#include <cstring>
|
||||
#include <cstring> // std::strlen
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
||||
#define FMT_VERSION 80101
|
||||
#define FMT_VERSION 100000
|
||||
|
||||
#if defined(__clang__) && !defined(__ibmxl__)
|
||||
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
|
||||
@@ -49,29 +49,27 @@
|
||||
# define FMT_ICC_VERSION 0
|
||||
#endif
|
||||
|
||||
#ifdef __NVCC__
|
||||
# define FMT_NVCC __NVCC__
|
||||
#else
|
||||
# define FMT_NVCC 0
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define FMT_MSC_VER _MSC_VER
|
||||
# define FMT_MSC_VERSION _MSC_VER
|
||||
# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__))
|
||||
#else
|
||||
# define FMT_MSC_VER 0
|
||||
# define FMT_MSC_VERSION 0
|
||||
# define FMT_MSC_WARNING(...)
|
||||
#endif
|
||||
|
||||
#ifdef _MSVC_LANG
|
||||
# define FMT_CPLUSPLUS _MSVC_LANG
|
||||
#else
|
||||
# define FMT_CPLUSPLUS __cplusplus
|
||||
#endif
|
||||
|
||||
#ifdef __has_feature
|
||||
# define FMT_HAS_FEATURE(x) __has_feature(x)
|
||||
#else
|
||||
# define FMT_HAS_FEATURE(x) 0
|
||||
#endif
|
||||
|
||||
#if defined(__has_include) && \
|
||||
(!defined(__INTELLISENSE__) || FMT_MSC_VER > 1900) && \
|
||||
(!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600)
|
||||
#if defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900
|
||||
# define FMT_HAS_INCLUDE(x) __has_include(x)
|
||||
#else
|
||||
# define FMT_HAS_INCLUDE(x) 0
|
||||
@@ -83,12 +81,6 @@
|
||||
# define FMT_HAS_CPP_ATTRIBUTE(x) 0
|
||||
#endif
|
||||
|
||||
#ifdef _MSVC_LANG
|
||||
# define FMT_CPLUSPLUS _MSVC_LANG
|
||||
#else
|
||||
# define FMT_CPLUSPLUS __cplusplus
|
||||
#endif
|
||||
|
||||
#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
|
||||
(FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
|
||||
|
||||
@@ -98,37 +90,38 @@
|
||||
// Check if relaxed C++14 constexpr is supported.
|
||||
// GCC doesn't allow throw in constexpr until version 6 (bug 67371).
|
||||
#ifndef FMT_USE_CONSTEXPR
|
||||
# define FMT_USE_CONSTEXPR \
|
||||
(FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1912 || \
|
||||
(FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \
|
||||
!FMT_NVCC && !FMT_ICC_VERSION
|
||||
# if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \
|
||||
(FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \
|
||||
!FMT_ICC_VERSION && !defined(__NVCC__)
|
||||
# define FMT_USE_CONSTEXPR 1
|
||||
# else
|
||||
# define FMT_USE_CONSTEXPR 0
|
||||
# endif
|
||||
#endif
|
||||
#if FMT_USE_CONSTEXPR
|
||||
# define FMT_CONSTEXPR constexpr
|
||||
# define FMT_CONSTEXPR_DECL constexpr
|
||||
#else
|
||||
# define FMT_CONSTEXPR
|
||||
# define FMT_CONSTEXPR_DECL
|
||||
#endif
|
||||
|
||||
#if ((__cplusplus >= 202002L) && \
|
||||
#if ((FMT_CPLUSPLUS >= 202002L) && \
|
||||
(!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \
|
||||
(__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002)
|
||||
(FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
|
||||
# define FMT_CONSTEXPR20 constexpr
|
||||
#else
|
||||
# define FMT_CONSTEXPR20
|
||||
#endif
|
||||
|
||||
// Check if constexpr std::char_traits<>::compare,length is supported.
|
||||
// Check if constexpr std::char_traits<>::{compare,length} are supported.
|
||||
#if defined(__GLIBCXX__)
|
||||
# if __cplusplus >= 201703L && defined(_GLIBCXX_RELEASE) && \
|
||||
# if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \
|
||||
_GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE.
|
||||
# define FMT_CONSTEXPR_CHAR_TRAITS constexpr
|
||||
# endif
|
||||
#elif defined(_LIBCPP_VERSION) && __cplusplus >= 201703L && \
|
||||
#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \
|
||||
_LIBCPP_VERSION >= 4000
|
||||
# define FMT_CONSTEXPR_CHAR_TRAITS constexpr
|
||||
#elif FMT_MSC_VER >= 1914 && _MSVC_LANG >= 201703L
|
||||
#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L
|
||||
# define FMT_CONSTEXPR_CHAR_TRAITS constexpr
|
||||
#endif
|
||||
#ifndef FMT_CONSTEXPR_CHAR_TRAITS
|
||||
@@ -138,61 +131,21 @@
|
||||
// Check if exceptions are disabled.
|
||||
#ifndef FMT_EXCEPTIONS
|
||||
# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
|
||||
FMT_MSC_VER && !_HAS_EXCEPTIONS
|
||||
(FMT_MSC_VERSION && !_HAS_EXCEPTIONS)
|
||||
# define FMT_EXCEPTIONS 0
|
||||
# else
|
||||
# define FMT_EXCEPTIONS 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature).
|
||||
#ifndef FMT_USE_NOEXCEPT
|
||||
# define FMT_USE_NOEXCEPT 0
|
||||
#endif
|
||||
|
||||
#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
|
||||
FMT_GCC_VERSION >= 408 || FMT_MSC_VER >= 1900
|
||||
# define FMT_DETECTED_NOEXCEPT noexcept
|
||||
# define FMT_HAS_CXX11_NOEXCEPT 1
|
||||
#else
|
||||
# define FMT_DETECTED_NOEXCEPT throw()
|
||||
# define FMT_HAS_CXX11_NOEXCEPT 0
|
||||
#endif
|
||||
|
||||
#ifndef FMT_NOEXCEPT
|
||||
# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT
|
||||
# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT
|
||||
# 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 && \
|
||||
!FMT_NVCC
|
||||
// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings.
|
||||
#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \
|
||||
!defined(__NVCC__)
|
||||
# define FMT_NORETURN [[noreturn]]
|
||||
#else
|
||||
# define FMT_NORETURN
|
||||
#endif
|
||||
|
||||
#if __cplusplus == 201103L || __cplusplus == 201402L
|
||||
# if defined(__INTEL_COMPILER) || defined(__PGI)
|
||||
# define FMT_FALLTHROUGH
|
||||
# elif defined(__clang__)
|
||||
# define FMT_FALLTHROUGH [[clang::fallthrough]]
|
||||
# elif FMT_GCC_VERSION >= 700 && \
|
||||
(!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
|
||||
# define FMT_FALLTHROUGH [[gnu::fallthrough]]
|
||||
# else
|
||||
# define FMT_FALLTHROUGH
|
||||
# endif
|
||||
#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
|
||||
# define FMT_FALLTHROUGH [[fallthrough]]
|
||||
#else
|
||||
# define FMT_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
#ifndef FMT_NODISCARD
|
||||
# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
|
||||
# define FMT_NODISCARD [[nodiscard]]
|
||||
@@ -201,16 +154,6 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef FMT_USE_FLOAT
|
||||
# define FMT_USE_FLOAT 1
|
||||
#endif
|
||||
#ifndef FMT_USE_DOUBLE
|
||||
# define FMT_USE_DOUBLE 1
|
||||
#endif
|
||||
#ifndef FMT_USE_LONG_DOUBLE
|
||||
# define FMT_USE_LONG_DOUBLE 1
|
||||
#endif
|
||||
|
||||
#ifndef FMT_INLINE
|
||||
# if FMT_GCC_VERSION || FMT_CLANG_VERSION
|
||||
# define FMT_INLINE inline __attribute__((always_inline))
|
||||
@@ -219,24 +162,20 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef FMT_DEPRECATED
|
||||
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
|
||||
# define FMT_DEPRECATED [[deprecated]]
|
||||
# else
|
||||
# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
|
||||
# define FMT_DEPRECATED __attribute__((deprecated))
|
||||
# elif FMT_MSC_VER
|
||||
# define FMT_DEPRECATED __declspec(deprecated)
|
||||
# else
|
||||
# define FMT_DEPRECATED /* deprecated */
|
||||
# endif
|
||||
# endif
|
||||
// An inline std::forward replacement.
|
||||
#define FMT_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define FMT_UNCHECKED_ITERATOR(It) \
|
||||
using _Unchecked_type = It // Mark iterator as checked.
|
||||
#else
|
||||
# define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It
|
||||
#endif
|
||||
|
||||
#ifndef FMT_BEGIN_NAMESPACE
|
||||
# define FMT_BEGIN_NAMESPACE \
|
||||
namespace fmt { \
|
||||
inline namespace v8 {
|
||||
inline namespace v10 {
|
||||
# define FMT_END_NAMESPACE \
|
||||
} \
|
||||
}
|
||||
@@ -244,22 +183,18 @@
|
||||
|
||||
#ifndef FMT_MODULE_EXPORT
|
||||
# define FMT_MODULE_EXPORT
|
||||
# define FMT_MODULE_EXPORT_BEGIN
|
||||
# define FMT_MODULE_EXPORT_END
|
||||
# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
|
||||
# define FMT_END_DETAIL_NAMESPACE }
|
||||
# define FMT_BEGIN_EXPORT
|
||||
# define FMT_END_EXPORT
|
||||
#endif
|
||||
|
||||
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
|
||||
# define FMT_CLASS_API FMT_MSC_WARNING(suppress : 4275)
|
||||
# ifdef FMT_EXPORT
|
||||
# ifdef FMT_LIB_EXPORT
|
||||
# define FMT_API __declspec(dllexport)
|
||||
# elif defined(FMT_SHARED)
|
||||
# define FMT_API __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define FMT_CLASS_API
|
||||
# if defined(FMT_EXPORT) || defined(FMT_SHARED)
|
||||
# if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED)
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# define FMT_API __attribute__((visibility("default")))
|
||||
# endif
|
||||
@@ -270,26 +205,27 @@
|
||||
#endif
|
||||
|
||||
// libc++ supports string_view in pre-c++17.
|
||||
#if (FMT_HAS_INCLUDE(<string_view>) && \
|
||||
(__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
|
||||
(defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
|
||||
#if FMT_HAS_INCLUDE(<string_view>) && \
|
||||
(FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION))
|
||||
# include <string_view>
|
||||
# define FMT_USE_STRING_VIEW
|
||||
#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L
|
||||
#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L
|
||||
# include <experimental/string_view>
|
||||
# define FMT_USE_EXPERIMENTAL_STRING_VIEW
|
||||
#endif
|
||||
|
||||
#ifndef FMT_UNICODE
|
||||
# define FMT_UNICODE !FMT_MSC_VER
|
||||
# define FMT_UNICODE !FMT_MSC_VERSION
|
||||
#endif
|
||||
|
||||
#ifndef FMT_CONSTEVAL
|
||||
# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \
|
||||
__cplusplus > 201703L && !defined(__apple_build_version__)) || \
|
||||
(defined(__cpp_consteval) && \
|
||||
(!FMT_MSC_VER || _MSC_FULL_VER >= 193030704))
|
||||
// consteval is broken in MSVC before VS2022 and Apple clang 13.
|
||||
# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \
|
||||
(!defined(__apple_build_version__) || \
|
||||
__apple_build_version__ >= 14000029L) && \
|
||||
FMT_CPLUSPLUS >= 202002L) || \
|
||||
(defined(__cpp_consteval) && \
|
||||
(!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704))
|
||||
// consteval is broken in MSVC before VS2022 and Apple clang before 14.
|
||||
# define FMT_CONSTEVAL consteval
|
||||
# define FMT_HAS_CONSTEVAL
|
||||
# else
|
||||
@@ -297,24 +233,31 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
# if defined(__cpp_nontype_template_args) && \
|
||||
((FMT_GCC_VERSION >= 903 && __cplusplus >= 201709L) || \
|
||||
__cpp_nontype_template_args >= 201911L)
|
||||
# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1
|
||||
#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
# if defined(__cpp_nontype_template_args) && \
|
||||
((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \
|
||||
__cpp_nontype_template_args >= 201911L) && \
|
||||
!defined(__NVCOMPILER) && !defined(__LCC__)
|
||||
# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
|
||||
# else
|
||||
# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0
|
||||
# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L
|
||||
# define FMT_INLINE_VARIABLE inline
|
||||
#else
|
||||
# define FMT_INLINE_VARIABLE
|
||||
#endif
|
||||
|
||||
// Enable minimal optimizations for more compact code in debug mode.
|
||||
FMT_GCC_PRAGMA("GCC push_options")
|
||||
#ifndef __OPTIMIZE__
|
||||
#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \
|
||||
!defined(__CUDACC__)
|
||||
FMT_GCC_PRAGMA("GCC optimize(\"Og\")")
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
// Implementations of enable_if_t and other metafunctions for older systems.
|
||||
template <bool B, typename T = void>
|
||||
@@ -330,6 +273,8 @@ template <typename T>
|
||||
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
|
||||
template <typename T> struct type_identity { using type = T; };
|
||||
template <typename T> using type_identity_t = typename type_identity<T>::type;
|
||||
template <typename T>
|
||||
using underlying_t = typename std::underlying_type<T>::type;
|
||||
|
||||
struct monostate {
|
||||
constexpr monostate() {}
|
||||
@@ -341,19 +286,32 @@ struct monostate {
|
||||
#ifdef FMT_DOC
|
||||
# define FMT_ENABLE_IF(...)
|
||||
#else
|
||||
# define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0
|
||||
# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
#ifdef __cpp_lib_byte
|
||||
inline auto format_as(std::byte b) -> unsigned char {
|
||||
return static_cast<unsigned char>(b);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Suppress "unused variable" warnings with the method described in
|
||||
namespace detail {
|
||||
// Suppresses "unused variable" warnings with the method described in
|
||||
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
|
||||
// (void)var does not work on many Intel compilers.
|
||||
template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
|
||||
|
||||
constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false)
|
||||
FMT_NOEXCEPT -> bool {
|
||||
#ifdef __cpp_lib_is_constant_evaluated
|
||||
constexpr FMT_INLINE auto is_constant_evaluated(
|
||||
bool default_value = false) noexcept -> bool {
|
||||
// Workaround for incompatibility between libstdc++ consteval-based
|
||||
// std::is_constant_evaluated() implementation and clang-14.
|
||||
// https://github.com/fmtlib/fmt/issues/3247
|
||||
#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \
|
||||
_GLIBCXX_RELEASE >= 12 && \
|
||||
(FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500)
|
||||
ignore_unused(default_value);
|
||||
return __builtin_is_constant_evaluated();
|
||||
#elif defined(__cpp_lib_is_constant_evaluated)
|
||||
ignore_unused(default_value);
|
||||
return std::is_constant_evaluated();
|
||||
#else
|
||||
@@ -361,7 +319,7 @@ constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false)
|
||||
#endif
|
||||
}
|
||||
|
||||
// A function to suppress "conditional expression is constant" warnings.
|
||||
// Suppresses "conditional expression is constant" warnings.
|
||||
template <typename T> constexpr FMT_INLINE auto const_check(T value) -> T {
|
||||
return value;
|
||||
}
|
||||
@@ -371,23 +329,17 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
|
||||
|
||||
#ifndef FMT_ASSERT
|
||||
# ifdef NDEBUG
|
||||
// FMT_ASSERT is not empty to avoid -Werror=empty-body.
|
||||
// FMT_ASSERT is not empty to avoid -Wempty-body.
|
||||
# define FMT_ASSERT(condition, message) \
|
||||
::fmt::detail::ignore_unused((condition), (message))
|
||||
fmt::detail::ignore_unused((condition), (message))
|
||||
# else
|
||||
# define FMT_ASSERT(condition, message) \
|
||||
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
|
||||
? (void)0 \
|
||||
: ::fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
|
||||
: fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __cpp_lib_byte
|
||||
using byte = std::byte;
|
||||
#else
|
||||
enum class byte : unsigned char {};
|
||||
#endif
|
||||
|
||||
#if defined(FMT_USE_STRING_VIEW)
|
||||
template <typename Char> using std_string_view = std::basic_string_view<Char>;
|
||||
#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
|
||||
@@ -399,11 +351,11 @@ template <typename T> struct std_string_view {};
|
||||
|
||||
#ifdef FMT_USE_INT128
|
||||
// Do nothing.
|
||||
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \
|
||||
!(FMT_CLANG_VERSION && FMT_MSC_VER)
|
||||
#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \
|
||||
!(FMT_CLANG_VERSION && FMT_MSC_VERSION)
|
||||
# define FMT_USE_INT128 1
|
||||
using int128_t = __int128_t;
|
||||
using uint128_t = __uint128_t;
|
||||
using int128_opt = __int128_t; // An optional native 128-bit integer.
|
||||
using uint128_opt = __uint128_t;
|
||||
template <typename T> inline auto convert_for_visit(T value) -> T {
|
||||
return value;
|
||||
}
|
||||
@@ -411,32 +363,29 @@ template <typename T> inline auto convert_for_visit(T value) -> T {
|
||||
# define FMT_USE_INT128 0
|
||||
#endif
|
||||
#if !FMT_USE_INT128
|
||||
enum class int128_t {};
|
||||
enum class uint128_t {};
|
||||
enum class int128_opt {};
|
||||
enum class uint128_opt {};
|
||||
// Reduce template instantiations.
|
||||
template <typename T> inline auto convert_for_visit(T) -> monostate {
|
||||
return {};
|
||||
}
|
||||
template <typename T> auto convert_for_visit(T) -> monostate { return {}; }
|
||||
#endif
|
||||
|
||||
// Casts a nonnegative integer to unsigned.
|
||||
template <typename Int>
|
||||
FMT_CONSTEXPR auto to_unsigned(Int value) ->
|
||||
typename std::make_unsigned<Int>::type {
|
||||
FMT_ASSERT(value >= 0, "negative value");
|
||||
FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value");
|
||||
return static_cast<typename std::make_unsigned<Int>::type>(value);
|
||||
}
|
||||
|
||||
FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5";
|
||||
FMT_CONSTEXPR inline auto is_utf8() -> bool {
|
||||
FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7";
|
||||
|
||||
constexpr auto is_utf8() -> bool {
|
||||
// Avoid buggy sign extensions in MSVC's constant evaluation mode.
|
||||
// https://developercommunity.visualstudio.com/t/C-difference-in-behavior-for-unsigned/1233612
|
||||
// Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297).
|
||||
using uchar = unsigned char;
|
||||
return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 &&
|
||||
uchar(micro[1]) == 0xB5);
|
||||
return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 &&
|
||||
uchar(section[1]) == 0xA7);
|
||||
}
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
|
||||
@@ -445,6 +394,7 @@ FMT_END_DETAIL_NAMESPACE
|
||||
compiled with a different ``-std`` option than the client code (which is not
|
||||
recommended).
|
||||
*/
|
||||
FMT_MODULE_EXPORT
|
||||
template <typename Char> class basic_string_view {
|
||||
private:
|
||||
const Char* data_;
|
||||
@@ -454,12 +404,11 @@ template <typename Char> class basic_string_view {
|
||||
using value_type = Char;
|
||||
using iterator = const Char*;
|
||||
|
||||
constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
|
||||
constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
|
||||
|
||||
/** Constructs a string reference object from a C string and a size. */
|
||||
constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT
|
||||
: data_(s),
|
||||
size_(count) {}
|
||||
constexpr basic_string_view(const Char* s, size_t count) noexcept
|
||||
: data_(s), size_(count) {}
|
||||
|
||||
/**
|
||||
\rst
|
||||
@@ -479,33 +428,44 @@ template <typename Char> class basic_string_view {
|
||||
/** Constructs a string reference from a ``std::basic_string`` object. */
|
||||
template <typename Traits, typename Alloc>
|
||||
FMT_CONSTEXPR basic_string_view(
|
||||
const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT
|
||||
: data_(s.data()),
|
||||
size_(s.size()) {}
|
||||
const std::basic_string<Char, Traits, Alloc>& s) noexcept
|
||||
: data_(s.data()), size_(s.size()) {}
|
||||
|
||||
template <typename S, FMT_ENABLE_IF(std::is_same<
|
||||
S, detail::std_string_view<Char>>::value)>
|
||||
FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()),
|
||||
size_(s.size()) {}
|
||||
FMT_CONSTEXPR basic_string_view(S s) noexcept
|
||||
: data_(s.data()), size_(s.size()) {}
|
||||
|
||||
/** Returns a pointer to the string data. */
|
||||
constexpr auto data() const FMT_NOEXCEPT -> const Char* { return data_; }
|
||||
constexpr auto data() const noexcept -> const Char* { return data_; }
|
||||
|
||||
/** Returns the string size. */
|
||||
constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; }
|
||||
constexpr auto size() const noexcept -> size_t { return size_; }
|
||||
|
||||
constexpr auto begin() const FMT_NOEXCEPT -> iterator { return data_; }
|
||||
constexpr auto end() const FMT_NOEXCEPT -> iterator { return data_ + size_; }
|
||||
constexpr auto begin() const noexcept -> iterator { return data_; }
|
||||
constexpr auto end() const noexcept -> iterator { return data_ + size_; }
|
||||
|
||||
constexpr auto operator[](size_t pos) const FMT_NOEXCEPT -> const Char& {
|
||||
constexpr auto operator[](size_t pos) const noexcept -> const Char& {
|
||||
return data_[pos];
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void remove_prefix(size_t n) FMT_NOEXCEPT {
|
||||
FMT_CONSTEXPR void remove_prefix(size_t n) noexcept {
|
||||
data_ += n;
|
||||
size_ -= n;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(
|
||||
basic_string_view<Char> sv) const noexcept {
|
||||
return size_ >= sv.size_ &&
|
||||
std::char_traits<Char>::compare(data_, sv.data_, sv.size_) == 0;
|
||||
}
|
||||
FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept {
|
||||
return size_ >= 1 && std::char_traits<Char>::eq(*data_, c);
|
||||
}
|
||||
FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const {
|
||||
return starts_with(basic_string_view<Char>(s));
|
||||
}
|
||||
|
||||
// Lexicographically compare this string reference to other.
|
||||
FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int {
|
||||
size_t str_size = size_ < other.size_ ? size_ : other.size_;
|
||||
@@ -537,13 +497,22 @@ template <typename Char> class basic_string_view {
|
||||
}
|
||||
};
|
||||
|
||||
FMT_MODULE_EXPORT
|
||||
using string_view = basic_string_view<char>;
|
||||
|
||||
/** Specifies if ``T`` is a character type. Can be specialized by users. */
|
||||
FMT_MODULE_EXPORT
|
||||
template <typename T> struct is_char : std::false_type {};
|
||||
template <> struct is_char<char> : std::true_type {};
|
||||
|
||||
// Returns a string view of `s`.
|
||||
namespace detail {
|
||||
|
||||
// A base class for compile-time strings.
|
||||
struct compile_string {};
|
||||
|
||||
template <typename S>
|
||||
struct is_compile_string : std::is_base_of<compile_string, S> {};
|
||||
|
||||
template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
|
||||
FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view<Char> {
|
||||
return s;
|
||||
@@ -559,36 +528,24 @@ constexpr auto to_string_view(basic_string_view<Char> s)
|
||||
return s;
|
||||
}
|
||||
template <typename Char,
|
||||
FMT_ENABLE_IF(!std::is_empty<detail::std_string_view<Char>>::value)>
|
||||
inline auto to_string_view(detail::std_string_view<Char> s)
|
||||
-> basic_string_view<Char> {
|
||||
FMT_ENABLE_IF(!std::is_empty<std_string_view<Char>>::value)>
|
||||
inline auto to_string_view(std_string_view<Char> s) -> basic_string_view<Char> {
|
||||
return s;
|
||||
}
|
||||
|
||||
// A base class for compile-time strings. It is defined in the fmt namespace to
|
||||
// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42).
|
||||
struct compile_string {};
|
||||
|
||||
template <typename S>
|
||||
struct is_compile_string : std::is_base_of<compile_string, S> {};
|
||||
|
||||
template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||
constexpr auto to_string_view(const S& s)
|
||||
-> basic_string_view<typename S::char_type> {
|
||||
return basic_string_view<typename S::char_type>(s);
|
||||
}
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
void to_string_view(...);
|
||||
using fmt::to_string_view;
|
||||
|
||||
// Specifies whether S is a string type convertible to fmt::basic_string_view.
|
||||
// It should be a constexpr function but MSVC 2017 fails to compile it in
|
||||
// enable_if and MSVC 2015 fails to compile it as an alias template.
|
||||
// ADL is intentionally disabled as to_string_view is not an extension point.
|
||||
template <typename S>
|
||||
struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))> {
|
||||
};
|
||||
struct is_string
|
||||
: std::is_class<decltype(detail::to_string_view(std::declval<S>()))> {};
|
||||
|
||||
template <typename S, typename = void> struct char_t_impl {};
|
||||
template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
|
||||
@@ -596,28 +553,91 @@ template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
|
||||
using type = typename result::value_type;
|
||||
};
|
||||
|
||||
// Reports a compile-time error if S is not a valid format string.
|
||||
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
|
||||
FMT_INLINE void check_format_string(const S&) {
|
||||
#ifdef FMT_ENFORCE_COMPILE_STRING
|
||||
static_assert(is_compile_string<S>::value,
|
||||
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
|
||||
"FMT_STRING.");
|
||||
#endif
|
||||
enum class type {
|
||||
none_type,
|
||||
// Integer types should go first,
|
||||
int_type,
|
||||
uint_type,
|
||||
long_long_type,
|
||||
ulong_long_type,
|
||||
int128_type,
|
||||
uint128_type,
|
||||
bool_type,
|
||||
char_type,
|
||||
last_integer_type = char_type,
|
||||
// followed by floating-point types.
|
||||
float_type,
|
||||
double_type,
|
||||
long_double_type,
|
||||
last_numeric_type = long_double_type,
|
||||
cstring_type,
|
||||
string_type,
|
||||
pointer_type,
|
||||
custom_type
|
||||
};
|
||||
|
||||
// Maps core type T to the corresponding type enum constant.
|
||||
template <typename T, typename Char>
|
||||
struct type_constant : std::integral_constant<type, type::custom_type> {};
|
||||
|
||||
#define FMT_TYPE_CONSTANT(Type, constant) \
|
||||
template <typename Char> \
|
||||
struct type_constant<Type, Char> \
|
||||
: std::integral_constant<type, type::constant> {}
|
||||
|
||||
FMT_TYPE_CONSTANT(int, int_type);
|
||||
FMT_TYPE_CONSTANT(unsigned, uint_type);
|
||||
FMT_TYPE_CONSTANT(long long, long_long_type);
|
||||
FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
|
||||
FMT_TYPE_CONSTANT(int128_opt, int128_type);
|
||||
FMT_TYPE_CONSTANT(uint128_opt, uint128_type);
|
||||
FMT_TYPE_CONSTANT(bool, bool_type);
|
||||
FMT_TYPE_CONSTANT(Char, char_type);
|
||||
FMT_TYPE_CONSTANT(float, float_type);
|
||||
FMT_TYPE_CONSTANT(double, double_type);
|
||||
FMT_TYPE_CONSTANT(long double, long_double_type);
|
||||
FMT_TYPE_CONSTANT(const Char*, cstring_type);
|
||||
FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
|
||||
FMT_TYPE_CONSTANT(const void*, pointer_type);
|
||||
|
||||
constexpr bool is_integral_type(type t) {
|
||||
return t > type::none_type && t <= type::last_integer_type;
|
||||
}
|
||||
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||
void check_format_string(S);
|
||||
constexpr bool is_arithmetic_type(type t) {
|
||||
return t > type::none_type && t <= type::last_numeric_type;
|
||||
}
|
||||
|
||||
constexpr auto set(type rhs) -> int { return 1 << static_cast<int>(rhs); }
|
||||
constexpr auto in(type t, int set) -> bool {
|
||||
return ((set >> static_cast<int>(t)) & 1) != 0;
|
||||
}
|
||||
|
||||
// Bitsets of types.
|
||||
enum {
|
||||
sint_set =
|
||||
set(type::int_type) | set(type::long_long_type) | set(type::int128_type),
|
||||
uint_set = set(type::uint_type) | set(type::ulong_long_type) |
|
||||
set(type::uint128_type),
|
||||
bool_set = set(type::bool_type),
|
||||
char_set = set(type::char_type),
|
||||
float_set = set(type::float_type) | set(type::double_type) |
|
||||
set(type::long_double_type),
|
||||
string_set = set(type::string_type),
|
||||
cstring_set = set(type::cstring_type),
|
||||
pointer_set = set(type::pointer_type)
|
||||
};
|
||||
|
||||
FMT_NORETURN FMT_API void throw_format_error(const char* message);
|
||||
|
||||
struct error_handler {
|
||||
constexpr error_handler() = default;
|
||||
constexpr error_handler(const error_handler&) = default;
|
||||
|
||||
// This function is intentionally not constexpr to give a compile-time error.
|
||||
FMT_NORETURN FMT_API void on_error(const char* message);
|
||||
FMT_NORETURN void on_error(const char* message) {
|
||||
throw_format_error(message);
|
||||
}
|
||||
};
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
} // namespace detail
|
||||
|
||||
/** String's character type. */
|
||||
template <typename S> using char_t = typename detail::char_t_impl<S>::type;
|
||||
@@ -629,35 +649,34 @@ template <typename S> using char_t = typename detail::char_t_impl<S>::type;
|
||||
You can use the ``format_parse_context`` type alias for ``char`` instead.
|
||||
\endrst
|
||||
*/
|
||||
template <typename Char, typename ErrorHandler = detail::error_handler>
|
||||
class basic_format_parse_context : private ErrorHandler {
|
||||
FMT_MODULE_EXPORT
|
||||
template <typename Char> class basic_format_parse_context {
|
||||
private:
|
||||
basic_string_view<Char> format_str_;
|
||||
int next_arg_id_;
|
||||
|
||||
FMT_CONSTEXPR void do_check_arg_id(int id);
|
||||
|
||||
public:
|
||||
using char_type = Char;
|
||||
using iterator = typename basic_string_view<Char>::iterator;
|
||||
using iterator = const Char*;
|
||||
|
||||
explicit constexpr basic_format_parse_context(
|
||||
basic_string_view<Char> format_str, ErrorHandler eh = {},
|
||||
int next_arg_id = 0)
|
||||
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {}
|
||||
basic_string_view<Char> format_str, int next_arg_id = 0)
|
||||
: format_str_(format_str), next_arg_id_(next_arg_id) {}
|
||||
|
||||
/**
|
||||
Returns an iterator to the beginning of the format string range being
|
||||
parsed.
|
||||
*/
|
||||
constexpr auto begin() const FMT_NOEXCEPT -> iterator {
|
||||
constexpr auto begin() const noexcept -> iterator {
|
||||
return format_str_.begin();
|
||||
}
|
||||
|
||||
/**
|
||||
Returns an iterator past the end of the format string range being parsed.
|
||||
*/
|
||||
constexpr auto end() const FMT_NOEXCEPT -> iterator {
|
||||
return format_str_.end();
|
||||
}
|
||||
constexpr auto end() const noexcept -> iterator { return format_str_.end(); }
|
||||
|
||||
/** Advances the begin iterator to ``it``. */
|
||||
FMT_CONSTEXPR void advance_to(iterator it) {
|
||||
@@ -669,40 +688,104 @@ class basic_format_parse_context : private ErrorHandler {
|
||||
the next argument index and switches to the automatic indexing.
|
||||
*/
|
||||
FMT_CONSTEXPR auto next_arg_id() -> int {
|
||||
// Don't check if the argument id is valid to avoid overhead and because it
|
||||
// will be checked during formatting anyway.
|
||||
if (next_arg_id_ >= 0) return next_arg_id_++;
|
||||
on_error("cannot switch from manual to automatic argument indexing");
|
||||
return 0;
|
||||
if (next_arg_id_ < 0) {
|
||||
detail::throw_format_error(
|
||||
"cannot switch from manual to automatic argument indexing");
|
||||
return 0;
|
||||
}
|
||||
int id = next_arg_id_++;
|
||||
do_check_arg_id(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
Reports an error if using the automatic argument indexing; otherwise
|
||||
switches to the manual indexing.
|
||||
*/
|
||||
FMT_CONSTEXPR void check_arg_id(int) {
|
||||
if (next_arg_id_ > 0)
|
||||
on_error("cannot switch from automatic to manual argument indexing");
|
||||
else
|
||||
next_arg_id_ = -1;
|
||||
FMT_CONSTEXPR void check_arg_id(int id) {
|
||||
if (next_arg_id_ > 0) {
|
||||
detail::throw_format_error(
|
||||
"cannot switch from automatic to manual argument indexing");
|
||||
return;
|
||||
}
|
||||
next_arg_id_ = -1;
|
||||
do_check_arg_id(id);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
|
||||
|
||||
FMT_CONSTEXPR void on_error(const char* message) {
|
||||
ErrorHandler::on_error(message);
|
||||
}
|
||||
|
||||
constexpr auto error_handler() const -> ErrorHandler { return *this; }
|
||||
FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
|
||||
};
|
||||
|
||||
FMT_MODULE_EXPORT
|
||||
using format_parse_context = basic_format_parse_context<char>;
|
||||
|
||||
template <typename Context> class basic_format_arg;
|
||||
template <typename Context> class basic_format_args;
|
||||
template <typename Context> class dynamic_format_arg_store;
|
||||
namespace detail {
|
||||
// A parse context with extra data used only in compile-time checks.
|
||||
template <typename Char>
|
||||
class compile_parse_context : public basic_format_parse_context<Char> {
|
||||
private:
|
||||
int num_args_;
|
||||
const type* types_;
|
||||
using base = basic_format_parse_context<Char>;
|
||||
|
||||
public:
|
||||
explicit FMT_CONSTEXPR compile_parse_context(
|
||||
basic_string_view<Char> format_str, int num_args, const type* types,
|
||||
int next_arg_id = 0)
|
||||
: base(format_str, next_arg_id), num_args_(num_args), types_(types) {}
|
||||
|
||||
constexpr auto num_args() const -> int { return num_args_; }
|
||||
constexpr auto arg_type(int id) const -> type { return types_[id]; }
|
||||
|
||||
FMT_CONSTEXPR auto next_arg_id() -> int {
|
||||
int id = base::next_arg_id();
|
||||
if (id >= num_args_) throw_format_error("argument not found");
|
||||
return id;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void check_arg_id(int id) {
|
||||
base::check_arg_id(id);
|
||||
if (id >= num_args_) throw_format_error("argument not found");
|
||||
}
|
||||
using base::check_arg_id;
|
||||
|
||||
FMT_CONSTEXPR void check_dynamic_spec(int arg_id) {
|
||||
detail::ignore_unused(arg_id);
|
||||
#if !defined(__LCC__)
|
||||
if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id]))
|
||||
throw_format_error("width/precision is not integer");
|
||||
#endif
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR void basic_format_parse_context<Char>::do_check_arg_id(int id) {
|
||||
// Argument id is only checked at compile-time during parsing because
|
||||
// formatting has its own validation.
|
||||
if (detail::is_constant_evaluated() &&
|
||||
(!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
|
||||
using context = detail::compile_parse_context<Char>;
|
||||
if (id >= static_cast<context*>(this)->num_args())
|
||||
detail::throw_format_error("argument not found");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR void basic_format_parse_context<Char>::check_dynamic_spec(
|
||||
int arg_id) {
|
||||
if (detail::is_constant_evaluated() &&
|
||||
(!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
|
||||
using context = detail::compile_parse_context<Char>;
|
||||
static_cast<context*>(this)->check_dynamic_spec(arg_id);
|
||||
}
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT template <typename Context> class basic_format_arg;
|
||||
FMT_MODULE_EXPORT template <typename Context> class basic_format_args;
|
||||
FMT_MODULE_EXPORT template <typename Context> class dynamic_format_arg_store;
|
||||
|
||||
// A formatter for objects of type T.
|
||||
FMT_MODULE_EXPORT
|
||||
template <typename T, typename Char = char, typename Enable = void>
|
||||
struct formatter {
|
||||
// A deleted default constructor indicates a disabled formatter.
|
||||
@@ -722,7 +805,7 @@ struct is_contiguous<std::basic_string<Char>> : std::true_type {};
|
||||
|
||||
class appender;
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
namespace detail {
|
||||
|
||||
template <typename Context, typename T>
|
||||
constexpr auto has_const_formatter_impl(T*)
|
||||
@@ -744,10 +827,10 @@ constexpr auto has_const_formatter() -> bool {
|
||||
template <typename Container>
|
||||
inline auto get_container(std::back_insert_iterator<Container> it)
|
||||
-> Container& {
|
||||
using bi_iterator = std::back_insert_iterator<Container>;
|
||||
struct accessor : bi_iterator {
|
||||
accessor(bi_iterator iter) : bi_iterator(iter) {}
|
||||
using bi_iterator::container;
|
||||
using base = std::back_insert_iterator<Container>;
|
||||
struct accessor : base {
|
||||
accessor(base b) : base(b) {}
|
||||
using base::container;
|
||||
};
|
||||
return *accessor(it).container;
|
||||
}
|
||||
@@ -765,7 +848,7 @@ template <typename Char, typename T, typename U,
|
||||
FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* {
|
||||
if (is_constant_evaluated()) return copy_str<Char, T*, U*>(begin, end, out);
|
||||
auto size = to_unsigned(end - begin);
|
||||
memcpy(out, begin, size * sizeof(U));
|
||||
if (size > 0) memcpy(out, begin, size * sizeof(U));
|
||||
return out + size;
|
||||
}
|
||||
|
||||
@@ -784,18 +867,16 @@ template <typename T> class buffer {
|
||||
protected:
|
||||
// Don't initialize ptr_ since it is not accessed to save a few cycles.
|
||||
FMT_MSC_WARNING(suppress : 26495)
|
||||
buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {}
|
||||
buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {}
|
||||
|
||||
FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0,
|
||||
size_t cap = 0) FMT_NOEXCEPT : ptr_(p),
|
||||
size_(sz),
|
||||
capacity_(cap) {}
|
||||
FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept
|
||||
: ptr_(p), size_(sz), capacity_(cap) {}
|
||||
|
||||
FMT_CONSTEXPR20 ~buffer() = default;
|
||||
buffer(buffer&&) = default;
|
||||
|
||||
/** Sets the buffer data and capacity. */
|
||||
FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
|
||||
FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept {
|
||||
ptr_ = buf_data;
|
||||
capacity_ = buf_capacity;
|
||||
}
|
||||
@@ -810,23 +891,23 @@ template <typename T> class buffer {
|
||||
buffer(const buffer&) = delete;
|
||||
void operator=(const buffer&) = delete;
|
||||
|
||||
auto begin() FMT_NOEXCEPT -> T* { return ptr_; }
|
||||
auto end() FMT_NOEXCEPT -> T* { return ptr_ + size_; }
|
||||
FMT_INLINE auto begin() noexcept -> T* { return ptr_; }
|
||||
FMT_INLINE auto end() noexcept -> T* { return ptr_ + size_; }
|
||||
|
||||
auto begin() const FMT_NOEXCEPT -> const T* { return ptr_; }
|
||||
auto end() const FMT_NOEXCEPT -> const T* { return ptr_ + size_; }
|
||||
FMT_INLINE auto begin() const noexcept -> const T* { return ptr_; }
|
||||
FMT_INLINE auto end() const noexcept -> const T* { return ptr_ + size_; }
|
||||
|
||||
/** Returns the size of this buffer. */
|
||||
constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; }
|
||||
constexpr auto size() const noexcept -> size_t { return size_; }
|
||||
|
||||
/** Returns the capacity of this buffer. */
|
||||
constexpr auto capacity() const FMT_NOEXCEPT -> size_t { return capacity_; }
|
||||
constexpr auto capacity() const noexcept -> size_t { return capacity_; }
|
||||
|
||||
/** Returns a pointer to the buffer data. */
|
||||
FMT_CONSTEXPR auto data() FMT_NOEXCEPT -> T* { return ptr_; }
|
||||
FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
|
||||
|
||||
/** Returns a pointer to the buffer data. */
|
||||
FMT_CONSTEXPR auto data() const FMT_NOEXCEPT -> const T* { return ptr_; }
|
||||
FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; }
|
||||
|
||||
/** Clears this buffer. */
|
||||
void clear() { size_ = 0; }
|
||||
@@ -854,11 +935,11 @@ template <typename T> class buffer {
|
||||
/** Appends data to the end of the buffer. */
|
||||
template <typename U> void append(const U* begin, const U* end);
|
||||
|
||||
template <typename I> FMT_CONSTEXPR auto operator[](I index) -> T& {
|
||||
template <typename Idx> FMT_CONSTEXPR auto operator[](Idx index) -> T& {
|
||||
return ptr_[index];
|
||||
}
|
||||
template <typename I>
|
||||
FMT_CONSTEXPR auto operator[](I index) const -> const T& {
|
||||
template <typename Idx>
|
||||
FMT_CONSTEXPR auto operator[](Idx index) const -> const T& {
|
||||
return ptr_[index];
|
||||
}
|
||||
};
|
||||
@@ -993,6 +1074,7 @@ class iterator_buffer<std::back_insert_iterator<Container>,
|
||||
: buffer<typename Container::value_type>(c.size()), container_(c) {}
|
||||
explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
|
||||
: iterator_buffer(get_container(out)) {}
|
||||
|
||||
auto out() -> std::back_insert_iterator<Container> {
|
||||
return std::back_inserter(container_);
|
||||
}
|
||||
@@ -1027,25 +1109,21 @@ template <typename T, typename OutputIt>
|
||||
auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> {
|
||||
return iterator_buffer<OutputIt, T>(out);
|
||||
}
|
||||
template <typename T, typename Buf,
|
||||
FMT_ENABLE_IF(std::is_base_of<buffer<char>, Buf>::value)>
|
||||
auto get_buffer(std::back_insert_iterator<Buf> out) -> buffer<char>& {
|
||||
return get_container(out);
|
||||
}
|
||||
|
||||
template <typename Buffer>
|
||||
auto get_iterator(Buffer& buf) -> decltype(buf.out()) {
|
||||
template <typename Buf, typename OutputIt>
|
||||
FMT_INLINE auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) {
|
||||
return buf.out();
|
||||
}
|
||||
template <typename T> auto get_iterator(buffer<T>& buf) -> buffer_appender<T> {
|
||||
return buffer_appender<T>(buf);
|
||||
template <typename T, typename OutputIt>
|
||||
auto get_iterator(buffer<T>&, OutputIt out) -> OutputIt {
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename T, typename Char = char, typename Enable = void>
|
||||
struct fallback_formatter {
|
||||
fallback_formatter() = delete;
|
||||
};
|
||||
|
||||
// Specifies if T has an enabled fallback_formatter specialization.
|
||||
template <typename T, typename Char>
|
||||
using has_fallback_formatter =
|
||||
std::is_constructible<fallback_formatter<T, Char>>;
|
||||
|
||||
struct view {};
|
||||
|
||||
template <typename Char, typename T> struct named_arg : view {
|
||||
@@ -1128,64 +1206,8 @@ constexpr auto count_statically_named_args() -> size_t {
|
||||
return count<is_statically_named_arg<Args>::value...>();
|
||||
}
|
||||
|
||||
enum class type {
|
||||
none_type,
|
||||
// Integer types should go first,
|
||||
int_type,
|
||||
uint_type,
|
||||
long_long_type,
|
||||
ulong_long_type,
|
||||
int128_type,
|
||||
uint128_type,
|
||||
bool_type,
|
||||
char_type,
|
||||
last_integer_type = char_type,
|
||||
// followed by floating-point types.
|
||||
float_type,
|
||||
double_type,
|
||||
long_double_type,
|
||||
last_numeric_type = long_double_type,
|
||||
cstring_type,
|
||||
string_type,
|
||||
pointer_type,
|
||||
custom_type
|
||||
};
|
||||
|
||||
// Maps core type T to the corresponding type enum constant.
|
||||
template <typename T, typename Char>
|
||||
struct type_constant : std::integral_constant<type, type::custom_type> {};
|
||||
|
||||
#define FMT_TYPE_CONSTANT(Type, constant) \
|
||||
template <typename Char> \
|
||||
struct type_constant<Type, Char> \
|
||||
: std::integral_constant<type, type::constant> {}
|
||||
|
||||
FMT_TYPE_CONSTANT(int, int_type);
|
||||
FMT_TYPE_CONSTANT(unsigned, uint_type);
|
||||
FMT_TYPE_CONSTANT(long long, long_long_type);
|
||||
FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
|
||||
FMT_TYPE_CONSTANT(int128_t, int128_type);
|
||||
FMT_TYPE_CONSTANT(uint128_t, uint128_type);
|
||||
FMT_TYPE_CONSTANT(bool, bool_type);
|
||||
FMT_TYPE_CONSTANT(Char, char_type);
|
||||
FMT_TYPE_CONSTANT(float, float_type);
|
||||
FMT_TYPE_CONSTANT(double, double_type);
|
||||
FMT_TYPE_CONSTANT(long double, long_double_type);
|
||||
FMT_TYPE_CONSTANT(const Char*, cstring_type);
|
||||
FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
|
||||
FMT_TYPE_CONSTANT(const void*, pointer_type);
|
||||
|
||||
constexpr bool is_integral_type(type t) {
|
||||
return t > type::none_type && t <= type::last_integer_type;
|
||||
}
|
||||
|
||||
constexpr bool is_arithmetic_type(type t) {
|
||||
return t > type::none_type && t <= type::last_numeric_type;
|
||||
}
|
||||
|
||||
struct unformattable {};
|
||||
struct unformattable_char : unformattable {};
|
||||
struct unformattable_const : unformattable {};
|
||||
struct unformattable_pointer : unformattable {};
|
||||
|
||||
template <typename Char> struct string_value {
|
||||
@@ -1215,8 +1237,8 @@ template <typename Context> class value {
|
||||
unsigned uint_value;
|
||||
long long long_long_value;
|
||||
unsigned long long ulong_long_value;
|
||||
int128_t int128_value;
|
||||
uint128_t uint128_value;
|
||||
int128_opt int128_value;
|
||||
uint128_opt uint128_value;
|
||||
bool bool_value;
|
||||
char_type char_value;
|
||||
float float_value;
|
||||
@@ -1233,8 +1255,8 @@ template <typename Context> class value {
|
||||
constexpr FMT_INLINE value(unsigned val) : uint_value(val) {}
|
||||
constexpr FMT_INLINE value(long long val) : long_long_value(val) {}
|
||||
constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
|
||||
FMT_INLINE value(int128_t val) : int128_value(val) {}
|
||||
FMT_INLINE value(uint128_t val) : uint128_value(val) {}
|
||||
FMT_INLINE value(int128_opt val) : int128_value(val) {}
|
||||
FMT_INLINE value(uint128_opt val) : uint128_value(val) {}
|
||||
constexpr FMT_INLINE value(float val) : float_value(val) {}
|
||||
constexpr FMT_INLINE value(double val) : double_value(val) {}
|
||||
FMT_INLINE value(long double val) : long_double_value(val) {}
|
||||
@@ -1259,14 +1281,10 @@ template <typename Context> class value {
|
||||
// have different extension points, e.g. `formatter<T>` for `format` and
|
||||
// `printf_formatter<T>` for `printf`.
|
||||
custom.format = format_custom_arg<
|
||||
value_type,
|
||||
conditional_t<has_formatter<value_type, Context>::value,
|
||||
typename Context::template formatter_type<value_type>,
|
||||
fallback_formatter<value_type, char_type>>>;
|
||||
value_type, typename Context::template formatter_type<value_type>>;
|
||||
}
|
||||
value(unformattable);
|
||||
value(unformattable_char);
|
||||
value(unformattable_const);
|
||||
value(unformattable_pointer);
|
||||
|
||||
private:
|
||||
@@ -1284,7 +1302,7 @@ template <typename Context> class value {
|
||||
};
|
||||
|
||||
template <typename Context, typename T>
|
||||
FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context>;
|
||||
FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context>;
|
||||
|
||||
// To minimize the number of types we need to deal with, long is translated
|
||||
// either to int or to long long depending on its size.
|
||||
@@ -1292,6 +1310,20 @@ enum { long_short = sizeof(long) == sizeof(int) };
|
||||
using long_type = conditional_t<long_short, int, long long>;
|
||||
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
|
||||
|
||||
template <typename T> struct format_as_result {
|
||||
template <typename U,
|
||||
FMT_ENABLE_IF(std::is_enum<U>::value || std::is_class<U>::value)>
|
||||
static auto map(U*) -> decltype(format_as(std::declval<U>()));
|
||||
static auto map(...) -> void;
|
||||
|
||||
using type = decltype(map(static_cast<T*>(nullptr)));
|
||||
};
|
||||
template <typename T> using format_as_t = typename format_as_result<T>::type;
|
||||
|
||||
template <typename T>
|
||||
struct has_format_as
|
||||
: bool_constant<!std::is_same<format_as_t<T>, void>::value> {};
|
||||
|
||||
// Maps formatting arguments to core types.
|
||||
// arg_mapper reports errors by returning unformattable instead of using
|
||||
// static_assert because it's used in the is_formattable trait.
|
||||
@@ -1317,8 +1349,12 @@ template <typename Context> struct arg_mapper {
|
||||
-> unsigned long long {
|
||||
return val;
|
||||
}
|
||||
FMT_CONSTEXPR FMT_INLINE auto map(int128_t val) -> int128_t { return val; }
|
||||
FMT_CONSTEXPR FMT_INLINE auto map(uint128_t val) -> uint128_t { return val; }
|
||||
FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt {
|
||||
return val;
|
||||
}
|
||||
FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt {
|
||||
return val;
|
||||
}
|
||||
FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; }
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value ||
|
||||
@@ -1363,46 +1399,6 @@ template <typename Context> struct arg_mapper {
|
||||
FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char {
|
||||
return {};
|
||||
}
|
||||
template <typename T,
|
||||
FMT_ENABLE_IF(
|
||||
std::is_constructible<basic_string_view<char_type>, T>::value &&
|
||||
!is_string<T>::value && !has_formatter<T, Context>::value &&
|
||||
!has_fallback_formatter<T, char_type>::value)>
|
||||
FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
|
||||
-> basic_string_view<char_type> {
|
||||
return basic_string_view<char_type>(val);
|
||||
}
|
||||
template <
|
||||
typename T,
|
||||
FMT_ENABLE_IF(
|
||||
std::is_constructible<std_string_view<char_type>, T>::value &&
|
||||
!std::is_constructible<basic_string_view<char_type>, T>::value &&
|
||||
!is_string<T>::value && !has_formatter<T, Context>::value &&
|
||||
!has_fallback_formatter<T, char_type>::value)>
|
||||
FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
|
||||
-> basic_string_view<char_type> {
|
||||
return std_string_view<char_type>(val);
|
||||
}
|
||||
|
||||
using cstring_result = conditional_t<std::is_same<char_type, char>::value,
|
||||
const char*, unformattable_pointer>;
|
||||
|
||||
FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val)
|
||||
-> cstring_result {
|
||||
return map(reinterpret_cast<const char*>(val));
|
||||
}
|
||||
FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val)
|
||||
-> cstring_result {
|
||||
return map(reinterpret_cast<const char*>(val));
|
||||
}
|
||||
FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(signed char* val)
|
||||
-> cstring_result {
|
||||
return map(reinterpret_cast<const char*>(val));
|
||||
}
|
||||
FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val)
|
||||
-> cstring_result {
|
||||
return map(reinterpret_cast<const char*>(val));
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; }
|
||||
FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* {
|
||||
@@ -1412,15 +1408,16 @@ template <typename Context> struct arg_mapper {
|
||||
return val;
|
||||
}
|
||||
|
||||
// We use SFINAE instead of a const T* parameter to avoid conflicting with
|
||||
// the C array overload.
|
||||
// Use SFINAE instead of a const T* parameter to avoid a conflict with the
|
||||
// array overload.
|
||||
template <
|
||||
typename T,
|
||||
FMT_ENABLE_IF(
|
||||
std::is_member_pointer<T>::value ||
|
||||
std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
|
||||
std::is_function<typename std::remove_pointer<T>::type>::value ||
|
||||
(std::is_convertible<const T&, const void*>::value &&
|
||||
!std::is_convertible<const T&, const char_type*>::value))>
|
||||
!std::is_convertible<const T&, const char_type*>::value &&
|
||||
!has_formatter<T, Context>::value))>
|
||||
FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer {
|
||||
return {};
|
||||
}
|
||||
@@ -1431,48 +1428,34 @@ template <typename Context> struct arg_mapper {
|
||||
return values;
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
FMT_ENABLE_IF(
|
||||
std::is_enum<T>::value&& std::is_convertible<T, int>::value &&
|
||||
!has_formatter<T, Context>::value &&
|
||||
!has_fallback_formatter<T, char_type>::value)>
|
||||
FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
|
||||
-> decltype(std::declval<arg_mapper>().map(
|
||||
static_cast<typename std::underlying_type<T>::type>(val))) {
|
||||
return map(static_cast<typename std::underlying_type<T>::type>(val));
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR FMT_INLINE auto map(detail::byte val) -> unsigned {
|
||||
return map(static_cast<unsigned char>(val));
|
||||
// Only map owning types because mapping views can be unsafe.
|
||||
template <typename T, typename U = format_as_t<T>,
|
||||
FMT_ENABLE_IF(std::is_arithmetic<U>::value)>
|
||||
FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(this->map(U())) {
|
||||
return map(format_as(val));
|
||||
}
|
||||
|
||||
template <typename T, typename U = remove_cvref_t<T>>
|
||||
struct formattable
|
||||
: bool_constant<has_const_formatter<U, Context>() ||
|
||||
!std::is_const<remove_reference_t<T>>::value ||
|
||||
has_fallback_formatter<U, char_type>::value> {};
|
||||
(has_formatter<U, Context>::value &&
|
||||
!std::is_const<remove_reference_t<T>>::value)> {};
|
||||
|
||||
#if FMT_MSC_VER != 0 && FMT_MSC_VER < 1910
|
||||
// Workaround a bug in MSVC.
|
||||
template <typename T> FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
|
||||
return val;
|
||||
}
|
||||
#else
|
||||
template <typename T, FMT_ENABLE_IF(formattable<T>::value)>
|
||||
FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
|
||||
return val;
|
||||
}
|
||||
template <typename T, FMT_ENABLE_IF(!formattable<T>::value)>
|
||||
FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable_const {
|
||||
FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable {
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T, typename U = remove_cvref_t<T>,
|
||||
FMT_ENABLE_IF(!is_string<U>::value && !is_char<U>::value &&
|
||||
!std::is_array<U>::value &&
|
||||
(has_formatter<U, Context>::value ||
|
||||
has_fallback_formatter<U, char_type>::value))>
|
||||
FMT_ENABLE_IF((std::is_class<U>::value || std::is_enum<U>::value ||
|
||||
std::is_union<U>::value) &&
|
||||
!is_string<U>::value && !is_char<U>::value &&
|
||||
!is_named_arg<U>::value &&
|
||||
!std::is_arithmetic<format_as_t<U>>::value)>
|
||||
FMT_CONSTEXPR FMT_INLINE auto map(T&& val)
|
||||
-> decltype(this->do_map(std::forward<T>(val))) {
|
||||
return do_map(std::forward<T>(val));
|
||||
@@ -1480,7 +1463,7 @@ template <typename Context> struct arg_mapper {
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
|
||||
FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg)
|
||||
-> decltype(std::declval<arg_mapper>().map(named_arg.value)) {
|
||||
-> decltype(this->map(named_arg.value)) {
|
||||
return map(named_arg.value);
|
||||
}
|
||||
|
||||
@@ -1498,27 +1481,20 @@ enum { packed_arg_bits = 4 };
|
||||
enum { max_packed_args = 62 / packed_arg_bits };
|
||||
enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
|
||||
enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
} // namespace detail
|
||||
|
||||
// An output iterator that appends to a buffer.
|
||||
// It is used to reduce symbol sizes for the common case.
|
||||
class appender : public std::back_insert_iterator<detail::buffer<char>> {
|
||||
using base = std::back_insert_iterator<detail::buffer<char>>;
|
||||
|
||||
template <typename T>
|
||||
friend auto get_buffer(appender out) -> detail::buffer<char>& {
|
||||
return detail::get_container(out);
|
||||
}
|
||||
|
||||
public:
|
||||
using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
|
||||
appender(base it) FMT_NOEXCEPT : base(it) {}
|
||||
using _Unchecked_type = appender; // Mark iterator as checked.
|
||||
appender(base it) noexcept : base(it) {}
|
||||
FMT_UNCHECKED_ITERATOR(appender);
|
||||
|
||||
auto operator++() FMT_NOEXCEPT -> appender& { return *this; }
|
||||
|
||||
auto operator++(int) FMT_NOEXCEPT -> appender { return *this; }
|
||||
auto operator++() noexcept -> appender& { return *this; }
|
||||
auto operator++(int) noexcept -> appender { return *this; }
|
||||
};
|
||||
|
||||
// A formatting argument. It is a trivially copyable/constructible type to
|
||||
@@ -1529,7 +1505,7 @@ template <typename Context> class basic_format_arg {
|
||||
detail::type type_;
|
||||
|
||||
template <typename ContextType, typename T>
|
||||
friend FMT_CONSTEXPR auto detail::make_arg(const T& value)
|
||||
friend FMT_CONSTEXPR auto detail::make_arg(T&& value)
|
||||
-> basic_format_arg<ContextType>;
|
||||
|
||||
template <typename Visitor, typename Ctx>
|
||||
@@ -1564,7 +1540,7 @@ template <typename Context> class basic_format_arg {
|
||||
|
||||
constexpr basic_format_arg() : type_(detail::type::none_type) {}
|
||||
|
||||
constexpr explicit operator bool() const FMT_NOEXCEPT {
|
||||
constexpr explicit operator bool() const noexcept {
|
||||
return type_ != detail::type::none_type;
|
||||
}
|
||||
|
||||
@@ -1583,6 +1559,7 @@ template <typename Context> class basic_format_arg {
|
||||
``vis(value)`` will be called with the value of type ``double``.
|
||||
\endrst
|
||||
*/
|
||||
FMT_MODULE_EXPORT
|
||||
template <typename Visitor, typename Context>
|
||||
FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
|
||||
Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) {
|
||||
@@ -1624,7 +1601,7 @@ FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
|
||||
return vis(monostate());
|
||||
}
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
namespace detail {
|
||||
|
||||
template <typename Char, typename InputIt>
|
||||
auto copy_str(InputIt begin, InputIt end, appender out) -> appender {
|
||||
@@ -1632,11 +1609,15 @@ auto copy_str(InputIt begin, InputIt end, appender out) -> appender {
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Char, typename R, typename OutputIt>
|
||||
FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt {
|
||||
return detail::copy_str<Char>(rng.begin(), rng.end(), out);
|
||||
}
|
||||
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
|
||||
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
|
||||
template <typename... Ts> struct void_t_impl { using type = void; };
|
||||
template <typename... Ts>
|
||||
using void_t = typename detail::void_t_impl<Ts...>::type;
|
||||
template <typename...> struct void_t_impl { using type = void; };
|
||||
template <typename... T> using void_t = typename void_t_impl<T...>::type;
|
||||
#else
|
||||
template <typename...> using void_t = void;
|
||||
#endif
|
||||
@@ -1651,13 +1632,12 @@ struct is_output_iterator<
|
||||
decltype(*std::declval<It>() = std::declval<T>())>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename OutputIt>
|
||||
struct is_back_insert_iterator : std::false_type {};
|
||||
template <typename It> struct is_back_insert_iterator : std::false_type {};
|
||||
template <typename Container>
|
||||
struct is_back_insert_iterator<std::back_insert_iterator<Container>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename OutputIt>
|
||||
template <typename It>
|
||||
struct is_contiguous_back_insert_iterator : std::false_type {};
|
||||
template <typename Container>
|
||||
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
|
||||
@@ -1665,16 +1645,16 @@ struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
|
||||
template <>
|
||||
struct is_contiguous_back_insert_iterator<appender> : std::true_type {};
|
||||
|
||||
// A type-erased reference to an std::locale to avoid heavy <locale> include.
|
||||
// A type-erased reference to an std::locale to avoid a heavy <locale> include.
|
||||
class locale_ref {
|
||||
private:
|
||||
const void* locale_; // A type-erased pointer to std::locale.
|
||||
|
||||
public:
|
||||
constexpr locale_ref() : locale_(nullptr) {}
|
||||
constexpr FMT_INLINE locale_ref() : locale_(nullptr) {}
|
||||
template <typename Locale> explicit locale_ref(const Locale& loc);
|
||||
|
||||
explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; }
|
||||
explicit operator bool() const noexcept { return locale_ != nullptr; }
|
||||
|
||||
template <typename Locale> auto get() const -> Locale;
|
||||
};
|
||||
@@ -1690,40 +1670,23 @@ constexpr auto encode_types() -> unsigned long long {
|
||||
}
|
||||
|
||||
template <typename Context, typename T>
|
||||
FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context> {
|
||||
basic_format_arg<Context> arg;
|
||||
arg.type_ = mapped_type_constant<T, Context>::value;
|
||||
arg.value_ = arg_mapper<Context>().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 <bool IS_PACKED, typename Context, type, typename T,
|
||||
FMT_ENABLE_IF(IS_PACKED)>
|
||||
FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
|
||||
const auto& arg = arg_mapper<Context>().map(std::forward<T>(val));
|
||||
FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value<Context> {
|
||||
auto&& arg = arg_mapper<Context>().map(FMT_FORWARD(val));
|
||||
using arg_type = remove_cvref_t<decltype(arg)>;
|
||||
|
||||
constexpr bool formattable_char =
|
||||
!std::is_same<decltype(arg), const unformattable_char&>::value;
|
||||
!std::is_same<arg_type, unformattable_char>::value;
|
||||
static_assert(formattable_char, "Mixing character types is disallowed.");
|
||||
|
||||
constexpr bool formattable_const =
|
||||
!std::is_same<decltype(arg), const unformattable_const&>::value;
|
||||
static_assert(formattable_const, "Cannot format a const argument.");
|
||||
|
||||
// Formatting of arbitrary pointers is disallowed. If you want to output
|
||||
// a pointer cast it to "void *" or "const void *". In particular, this
|
||||
// forbids formatting of "[const] volatile char *" which is printed as bool
|
||||
// by iostreams.
|
||||
// Formatting of arbitrary pointers is disallowed. If you want to format a
|
||||
// pointer cast it to `void*` or `const void*`. In particular, this forbids
|
||||
// formatting of `[const] volatile char*` printed as bool by iostreams.
|
||||
constexpr bool formattable_pointer =
|
||||
!std::is_same<decltype(arg), const unformattable_pointer&>::value;
|
||||
!std::is_same<arg_type, unformattable_pointer>::value;
|
||||
static_assert(formattable_pointer,
|
||||
"Formatting of non-void pointers is disallowed.");
|
||||
|
||||
constexpr bool formattable =
|
||||
!std::is_same<decltype(arg), const unformattable&>::value;
|
||||
constexpr bool formattable = !std::is_same<arg_type, unformattable>::value;
|
||||
static_assert(
|
||||
formattable,
|
||||
"Cannot format an argument. To make type T formattable provide a "
|
||||
@@ -1731,19 +1694,33 @@ FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
|
||||
return {arg};
|
||||
}
|
||||
|
||||
template <typename Context, typename T>
|
||||
FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context> {
|
||||
auto arg = basic_format_arg<Context>();
|
||||
arg.type_ = mapped_type_constant<T, Context>::value;
|
||||
arg.value_ = make_value<Context>(value);
|
||||
return arg;
|
||||
}
|
||||
|
||||
// The DEPRECATED type template parameter is there to avoid an ODR violation
|
||||
// when using a fallback formatter in one translation unit and an implicit
|
||||
// conversion in another (not recommended).
|
||||
template <bool IS_PACKED, typename Context, type, typename T,
|
||||
FMT_ENABLE_IF(IS_PACKED)>
|
||||
FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
|
||||
return make_value<Context>(val);
|
||||
}
|
||||
|
||||
template <bool IS_PACKED, typename Context, type, typename T,
|
||||
FMT_ENABLE_IF(!IS_PACKED)>
|
||||
inline auto make_arg(const T& value) -> basic_format_arg<Context> {
|
||||
FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg<Context> {
|
||||
return make_arg<Context>(value);
|
||||
}
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
} // namespace detail
|
||||
FMT_BEGIN_EXPORT
|
||||
|
||||
// Formatting context.
|
||||
template <typename OutputIt, typename Char> class basic_format_context {
|
||||
public:
|
||||
/** The character type for the output. */
|
||||
using char_type = Char;
|
||||
|
||||
private:
|
||||
OutputIt out_;
|
||||
basic_format_args<basic_format_context> args_;
|
||||
@@ -1752,31 +1729,32 @@ template <typename OutputIt, typename Char> class basic_format_context {
|
||||
public:
|
||||
using iterator = OutputIt;
|
||||
using format_arg = basic_format_arg<basic_format_context>;
|
||||
using format_args = basic_format_args<basic_format_context>;
|
||||
using parse_context_type = basic_format_parse_context<Char>;
|
||||
template <typename T> using formatter_type = formatter<T, char_type>;
|
||||
template <typename T> using formatter_type = formatter<T, Char>;
|
||||
|
||||
/** The character type for the output. */
|
||||
using char_type = Char;
|
||||
|
||||
basic_format_context(basic_format_context&&) = default;
|
||||
basic_format_context(const basic_format_context&) = delete;
|
||||
void operator=(const basic_format_context&) = delete;
|
||||
/**
|
||||
Constructs a ``basic_format_context`` object. References to the arguments are
|
||||
stored in the object so make sure they have appropriate lifetimes.
|
||||
Constructs a ``basic_format_context`` object. References to the arguments
|
||||
are stored in the object so make sure they have appropriate lifetimes.
|
||||
*/
|
||||
constexpr basic_format_context(
|
||||
OutputIt out, basic_format_args<basic_format_context> ctx_args,
|
||||
detail::locale_ref loc = detail::locale_ref())
|
||||
constexpr basic_format_context(OutputIt out, format_args ctx_args,
|
||||
detail::locale_ref loc = {})
|
||||
: out_(out), args_(ctx_args), loc_(loc) {}
|
||||
|
||||
constexpr auto arg(int id) const -> format_arg { return args_.get(id); }
|
||||
FMT_CONSTEXPR auto arg(basic_string_view<char_type> name) -> format_arg {
|
||||
FMT_CONSTEXPR auto arg(basic_string_view<Char> name) -> format_arg {
|
||||
return args_.get(name);
|
||||
}
|
||||
FMT_CONSTEXPR auto arg_id(basic_string_view<char_type> name) -> int {
|
||||
FMT_CONSTEXPR auto arg_id(basic_string_view<Char> name) -> int {
|
||||
return args_.get_id(name);
|
||||
}
|
||||
auto args() const -> const basic_format_args<basic_format_context>& {
|
||||
return args_;
|
||||
}
|
||||
auto args() const -> const format_args& { return args_; }
|
||||
|
||||
FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; }
|
||||
void on_error(const char* message) { error_handler().on_error(message); }
|
||||
@@ -1797,16 +1775,10 @@ using buffer_context =
|
||||
basic_format_context<detail::buffer_appender<Char>, Char>;
|
||||
using format_context = buffer_context<char>;
|
||||
|
||||
// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164.
|
||||
#define FMT_BUFFER_CONTEXT(Char) \
|
||||
basic_format_context<detail::buffer_appender<Char>, Char>
|
||||
|
||||
template <typename T, typename Char = char>
|
||||
using is_formattable = bool_constant<
|
||||
!std::is_base_of<detail::unformattable,
|
||||
decltype(detail::arg_mapper<buffer_context<Char>>().map(
|
||||
std::declval<T>()))>::value &&
|
||||
!detail::has_fallback_formatter<T, Char>::value>;
|
||||
using is_formattable = bool_constant<!std::is_base_of<
|
||||
detail::unformattable, decltype(detail::arg_mapper<buffer_context<Char>>()
|
||||
.map(std::declval<T>()))>::value>;
|
||||
|
||||
/**
|
||||
\rst
|
||||
@@ -1853,7 +1825,7 @@ class format_arg_store
|
||||
data_{detail::make_arg<
|
||||
is_packed, Context,
|
||||
detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>(
|
||||
std::forward<T>(args))...} {
|
||||
FMT_FORWARD(args))...} {
|
||||
detail::init_named_args(data_.named_args(), 0, 0, args...);
|
||||
}
|
||||
};
|
||||
@@ -1866,10 +1838,10 @@ class format_arg_store
|
||||
See `~fmt::arg` for lifetime considerations.
|
||||
\endrst
|
||||
*/
|
||||
template <typename Context = format_context, typename... Args>
|
||||
constexpr auto make_format_args(Args&&... args)
|
||||
-> format_arg_store<Context, remove_cvref_t<Args>...> {
|
||||
return {std::forward<Args>(args)...};
|
||||
template <typename Context = format_context, typename... T>
|
||||
constexpr auto make_format_args(T&&... args)
|
||||
-> format_arg_store<Context, remove_cvref_t<T>...> {
|
||||
return {FMT_FORWARD(args)...};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1888,6 +1860,7 @@ inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
|
||||
static_assert(!detail::is_named_arg<T>(), "nested named arguments");
|
||||
return {name, arg};
|
||||
}
|
||||
FMT_END_EXPORT
|
||||
|
||||
/**
|
||||
\rst
|
||||
@@ -2013,20 +1986,28 @@ template <typename Context> class basic_format_args {
|
||||
/** An alias to ``basic_format_args<format_context>``. */
|
||||
// A separate type would result in shorter symbols but break ABI compatibility
|
||||
// between clang and gcc on ARM (#1919).
|
||||
using format_args = basic_format_args<format_context>;
|
||||
FMT_MODULE_EXPORT using format_args = basic_format_args<format_context>;
|
||||
|
||||
// We cannot use enum classes as bit fields because of a gcc bug
|
||||
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414.
|
||||
// We cannot use enum classes as bit fields because of a gcc bug, so we put them
|
||||
// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414).
|
||||
// Additionally, if an underlying type is specified, older gcc incorrectly warns
|
||||
// that the type is too small. Both bugs are fixed in gcc 9.3.
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903
|
||||
# define FMT_ENUM_UNDERLYING_TYPE(type)
|
||||
#else
|
||||
# define FMT_ENUM_UNDERLYING_TYPE(type) : type
|
||||
#endif
|
||||
namespace align {
|
||||
enum type { none, left, right, center, numeric };
|
||||
enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center,
|
||||
numeric};
|
||||
}
|
||||
using align_t = align::type;
|
||||
namespace sign {
|
||||
enum type { none, minus, plus, space };
|
||||
enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space};
|
||||
}
|
||||
using sign_t = sign::type;
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
namespace detail {
|
||||
|
||||
// Workaround an array initialization issue in gcc 4.8.
|
||||
template <typename Char> struct fill_t {
|
||||
@@ -2038,7 +2019,7 @@ template <typename Char> struct fill_t {
|
||||
public:
|
||||
FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
|
||||
auto size = s.size();
|
||||
if (size > max_size) return throw_format_error("invalid fill");
|
||||
FMT_ASSERT(size <= max_size, "invalid fill");
|
||||
for (size_t i = 0; i < size; ++i) data_[i] = s[i];
|
||||
size_ = static_cast<unsigned char>(size);
|
||||
}
|
||||
@@ -2051,11 +2032,10 @@ template <typename Char> struct fill_t {
|
||||
return data_[index];
|
||||
}
|
||||
};
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
} // namespace detail
|
||||
|
||||
enum class presentation_type : unsigned char {
|
||||
none,
|
||||
// Integer types should go first,
|
||||
dec, // 'd'
|
||||
oct, // 'o'
|
||||
hex_lower, // 'x'
|
||||
@@ -2072,11 +2052,12 @@ enum class presentation_type : unsigned char {
|
||||
general_upper, // 'G'
|
||||
chr, // 'c'
|
||||
string, // 's'
|
||||
pointer // 'p'
|
||||
pointer, // 'p'
|
||||
debug // '?'
|
||||
};
|
||||
|
||||
// Format specifiers for built-in and string types.
|
||||
template <typename Char> struct basic_format_specs {
|
||||
template <typename Char = char> struct format_specs {
|
||||
int width;
|
||||
int precision;
|
||||
presentation_type type;
|
||||
@@ -2086,7 +2067,7 @@ template <typename Char> struct basic_format_specs {
|
||||
bool localized : 1;
|
||||
detail::fill_t<Char> fill;
|
||||
|
||||
constexpr basic_format_specs()
|
||||
constexpr format_specs()
|
||||
: width(0),
|
||||
precision(-1),
|
||||
type(presentation_type::none),
|
||||
@@ -2096,9 +2077,7 @@ template <typename Char> struct basic_format_specs {
|
||||
localized(false) {}
|
||||
};
|
||||
|
||||
using format_specs = basic_format_specs<char>;
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
namespace detail {
|
||||
|
||||
enum class arg_id_kind { none, index, name };
|
||||
|
||||
@@ -2119,7 +2098,7 @@ template <typename Char> struct arg_ref {
|
||||
|
||||
arg_id_kind kind;
|
||||
union value {
|
||||
FMT_CONSTEXPR value(int id = 0) : index{id} {}
|
||||
FMT_CONSTEXPR value(int idx = 0) : index(idx) {}
|
||||
FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
|
||||
|
||||
int index;
|
||||
@@ -2128,129 +2107,30 @@ template <typename Char> struct arg_ref {
|
||||
};
|
||||
|
||||
// Format specifiers with width and precision resolved at formatting rather
|
||||
// than parsing time to allow re-using the same parsed specifiers with
|
||||
// than parsing time to allow reusing the same parsed specifiers with
|
||||
// different sets of arguments (precompilation of format strings).
|
||||
template <typename Char>
|
||||
struct dynamic_format_specs : basic_format_specs<Char> {
|
||||
template <typename Char = char>
|
||||
struct dynamic_format_specs : format_specs<Char> {
|
||||
arg_ref<Char> width_ref;
|
||||
arg_ref<Char> precision_ref;
|
||||
};
|
||||
|
||||
struct auto_id {};
|
||||
|
||||
// A format specifier handler that sets fields in basic_format_specs.
|
||||
template <typename Char> class specs_setter {
|
||||
protected:
|
||||
basic_format_specs<Char>& specs_;
|
||||
|
||||
public:
|
||||
explicit FMT_CONSTEXPR specs_setter(basic_format_specs<Char>& specs)
|
||||
: specs_(specs) {}
|
||||
|
||||
FMT_CONSTEXPR specs_setter(const specs_setter& other)
|
||||
: specs_(other.specs_) {}
|
||||
|
||||
FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; }
|
||||
FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
|
||||
specs_.fill = fill;
|
||||
}
|
||||
FMT_CONSTEXPR void on_sign(sign_t s) { specs_.sign = s; }
|
||||
FMT_CONSTEXPR void on_hash() { specs_.alt = true; }
|
||||
FMT_CONSTEXPR void on_localized() { specs_.localized = true; }
|
||||
|
||||
FMT_CONSTEXPR void on_zero() {
|
||||
if (specs_.align == align::none) specs_.align = align::numeric;
|
||||
specs_.fill[0] = Char('0');
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_width(int width) { specs_.width = width; }
|
||||
FMT_CONSTEXPR void on_precision(int precision) {
|
||||
specs_.precision = precision;
|
||||
}
|
||||
FMT_CONSTEXPR void end_precision() {}
|
||||
|
||||
FMT_CONSTEXPR void on_type(presentation_type type) { specs_.type = type; }
|
||||
};
|
||||
|
||||
// Format spec handler that saves references to arguments representing dynamic
|
||||
// width and precision to be resolved at formatting time.
|
||||
template <typename ParseContext>
|
||||
class dynamic_specs_handler
|
||||
: public specs_setter<typename ParseContext::char_type> {
|
||||
public:
|
||||
using char_type = typename ParseContext::char_type;
|
||||
|
||||
FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs<char_type>& specs,
|
||||
ParseContext& ctx)
|
||||
: specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
|
||||
|
||||
FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other)
|
||||
: specs_setter<char_type>(other),
|
||||
specs_(other.specs_),
|
||||
context_(other.context_) {}
|
||||
|
||||
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
|
||||
specs_.width_ref = make_arg_ref(arg_id);
|
||||
}
|
||||
|
||||
template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
|
||||
specs_.precision_ref = make_arg_ref(arg_id);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_error(const char* message) {
|
||||
context_.on_error(message);
|
||||
}
|
||||
|
||||
private:
|
||||
dynamic_format_specs<char_type>& specs_;
|
||||
ParseContext& context_;
|
||||
|
||||
using arg_ref_type = arg_ref<char_type>;
|
||||
|
||||
FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type {
|
||||
context_.check_arg_id(arg_id);
|
||||
return arg_ref_type(arg_id);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type {
|
||||
return arg_ref_type(context_.next_arg_id());
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR auto make_arg_ref(basic_string_view<char_type> arg_id)
|
||||
-> arg_ref_type {
|
||||
context_.check_arg_id(arg_id);
|
||||
basic_string_view<char_type> format_str(
|
||||
context_.begin(), to_unsigned(context_.end() - context_.begin()));
|
||||
return arg_ref_type(arg_id);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char> constexpr bool is_ascii_letter(Char c) {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||
}
|
||||
|
||||
// Converts a character to ASCII. Returns a number > 127 on conversion failure.
|
||||
// Converts a character to ASCII. Returns '\0' on conversion failure.
|
||||
template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
|
||||
constexpr auto to_ascii(Char value) -> Char {
|
||||
return value;
|
||||
constexpr auto to_ascii(Char c) -> char {
|
||||
return c <= 0xff ? static_cast<char>(c) : '\0';
|
||||
}
|
||||
template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
|
||||
constexpr auto to_ascii(Char value) ->
|
||||
typename std::underlying_type<Char>::type {
|
||||
return value;
|
||||
constexpr auto to_ascii(Char c) -> char {
|
||||
return c <= 0xff ? static_cast<char>(c) : '\0';
|
||||
}
|
||||
|
||||
// Returns the number of code units in a code point or 1 on error.
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
|
||||
if (const_check(sizeof(Char) != 1)) return 1;
|
||||
auto lengths =
|
||||
"\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4";
|
||||
int len = lengths[static_cast<unsigned char>(*begin) >> 3];
|
||||
|
||||
// Compute the pointer to the next character early so that the next
|
||||
// iteration can start working on the next character. Neither Clang
|
||||
// nor GCC figure out this reordering on their own.
|
||||
return len + !len;
|
||||
auto c = static_cast<unsigned char>(*begin);
|
||||
return static_cast<int>((0x3a55000000000000ull >> (2 * (c >> 3))) & 0x3) + 1;
|
||||
}
|
||||
|
||||
// Return the result via the out param to workaround gcc bug 77539.
|
||||
@@ -2295,277 +2175,284 @@ FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end,
|
||||
: error_value;
|
||||
}
|
||||
|
||||
// Parses fill and alignment.
|
||||
template <typename Char, typename Handler>
|
||||
FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
|
||||
Handler&& handler) -> const Char* {
|
||||
FMT_ASSERT(begin != end, "");
|
||||
auto align = align::none;
|
||||
auto p = begin + code_point_length(begin);
|
||||
if (p >= end) p = begin;
|
||||
for (;;) {
|
||||
switch (to_ascii(*p)) {
|
||||
case '<':
|
||||
align = align::left;
|
||||
break;
|
||||
case '>':
|
||||
align = align::right;
|
||||
break;
|
||||
case '^':
|
||||
align = align::center;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (align != align::none) {
|
||||
if (p != begin) {
|
||||
auto c = *begin;
|
||||
if (c == '{')
|
||||
return handler.on_error("invalid fill character '{'"), begin;
|
||||
handler.on_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
|
||||
begin = p + 1;
|
||||
} else
|
||||
++begin;
|
||||
handler.on_align(align);
|
||||
break;
|
||||
} else if (p == begin) {
|
||||
break;
|
||||
}
|
||||
p = begin;
|
||||
FMT_CONSTEXPR inline auto parse_align(char c) -> align_t {
|
||||
switch (c) {
|
||||
case '<':
|
||||
return align::left;
|
||||
case '>':
|
||||
return align::right;
|
||||
case '^':
|
||||
return align::center;
|
||||
}
|
||||
return begin;
|
||||
return align::none;
|
||||
}
|
||||
|
||||
template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
|
||||
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
|
||||
template <typename Char> constexpr auto is_name_start(Char c) -> bool {
|
||||
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_';
|
||||
}
|
||||
|
||||
template <typename Char, typename IDHandler>
|
||||
template <typename Char, typename Handler>
|
||||
FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end,
|
||||
IDHandler&& handler) -> const Char* {
|
||||
FMT_ASSERT(begin != end, "");
|
||||
Handler&& handler) -> const Char* {
|
||||
Char c = *begin;
|
||||
if (c >= '0' && c <= '9') {
|
||||
int index = 0;
|
||||
constexpr int max = (std::numeric_limits<int>::max)();
|
||||
if (c != '0')
|
||||
index =
|
||||
parse_nonnegative_int(begin, end, (std::numeric_limits<int>::max)());
|
||||
index = parse_nonnegative_int(begin, end, max);
|
||||
else
|
||||
++begin;
|
||||
if (begin == end || (*begin != '}' && *begin != ':'))
|
||||
handler.on_error("invalid format string");
|
||||
throw_format_error("invalid format string");
|
||||
else
|
||||
handler(index);
|
||||
handler.on_index(index);
|
||||
return begin;
|
||||
}
|
||||
if (!is_name_start(c)) {
|
||||
handler.on_error("invalid format string");
|
||||
throw_format_error("invalid format string");
|
||||
return begin;
|
||||
}
|
||||
auto it = begin;
|
||||
do {
|
||||
++it;
|
||||
} while (it != end && (is_name_start(c = *it) || ('0' <= c && c <= '9')));
|
||||
handler(basic_string_view<Char>(begin, to_unsigned(it - begin)));
|
||||
} while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9')));
|
||||
handler.on_name({begin, to_unsigned(it - begin)});
|
||||
return it;
|
||||
}
|
||||
|
||||
template <typename Char, typename IDHandler>
|
||||
template <typename Char, typename Handler>
|
||||
FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end,
|
||||
IDHandler&& handler) -> const Char* {
|
||||
Handler&& handler) -> const Char* {
|
||||
FMT_ASSERT(begin != end, "");
|
||||
Char c = *begin;
|
||||
if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler);
|
||||
handler();
|
||||
handler.on_auto();
|
||||
return begin;
|
||||
}
|
||||
|
||||
template <typename Char, typename Handler>
|
||||
FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end,
|
||||
Handler&& handler) -> const Char* {
|
||||
using detail::auto_id;
|
||||
struct width_adapter {
|
||||
Handler& handler;
|
||||
template <typename Char> struct dynamic_spec_id_handler {
|
||||
basic_format_parse_context<Char>& ctx;
|
||||
arg_ref<Char>& ref;
|
||||
|
||||
FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
|
||||
FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); }
|
||||
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
|
||||
handler.on_dynamic_width(id);
|
||||
}
|
||||
FMT_CONSTEXPR void on_error(const char* message) {
|
||||
if (message) handler.on_error(message);
|
||||
}
|
||||
};
|
||||
FMT_CONSTEXPR void on_auto() {
|
||||
int id = ctx.next_arg_id();
|
||||
ref = arg_ref<Char>(id);
|
||||
ctx.check_dynamic_spec(id);
|
||||
}
|
||||
FMT_CONSTEXPR void on_index(int id) {
|
||||
ref = arg_ref<Char>(id);
|
||||
ctx.check_arg_id(id);
|
||||
ctx.check_dynamic_spec(id);
|
||||
}
|
||||
FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
|
||||
ref = arg_ref<Char>(id);
|
||||
ctx.check_arg_id(id);
|
||||
}
|
||||
};
|
||||
|
||||
// Parses [integer | "{" [arg_id] "}"].
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end,
|
||||
int& value, arg_ref<Char>& ref,
|
||||
basic_format_parse_context<Char>& ctx)
|
||||
-> const Char* {
|
||||
FMT_ASSERT(begin != end, "");
|
||||
if ('0' <= *begin && *begin <= '9') {
|
||||
int width = parse_nonnegative_int(begin, end, -1);
|
||||
if (width != -1)
|
||||
handler.on_width(width);
|
||||
int val = parse_nonnegative_int(begin, end, -1);
|
||||
if (val != -1)
|
||||
value = val;
|
||||
else
|
||||
handler.on_error("number is too big");
|
||||
throw_format_error("number is too big");
|
||||
} else if (*begin == '{') {
|
||||
++begin;
|
||||
if (begin != end) begin = parse_arg_id(begin, end, width_adapter{handler});
|
||||
if (begin == end || *begin != '}')
|
||||
return handler.on_error("invalid format string"), begin;
|
||||
++begin;
|
||||
auto handler = dynamic_spec_id_handler<Char>{ctx, ref};
|
||||
if (begin != end) begin = parse_arg_id(begin, end, handler);
|
||||
if (begin != end && *begin == '}') return ++begin;
|
||||
throw_format_error("invalid format string");
|
||||
}
|
||||
return begin;
|
||||
}
|
||||
|
||||
template <typename Char, typename Handler>
|
||||
FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
|
||||
Handler&& handler) -> const Char* {
|
||||
using detail::auto_id;
|
||||
struct precision_adapter {
|
||||
Handler& handler;
|
||||
|
||||
FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
|
||||
FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); }
|
||||
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
|
||||
handler.on_dynamic_precision(id);
|
||||
}
|
||||
FMT_CONSTEXPR void on_error(const char* message) {
|
||||
if (message) handler.on_error(message);
|
||||
}
|
||||
};
|
||||
|
||||
++begin;
|
||||
auto c = begin != end ? *begin : Char();
|
||||
if ('0' <= c && c <= '9') {
|
||||
auto precision = parse_nonnegative_int(begin, end, -1);
|
||||
if (precision != -1)
|
||||
handler.on_precision(precision);
|
||||
else
|
||||
handler.on_error("number is too big");
|
||||
} else if (c == '{') {
|
||||
++begin;
|
||||
if (begin != end)
|
||||
begin = parse_arg_id(begin, end, precision_adapter{handler});
|
||||
if (begin == end || *begin++ != '}')
|
||||
return handler.on_error("invalid format string"), begin;
|
||||
} else {
|
||||
return handler.on_error("missing precision specifier"), begin;
|
||||
}
|
||||
handler.end_precision();
|
||||
return begin;
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type {
|
||||
switch (to_ascii(type)) {
|
||||
case 'd':
|
||||
return presentation_type::dec;
|
||||
case 'o':
|
||||
return presentation_type::oct;
|
||||
case 'x':
|
||||
return presentation_type::hex_lower;
|
||||
case 'X':
|
||||
return presentation_type::hex_upper;
|
||||
case 'b':
|
||||
return presentation_type::bin_lower;
|
||||
case 'B':
|
||||
return presentation_type::bin_upper;
|
||||
case 'a':
|
||||
return presentation_type::hexfloat_lower;
|
||||
case 'A':
|
||||
return presentation_type::hexfloat_upper;
|
||||
case 'e':
|
||||
return presentation_type::exp_lower;
|
||||
case 'E':
|
||||
return presentation_type::exp_upper;
|
||||
case 'f':
|
||||
return presentation_type::fixed_lower;
|
||||
case 'F':
|
||||
return presentation_type::fixed_upper;
|
||||
case 'g':
|
||||
return presentation_type::general_lower;
|
||||
case 'G':
|
||||
return presentation_type::general_upper;
|
||||
case 'c':
|
||||
return presentation_type::chr;
|
||||
case 's':
|
||||
return presentation_type::string;
|
||||
case 'p':
|
||||
return presentation_type::pointer;
|
||||
default:
|
||||
return presentation_type::none;
|
||||
}
|
||||
}
|
||||
|
||||
// Parses standard format specifiers and sends notifications about parsed
|
||||
// components to handler.
|
||||
template <typename Char, typename SpecHandler>
|
||||
FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin,
|
||||
const Char* end,
|
||||
SpecHandler&& handler)
|
||||
FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
|
||||
int& value, arg_ref<Char>& ref,
|
||||
basic_format_parse_context<Char>& ctx)
|
||||
-> const Char* {
|
||||
if (1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) &&
|
||||
*begin != 'L') {
|
||||
presentation_type type = parse_presentation_type(*begin++);
|
||||
if (type == presentation_type::none)
|
||||
handler.on_error("invalid type specifier");
|
||||
handler.on_type(type);
|
||||
++begin;
|
||||
if (begin == end || *begin == '}') {
|
||||
throw_format_error("invalid precision");
|
||||
return begin;
|
||||
}
|
||||
return parse_dynamic_spec(begin, end, value, ref, ctx);
|
||||
}
|
||||
|
||||
if (begin == end) return begin;
|
||||
enum class state { start, align, sign, hash, zero, width, precision, locale };
|
||||
|
||||
begin = parse_align(begin, end, handler);
|
||||
if (begin == end) return begin;
|
||||
|
||||
// Parse sign.
|
||||
switch (to_ascii(*begin)) {
|
||||
case '+':
|
||||
handler.on_sign(sign::plus);
|
||||
++begin;
|
||||
break;
|
||||
case '-':
|
||||
handler.on_sign(sign::minus);
|
||||
++begin;
|
||||
break;
|
||||
case ' ':
|
||||
handler.on_sign(sign::space);
|
||||
++begin;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (begin == end) return begin;
|
||||
|
||||
if (*begin == '#') {
|
||||
handler.on_hash();
|
||||
if (++begin == end) return begin;
|
||||
}
|
||||
|
||||
// Parse zero flag.
|
||||
if (*begin == '0') {
|
||||
handler.on_zero();
|
||||
if (++begin == end) return begin;
|
||||
}
|
||||
|
||||
begin = parse_width(begin, end, handler);
|
||||
if (begin == end) return begin;
|
||||
|
||||
// Parse precision.
|
||||
if (*begin == '.') {
|
||||
begin = parse_precision(begin, end, handler);
|
||||
// Parses standard format specifiers.
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
|
||||
const Char* begin, const Char* end, dynamic_format_specs<Char>& specs,
|
||||
basic_format_parse_context<Char>& ctx, type arg_type) -> const Char* {
|
||||
auto c = '\0';
|
||||
if (end - begin > 1) {
|
||||
auto next = to_ascii(begin[1]);
|
||||
c = parse_align(next) == align::none ? to_ascii(*begin) : '\0';
|
||||
} else {
|
||||
if (begin == end) return begin;
|
||||
c = to_ascii(*begin);
|
||||
}
|
||||
|
||||
if (*begin == 'L') {
|
||||
handler.on_localized();
|
||||
++begin;
|
||||
}
|
||||
struct {
|
||||
state current_state = state::start;
|
||||
FMT_CONSTEXPR void operator()(state s, bool valid = true) {
|
||||
if (current_state >= s || !valid)
|
||||
throw_format_error("invalid format specifier");
|
||||
current_state = s;
|
||||
}
|
||||
} enter_state;
|
||||
|
||||
// Parse type.
|
||||
if (begin != end && *begin != '}') {
|
||||
presentation_type type = parse_presentation_type(*begin++);
|
||||
if (type == presentation_type::none)
|
||||
handler.on_error("invalid type specifier");
|
||||
handler.on_type(type);
|
||||
using pres = presentation_type;
|
||||
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
|
||||
struct {
|
||||
const Char*& begin;
|
||||
dynamic_format_specs<Char>& specs;
|
||||
type arg_type;
|
||||
|
||||
FMT_CONSTEXPR auto operator()(pres type, int set) -> const Char* {
|
||||
if (!in(arg_type, set)) throw_format_error("invalid format specifier");
|
||||
specs.type = type;
|
||||
return begin + 1;
|
||||
}
|
||||
} parse_presentation_type{begin, specs, arg_type};
|
||||
|
||||
for (;;) {
|
||||
switch (c) {
|
||||
case '<':
|
||||
case '>':
|
||||
case '^':
|
||||
enter_state(state::align);
|
||||
specs.align = parse_align(c);
|
||||
++begin;
|
||||
break;
|
||||
case '+':
|
||||
case '-':
|
||||
case ' ':
|
||||
enter_state(state::sign, in(arg_type, sint_set | float_set));
|
||||
switch (c) {
|
||||
case '+':
|
||||
specs.sign = sign::plus;
|
||||
break;
|
||||
case '-':
|
||||
specs.sign = sign::minus;
|
||||
break;
|
||||
case ' ':
|
||||
specs.sign = sign::space;
|
||||
break;
|
||||
}
|
||||
++begin;
|
||||
break;
|
||||
case '#':
|
||||
enter_state(state::hash, is_arithmetic_type(arg_type));
|
||||
specs.alt = true;
|
||||
++begin;
|
||||
break;
|
||||
case '0':
|
||||
enter_state(state::zero);
|
||||
if (!is_arithmetic_type(arg_type))
|
||||
throw_format_error("format specifier requires numeric argument");
|
||||
if (specs.align == align::none) {
|
||||
// Ignore 0 if align is specified for compatibility with std::format.
|
||||
specs.align = align::numeric;
|
||||
specs.fill[0] = Char('0');
|
||||
}
|
||||
++begin;
|
||||
break;
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '{':
|
||||
enter_state(state::width);
|
||||
begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx);
|
||||
break;
|
||||
case '.':
|
||||
enter_state(state::precision,
|
||||
in(arg_type, float_set | string_set | cstring_set));
|
||||
begin = parse_precision(begin, end, specs.precision, specs.precision_ref,
|
||||
ctx);
|
||||
break;
|
||||
case 'L':
|
||||
enter_state(state::locale, is_arithmetic_type(arg_type));
|
||||
specs.localized = true;
|
||||
++begin;
|
||||
break;
|
||||
case 'd':
|
||||
return parse_presentation_type(pres::dec, integral_set);
|
||||
case 'o':
|
||||
return parse_presentation_type(pres::oct, integral_set);
|
||||
case 'x':
|
||||
return parse_presentation_type(pres::hex_lower, integral_set);
|
||||
case 'X':
|
||||
return parse_presentation_type(pres::hex_upper, integral_set);
|
||||
case 'b':
|
||||
return parse_presentation_type(pres::bin_lower, integral_set);
|
||||
case 'B':
|
||||
return parse_presentation_type(pres::bin_upper, integral_set);
|
||||
case 'a':
|
||||
return parse_presentation_type(pres::hexfloat_lower, float_set);
|
||||
case 'A':
|
||||
return parse_presentation_type(pres::hexfloat_upper, float_set);
|
||||
case 'e':
|
||||
return parse_presentation_type(pres::exp_lower, float_set);
|
||||
case 'E':
|
||||
return parse_presentation_type(pres::exp_upper, float_set);
|
||||
case 'f':
|
||||
return parse_presentation_type(pres::fixed_lower, float_set);
|
||||
case 'F':
|
||||
return parse_presentation_type(pres::fixed_upper, float_set);
|
||||
case 'g':
|
||||
return parse_presentation_type(pres::general_lower, float_set);
|
||||
case 'G':
|
||||
return parse_presentation_type(pres::general_upper, float_set);
|
||||
case 'c':
|
||||
return parse_presentation_type(pres::chr, integral_set);
|
||||
case 's':
|
||||
return parse_presentation_type(pres::string,
|
||||
bool_set | string_set | cstring_set);
|
||||
case 'p':
|
||||
return parse_presentation_type(pres::pointer, pointer_set | cstring_set);
|
||||
case '?':
|
||||
return parse_presentation_type(pres::debug,
|
||||
char_set | string_set | cstring_set);
|
||||
case '}':
|
||||
return begin;
|
||||
default: {
|
||||
if (*begin == '}') return begin;
|
||||
// Parse fill and alignment.
|
||||
auto fill_end = begin + code_point_length(begin);
|
||||
if (end - fill_end <= 0) {
|
||||
throw_format_error("invalid format specifier");
|
||||
return begin;
|
||||
}
|
||||
if (*begin == '{') {
|
||||
throw_format_error("invalid fill character '{'");
|
||||
return begin;
|
||||
}
|
||||
auto align = parse_align(to_ascii(*fill_end));
|
||||
enter_state(state::align, align != align::none);
|
||||
specs.fill = {begin, to_unsigned(fill_end - begin)};
|
||||
specs.align = align;
|
||||
begin = fill_end + 1;
|
||||
}
|
||||
}
|
||||
if (begin == end) return begin;
|
||||
c = to_ascii(*begin);
|
||||
}
|
||||
return begin;
|
||||
}
|
||||
|
||||
template <typename Char, typename Handler>
|
||||
@@ -2575,14 +2462,11 @@ FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end,
|
||||
Handler& handler;
|
||||
int arg_id;
|
||||
|
||||
FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); }
|
||||
FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); }
|
||||
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
|
||||
FMT_CONSTEXPR void on_auto() { arg_id = handler.on_arg_id(); }
|
||||
FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); }
|
||||
FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
|
||||
arg_id = handler.on_arg_id(id);
|
||||
}
|
||||
FMT_CONSTEXPR void on_error(const char* message) {
|
||||
if (message) handler.on_error(message);
|
||||
}
|
||||
};
|
||||
|
||||
++begin;
|
||||
@@ -2611,9 +2495,6 @@ FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end,
|
||||
template <bool IS_CONSTEXPR, typename Char, typename Handler>
|
||||
FMT_CONSTEXPR FMT_INLINE void parse_format_string(
|
||||
basic_string_view<Char> format_str, Handler&& handler) {
|
||||
// Workaround a name-lookup bug in MSVC's modules implementation.
|
||||
using detail::find;
|
||||
|
||||
auto begin = format_str.data();
|
||||
auto end = begin + format_str.size();
|
||||
if (end - begin < 32) {
|
||||
@@ -2635,21 +2516,21 @@ FMT_CONSTEXPR FMT_INLINE void parse_format_string(
|
||||
return;
|
||||
}
|
||||
struct writer {
|
||||
FMT_CONSTEXPR void operator()(const Char* pbegin, const Char* pend) {
|
||||
if (pbegin == pend) return;
|
||||
FMT_CONSTEXPR void operator()(const Char* from, const Char* to) {
|
||||
if (from == to) return;
|
||||
for (;;) {
|
||||
const Char* p = nullptr;
|
||||
if (!find<IS_CONSTEXPR>(pbegin, pend, Char('}'), p))
|
||||
return handler_.on_text(pbegin, pend);
|
||||
if (!find<IS_CONSTEXPR>(from, to, Char('}'), p))
|
||||
return handler_.on_text(from, to);
|
||||
++p;
|
||||
if (p == pend || *p != '}')
|
||||
if (p == to || *p != '}')
|
||||
return handler_.on_error("unmatched '}' in format string");
|
||||
handler_.on_text(pbegin, p);
|
||||
pbegin = p + 1;
|
||||
handler_.on_text(from, p);
|
||||
from = p + 1;
|
||||
}
|
||||
}
|
||||
Handler& handler_;
|
||||
} write{handler};
|
||||
} write = {handler};
|
||||
while (begin != end) {
|
||||
// Doing two passes with memchr (one for '{' and another for '}') is up to
|
||||
// 2.5x faster than the naive one-pass implementation on big format strings.
|
||||
@@ -2661,6 +2542,13 @@ FMT_CONSTEXPR FMT_INLINE void parse_format_string(
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, bool = is_named_arg<T>::value> struct strip_named_arg {
|
||||
using type = T;
|
||||
};
|
||||
template <typename T> struct strip_named_arg<T, true> {
|
||||
using type = remove_cvref_t<decltype(T::value)>;
|
||||
};
|
||||
|
||||
template <typename T, typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
|
||||
-> decltype(ctx.begin()) {
|
||||
@@ -2668,206 +2556,30 @@ FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
|
||||
using context = buffer_context<char_type>;
|
||||
using mapped_type = conditional_t<
|
||||
mapped_type_constant<T, context>::value != type::custom_type,
|
||||
decltype(arg_mapper<context>().map(std::declval<const T&>())), T>;
|
||||
auto f = conditional_t<has_formatter<mapped_type, context>::value,
|
||||
formatter<mapped_type, char_type>,
|
||||
fallback_formatter<T, char_type>>();
|
||||
return f.parse(ctx);
|
||||
decltype(arg_mapper<context>().map(std::declval<const T&>())),
|
||||
typename strip_named_arg<T>::type>;
|
||||
return formatter<mapped_type, char_type>().parse(ctx);
|
||||
}
|
||||
|
||||
// A parse context with extra argument id checks. It is only used at compile
|
||||
// time because adding checks at runtime would introduce substantial overhead
|
||||
// and would be redundant since argument ids are checked when arguments are
|
||||
// retrieved anyway.
|
||||
template <typename Char, typename ErrorHandler = error_handler>
|
||||
class compile_parse_context
|
||||
: public basic_format_parse_context<Char, ErrorHandler> {
|
||||
private:
|
||||
int num_args_;
|
||||
using base = basic_format_parse_context<Char, ErrorHandler>;
|
||||
|
||||
public:
|
||||
explicit FMT_CONSTEXPR compile_parse_context(
|
||||
basic_string_view<Char> format_str,
|
||||
int num_args = (std::numeric_limits<int>::max)(), ErrorHandler eh = {})
|
||||
: base(format_str, eh), num_args_(num_args) {}
|
||||
|
||||
FMT_CONSTEXPR auto next_arg_id() -> int {
|
||||
int id = base::next_arg_id();
|
||||
if (id >= num_args_) this->on_error("argument not found");
|
||||
return id;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void check_arg_id(int id) {
|
||||
base::check_arg_id(id);
|
||||
if (id >= num_args_) this->on_error("argument not found");
|
||||
}
|
||||
using base::check_arg_id;
|
||||
};
|
||||
|
||||
template <typename ErrorHandler>
|
||||
FMT_CONSTEXPR void check_int_type_spec(presentation_type type,
|
||||
ErrorHandler&& eh) {
|
||||
if (type > presentation_type::bin_upper && type != presentation_type::chr)
|
||||
eh.on_error("invalid type specifier");
|
||||
}
|
||||
|
||||
// Checks char specs and returns true if the type spec is char (and not int).
|
||||
template <typename Char, typename ErrorHandler = error_handler>
|
||||
FMT_CONSTEXPR auto check_char_specs(const basic_format_specs<Char>& specs,
|
||||
ErrorHandler&& eh = {}) -> bool {
|
||||
// Checks char specs and returns true iff the presentation type is char-like.
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR auto check_char_specs(const format_specs<Char>& specs) -> bool {
|
||||
if (specs.type != presentation_type::none &&
|
||||
specs.type != presentation_type::chr) {
|
||||
check_int_type_spec(specs.type, eh);
|
||||
specs.type != presentation_type::chr &&
|
||||
specs.type != presentation_type::debug) {
|
||||
return false;
|
||||
}
|
||||
if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
|
||||
eh.on_error("invalid format specifier for char");
|
||||
throw_format_error("invalid format specifier for char");
|
||||
return true;
|
||||
}
|
||||
|
||||
// A floating-point presentation format.
|
||||
enum class float_format : unsigned char {
|
||||
general, // General: exponent notation or fixed point based on magnitude.
|
||||
exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3.
|
||||
fixed, // Fixed point with the default precision of 6, e.g. 0.0012.
|
||||
hex
|
||||
};
|
||||
constexpr FMT_INLINE_VARIABLE int invalid_arg_index = -1;
|
||||
|
||||
struct float_specs {
|
||||
int precision;
|
||||
float_format format : 8;
|
||||
sign_t sign : 8;
|
||||
bool upper : 1;
|
||||
bool locale : 1;
|
||||
bool binary32 : 1;
|
||||
bool fallback : 1;
|
||||
bool showpoint : 1;
|
||||
};
|
||||
|
||||
template <typename ErrorHandler = error_handler, typename Char>
|
||||
FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs<Char>& specs,
|
||||
ErrorHandler&& eh = {})
|
||||
-> float_specs {
|
||||
auto result = float_specs();
|
||||
result.showpoint = specs.alt;
|
||||
result.locale = specs.localized;
|
||||
switch (specs.type) {
|
||||
case presentation_type::none:
|
||||
result.format = float_format::general;
|
||||
break;
|
||||
case presentation_type::general_upper:
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case presentation_type::general_lower:
|
||||
result.format = float_format::general;
|
||||
break;
|
||||
case presentation_type::exp_upper:
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case presentation_type::exp_lower:
|
||||
result.format = float_format::exp;
|
||||
result.showpoint |= specs.precision != 0;
|
||||
break;
|
||||
case presentation_type::fixed_upper:
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case presentation_type::fixed_lower:
|
||||
result.format = float_format::fixed;
|
||||
result.showpoint |= specs.precision != 0;
|
||||
break;
|
||||
case presentation_type::hexfloat_upper:
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case presentation_type::hexfloat_lower:
|
||||
result.format = float_format::hex;
|
||||
break;
|
||||
default:
|
||||
eh.on_error("invalid type specifier");
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename ErrorHandler = error_handler>
|
||||
FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type,
|
||||
ErrorHandler&& eh = {}) -> bool {
|
||||
if (type == presentation_type::none || type == presentation_type::string)
|
||||
return true;
|
||||
if (type != presentation_type::pointer) eh.on_error("invalid type specifier");
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename ErrorHandler = error_handler>
|
||||
FMT_CONSTEXPR void check_string_type_spec(presentation_type type,
|
||||
ErrorHandler&& eh = {}) {
|
||||
if (type != presentation_type::none && type != presentation_type::string)
|
||||
eh.on_error("invalid type specifier");
|
||||
}
|
||||
|
||||
template <typename ErrorHandler>
|
||||
FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type,
|
||||
ErrorHandler&& eh) {
|
||||
if (type != presentation_type::none && type != presentation_type::pointer)
|
||||
eh.on_error("invalid type specifier");
|
||||
}
|
||||
|
||||
// A parse_format_specs handler that checks if specifiers are consistent with
|
||||
// the argument type.
|
||||
template <typename Handler> class specs_checker : public Handler {
|
||||
private:
|
||||
detail::type arg_type_;
|
||||
|
||||
FMT_CONSTEXPR void require_numeric_argument() {
|
||||
if (!is_arithmetic_type(arg_type_))
|
||||
this->on_error("format specifier requires numeric argument");
|
||||
}
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type)
|
||||
: Handler(handler), arg_type_(arg_type) {}
|
||||
|
||||
FMT_CONSTEXPR void on_align(align_t align) {
|
||||
if (align == align::numeric) require_numeric_argument();
|
||||
Handler::on_align(align);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_sign(sign_t s) {
|
||||
require_numeric_argument();
|
||||
if (is_integral_type(arg_type_) && arg_type_ != type::int_type &&
|
||||
arg_type_ != type::long_long_type && arg_type_ != type::char_type) {
|
||||
this->on_error("format specifier requires signed argument");
|
||||
}
|
||||
Handler::on_sign(s);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_hash() {
|
||||
require_numeric_argument();
|
||||
Handler::on_hash();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_localized() {
|
||||
require_numeric_argument();
|
||||
Handler::on_localized();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_zero() {
|
||||
require_numeric_argument();
|
||||
Handler::on_zero();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void end_precision() {
|
||||
if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type)
|
||||
this->on_error("precision not allowed for this argument type");
|
||||
}
|
||||
};
|
||||
|
||||
constexpr int invalid_arg_index = -1;
|
||||
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
template <int N, typename T, typename... Args, typename Char>
|
||||
constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
|
||||
if constexpr (detail::is_statically_named_arg<T>()) {
|
||||
if constexpr (is_statically_named_arg<T>()) {
|
||||
if (name == T::name) return N;
|
||||
}
|
||||
if constexpr (sizeof...(Args) > 0)
|
||||
@@ -2879,7 +2591,7 @@ constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
|
||||
|
||||
template <typename... Args, typename Char>
|
||||
FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
if constexpr (sizeof...(Args) > 0)
|
||||
return get_arg_index_by_name<0, Args...>(name);
|
||||
#endif
|
||||
@@ -2887,23 +2599,26 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
|
||||
return invalid_arg_index;
|
||||
}
|
||||
|
||||
template <typename Char, typename ErrorHandler, typename... Args>
|
||||
class format_string_checker {
|
||||
template <typename Char, typename... Args> class format_string_checker {
|
||||
private:
|
||||
using parse_context_type = compile_parse_context<Char, ErrorHandler>;
|
||||
enum { num_args = sizeof...(Args) };
|
||||
using parse_context_type = compile_parse_context<Char>;
|
||||
static constexpr int num_args = sizeof...(Args);
|
||||
|
||||
// Format specifier parsing function.
|
||||
// In the future basic_format_parse_context will replace compile_parse_context
|
||||
// here and will use is_constant_evaluated and downcasting to access the data
|
||||
// needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1.
|
||||
using parse_func = const Char* (*)(parse_context_type&);
|
||||
|
||||
parse_context_type context_;
|
||||
parse_func parse_funcs_[num_args > 0 ? num_args : 1];
|
||||
parse_func parse_funcs_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
|
||||
type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
|
||||
|
||||
public:
|
||||
explicit FMT_CONSTEXPR format_string_checker(
|
||||
basic_string_view<Char> format_str, ErrorHandler eh)
|
||||
: context_(format_str, num_args, eh),
|
||||
parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
|
||||
explicit FMT_CONSTEXPR format_string_checker(basic_string_view<Char> fmt)
|
||||
: context_(fmt, num_args, types_),
|
||||
parse_funcs_{&parse_format_specs<Args, parse_context_type>...},
|
||||
types_{mapped_type_constant<Args, buffer_context<Char>>::value...} {}
|
||||
|
||||
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
|
||||
|
||||
@@ -2912,10 +2627,10 @@ class format_string_checker {
|
||||
return context_.check_arg_id(id), id;
|
||||
}
|
||||
FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
auto index = get_arg_index_by_name<Args...>(id);
|
||||
if (index == invalid_arg_index) on_error("named argument is not found");
|
||||
return context_.check_arg_id(index), index;
|
||||
return index;
|
||||
#else
|
||||
(void)id;
|
||||
on_error("compile-time checks for named arguments require C++20 support");
|
||||
@@ -2927,41 +2642,55 @@ class format_string_checker {
|
||||
|
||||
FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*)
|
||||
-> const Char* {
|
||||
context_.advance_to(context_.begin() + (begin - &*context_.begin()));
|
||||
context_.advance_to(begin);
|
||||
// id >= 0 check is a workaround for gcc 10 bug (#2065).
|
||||
return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_error(const char* message) {
|
||||
context_.on_error(message);
|
||||
throw_format_error(message);
|
||||
}
|
||||
};
|
||||
|
||||
// Reports a compile-time error if S is not a valid format string.
|
||||
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
|
||||
FMT_INLINE void check_format_string(const S&) {
|
||||
#ifdef FMT_ENFORCE_COMPILE_STRING
|
||||
static_assert(is_compile_string<S>::value,
|
||||
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
|
||||
"FMT_STRING.");
|
||||
#endif
|
||||
}
|
||||
template <typename... Args, typename S,
|
||||
enable_if_t<(is_compile_string<S>::value), int>>
|
||||
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||
void check_format_string(S format_str) {
|
||||
FMT_CONSTEXPR auto s = to_string_view(format_str);
|
||||
using checker = format_string_checker<typename S::char_type, error_handler,
|
||||
remove_cvref_t<Args>...>;
|
||||
FMT_CONSTEXPR bool invalid_format =
|
||||
(parse_format_string<true>(s, checker(s, {})), true);
|
||||
ignore_unused(invalid_format);
|
||||
using char_t = typename S::char_type;
|
||||
FMT_CONSTEXPR auto s = basic_string_view<char_t>(format_str);
|
||||
using checker = format_string_checker<char_t, remove_cvref_t<Args>...>;
|
||||
FMT_CONSTEXPR bool error = (parse_format_string<true>(s, checker(s)), true);
|
||||
ignore_unused(error);
|
||||
}
|
||||
|
||||
template <typename Char = char> struct vformat_args {
|
||||
using type = basic_format_args<
|
||||
basic_format_context<std::back_insert_iterator<buffer<Char>>, Char>>;
|
||||
};
|
||||
template <> struct vformat_args<char> { using type = format_args; };
|
||||
|
||||
// Use vformat_args and avoid type_identity to keep symbols short.
|
||||
template <typename Char>
|
||||
void vformat_to(
|
||||
buffer<Char>& buf, basic_string_view<Char> fmt,
|
||||
basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
|
||||
locale_ref loc = {});
|
||||
void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
|
||||
typename vformat_args<Char>::type args, locale_ref loc = {});
|
||||
|
||||
FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
|
||||
#ifndef _WIN32
|
||||
inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
|
||||
#endif
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
} // namespace detail
|
||||
|
||||
// A formatter specialization for the core types corresponding to detail::type
|
||||
// constants.
|
||||
FMT_BEGIN_EXPORT
|
||||
|
||||
// A formatter specialization for natively supported types.
|
||||
template <typename T, typename Char>
|
||||
struct formatter<T, Char,
|
||||
enable_if_t<detail::type_constant<T, Char>::value !=
|
||||
@@ -2970,72 +2699,21 @@ struct formatter<T, Char,
|
||||
detail::dynamic_format_specs<Char> specs_;
|
||||
|
||||
public:
|
||||
// Parses format specifiers stopping either at the end of the range or at the
|
||||
// terminating '}'.
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
auto begin = ctx.begin(), end = ctx.end();
|
||||
if (begin == end) return begin;
|
||||
using handler_type = detail::dynamic_specs_handler<ParseContext>;
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
|
||||
auto type = detail::type_constant<T, Char>::value;
|
||||
auto checker =
|
||||
detail::specs_checker<handler_type>(handler_type(specs_, ctx), type);
|
||||
auto it = detail::parse_format_specs(begin, end, checker);
|
||||
auto eh = ctx.error_handler();
|
||||
switch (type) {
|
||||
case detail::type::none_type:
|
||||
FMT_ASSERT(false, "invalid argument type");
|
||||
break;
|
||||
case detail::type::bool_type:
|
||||
if (specs_.type == presentation_type::none ||
|
||||
specs_.type == presentation_type::string) {
|
||||
break;
|
||||
}
|
||||
FMT_FALLTHROUGH;
|
||||
case detail::type::int_type:
|
||||
case detail::type::uint_type:
|
||||
case detail::type::long_long_type:
|
||||
case detail::type::ulong_long_type:
|
||||
case detail::type::int128_type:
|
||||
case detail::type::uint128_type:
|
||||
detail::check_int_type_spec(specs_.type, eh);
|
||||
break;
|
||||
case detail::type::char_type:
|
||||
detail::check_char_specs(specs_, eh);
|
||||
break;
|
||||
case detail::type::float_type:
|
||||
if (detail::const_check(FMT_USE_FLOAT))
|
||||
detail::parse_float_type_spec(specs_, eh);
|
||||
else
|
||||
FMT_ASSERT(false, "float support disabled");
|
||||
break;
|
||||
case detail::type::double_type:
|
||||
if (detail::const_check(FMT_USE_DOUBLE))
|
||||
detail::parse_float_type_spec(specs_, eh);
|
||||
else
|
||||
FMT_ASSERT(false, "double support disabled");
|
||||
break;
|
||||
case detail::type::long_double_type:
|
||||
if (detail::const_check(FMT_USE_LONG_DOUBLE))
|
||||
detail::parse_float_type_spec(specs_, eh);
|
||||
else
|
||||
FMT_ASSERT(false, "long double support disabled");
|
||||
break;
|
||||
case detail::type::cstring_type:
|
||||
detail::check_cstring_type_spec(specs_.type, eh);
|
||||
break;
|
||||
case detail::type::string_type:
|
||||
detail::check_string_type_spec(specs_.type, eh);
|
||||
break;
|
||||
case detail::type::pointer_type:
|
||||
detail::check_pointer_type_spec(specs_.type, eh);
|
||||
break;
|
||||
case detail::type::custom_type:
|
||||
// Custom format specifiers are checked in parse functions of
|
||||
// formatter specializations.
|
||||
break;
|
||||
}
|
||||
return it;
|
||||
auto end =
|
||||
detail::parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, type);
|
||||
if (type == detail::type::char_type) detail::check_char_specs(specs_);
|
||||
return end;
|
||||
}
|
||||
|
||||
template <detail::type U = detail::type_constant<T, Char>::value,
|
||||
FMT_ENABLE_IF(U == detail::type::string_type ||
|
||||
U == detail::type::cstring_type ||
|
||||
U == detail::type::char_type)>
|
||||
FMT_CONSTEXPR void set_debug_format(bool set = true) {
|
||||
specs_.type = set ? presentation_type::debug : presentation_type::none;
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
@@ -3043,7 +2721,30 @@ struct formatter<T, Char,
|
||||
-> decltype(ctx.out());
|
||||
};
|
||||
|
||||
template <typename Char> struct basic_runtime { basic_string_view<Char> str; };
|
||||
#define FMT_FORMAT_AS(Type, Base) \
|
||||
template <typename Char> \
|
||||
struct formatter<Type, Char> : formatter<Base, Char> { \
|
||||
template <typename FormatContext> \
|
||||
auto format(const Type& val, FormatContext& ctx) const \
|
||||
-> decltype(ctx.out()) { \
|
||||
return formatter<Base, Char>::format(static_cast<Base>(val), ctx); \
|
||||
} \
|
||||
}
|
||||
|
||||
FMT_FORMAT_AS(signed char, int);
|
||||
FMT_FORMAT_AS(unsigned char, unsigned);
|
||||
FMT_FORMAT_AS(short, int);
|
||||
FMT_FORMAT_AS(unsigned short, unsigned);
|
||||
FMT_FORMAT_AS(long, long long);
|
||||
FMT_FORMAT_AS(unsigned long, unsigned long long);
|
||||
FMT_FORMAT_AS(Char*, const Char*);
|
||||
FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
|
||||
FMT_FORMAT_AS(std::nullptr_t, const void*);
|
||||
FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
|
||||
|
||||
template <typename Char = char> struct runtime_format_string {
|
||||
basic_string_view<Char> str;
|
||||
};
|
||||
|
||||
/** A compile-time format string. */
|
||||
template <typename Char, typename... Args> class basic_format_string {
|
||||
@@ -3063,25 +2764,24 @@ template <typename Char, typename... Args> class basic_format_string {
|
||||
#ifdef FMT_HAS_CONSTEVAL
|
||||
if constexpr (detail::count_named_args<Args...>() ==
|
||||
detail::count_statically_named_args<Args...>()) {
|
||||
using checker = detail::format_string_checker<Char, detail::error_handler,
|
||||
remove_cvref_t<Args>...>;
|
||||
detail::parse_format_string<true>(str_, checker(s, {}));
|
||||
using checker =
|
||||
detail::format_string_checker<Char, remove_cvref_t<Args>...>;
|
||||
detail::parse_format_string<true>(str_, checker(s));
|
||||
}
|
||||
#else
|
||||
detail::check_format_string<Args...>(s);
|
||||
#endif
|
||||
}
|
||||
basic_format_string(basic_runtime<Char> r) : str_(r.str) {}
|
||||
basic_format_string(runtime_format_string<Char> fmt) : str_(fmt.str) {}
|
||||
|
||||
FMT_INLINE operator basic_string_view<Char>() const { return str_; }
|
||||
FMT_INLINE auto get() const -> basic_string_view<Char> { return str_; }
|
||||
};
|
||||
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||
// Workaround broken conversion on older gcc.
|
||||
template <typename... Args> using format_string = string_view;
|
||||
template <typename S> auto runtime(const S& s) -> basic_string_view<char_t<S>> {
|
||||
return s;
|
||||
}
|
||||
template <typename...> using format_string = string_view;
|
||||
inline auto runtime(string_view s) -> string_view { return s; }
|
||||
#else
|
||||
template <typename... Args>
|
||||
using format_string = basic_format_string<char, type_identity_t<Args>...>;
|
||||
@@ -3095,9 +2795,7 @@ using format_string = basic_format_string<char, type_identity_t<Args>...>;
|
||||
fmt::print(fmt::runtime("{:d}"), "I am not a number");
|
||||
\endrst
|
||||
*/
|
||||
template <typename S> auto runtime(const S& s) -> basic_runtime<char_t<S>> {
|
||||
return {{s}};
|
||||
}
|
||||
inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; }
|
||||
#endif
|
||||
|
||||
FMT_API auto vformat(string_view fmt, format_args args) -> std::string;
|
||||
@@ -3123,10 +2821,9 @@ FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T&&... args)
|
||||
template <typename OutputIt,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
|
||||
auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt {
|
||||
using detail::get_buffer;
|
||||
auto&& buf = get_buffer<char>(out);
|
||||
auto&& buf = detail::get_buffer<char>(out);
|
||||
detail::vformat_to(buf, fmt, args, {});
|
||||
return detail::get_iterator(buf);
|
||||
return detail::get_iterator(buf, out);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3185,7 +2882,7 @@ template <typename... T>
|
||||
FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt,
|
||||
T&&... args) -> size_t {
|
||||
auto buf = detail::counting_buffer<>();
|
||||
detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...), {});
|
||||
detail::vformat_to<char>(buf, fmt, fmt::make_format_args(args...), {});
|
||||
return buf.count();
|
||||
}
|
||||
|
||||
@@ -3226,7 +2923,25 @@ FMT_INLINE void print(std::FILE* f, format_string<T...> fmt, T&&... args) {
|
||||
: detail::vprint_mojibake(f, fmt, vargs);
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
/**
|
||||
Formats ``args`` according to specifications in ``fmt`` and writes the
|
||||
output to the file ``f`` followed by a newline.
|
||||
*/
|
||||
template <typename... T>
|
||||
FMT_INLINE void println(std::FILE* f, format_string<T...> fmt, T&&... args) {
|
||||
return fmt::print(f, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
|
||||
}
|
||||
|
||||
/**
|
||||
Formats ``args`` according to specifications in ``fmt`` and writes the output
|
||||
to ``stdout`` followed by a newline.
|
||||
*/
|
||||
template <typename... T>
|
||||
FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
|
||||
return fmt::println(stdout, fmt, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
FMT_END_EXPORT
|
||||
FMT_GCC_PRAGMA("GCC pop_options")
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
|
||||
2630
extern/fmtlib/include/fmt/format-inl.h
vendored
2630
extern/fmtlib/include/fmt/format-inl.h
vendored
@@ -9,13 +9,9 @@
|
||||
#define FMT_FORMAT_INL_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cerrno> // errno
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
#include <cstring> // std::memmove
|
||||
#include <cwchar>
|
||||
#include <exception>
|
||||
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
@@ -44,21 +40,8 @@ FMT_FUNC void throw_format_error(const char* message) {
|
||||
FMT_THROW(format_error(message));
|
||||
}
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# define FMT_SNPRINTF snprintf
|
||||
#else // _MSC_VER
|
||||
inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
# define FMT_SNPRINTF fmt_snprintf
|
||||
#endif // _MSC_VER
|
||||
|
||||
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
|
||||
string_view message) FMT_NOEXCEPT {
|
||||
string_view message) noexcept {
|
||||
// Report error code making sure that the output fits into
|
||||
// inline_buffer_size to avoid dynamic memory allocation and potential
|
||||
// bad_alloc.
|
||||
@@ -81,7 +64,7 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
|
||||
}
|
||||
|
||||
FMT_FUNC void report_error(format_func func, int error_code,
|
||||
const char* message) FMT_NOEXCEPT {
|
||||
const char* message) noexcept {
|
||||
memory_buffer full_message;
|
||||
func(full_message, error_code, message);
|
||||
// Don't use fwrite_fully because the latter may throw.
|
||||
@@ -93,7 +76,8 @@ FMT_FUNC void report_error(format_func func, int error_code,
|
||||
inline void fwrite_fully(const void* ptr, size_t size, size_t count,
|
||||
FILE* stream) {
|
||||
size_t written = std::fwrite(ptr, size, count, stream);
|
||||
if (written < count) FMT_THROW(system_error(errno, "cannot write to file"));
|
||||
if (written < count)
|
||||
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
|
||||
}
|
||||
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
@@ -127,928 +111,151 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
|
||||
return '.';
|
||||
}
|
||||
#endif
|
||||
|
||||
FMT_FUNC auto write_loc(appender out, loc_value value,
|
||||
const format_specs<>& specs, locale_ref loc) -> bool {
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
auto locale = loc.get<std::locale>();
|
||||
// We cannot use the num_put<char> facet because it may produce output in
|
||||
// a wrong encoding.
|
||||
using facet = format_facet<std::locale>;
|
||||
if (std::has_facet<facet>(locale))
|
||||
return std::use_facet<facet>(locale).put(out, value, specs);
|
||||
return facet(locale).put(out, value, specs);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
#if !FMT_MSC_VER
|
||||
FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default;
|
||||
template <typename Locale> typename Locale::id format_facet<Locale>::id;
|
||||
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) {
|
||||
auto& numpunct = std::use_facet<std::numpunct<char>>(loc);
|
||||
grouping_ = numpunct.grouping();
|
||||
if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep());
|
||||
}
|
||||
|
||||
template <>
|
||||
FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
|
||||
appender out, loc_value val, const format_specs<>& specs) const -> bool {
|
||||
return val.visit(
|
||||
detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_});
|
||||
}
|
||||
#endif
|
||||
|
||||
FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str,
|
||||
FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt,
|
||||
format_args args) {
|
||||
auto ec = std::error_code(error_code, std::generic_category());
|
||||
return std::system_error(ec, vformat(format_str, args));
|
||||
return std::system_error(ec, vformat(fmt, args));
|
||||
}
|
||||
|
||||
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<int>(sizeof(void*)) - 1;
|
||||
while (i > 0 && n.value[i] == 0) --i;
|
||||
auto char_digits = std::numeric_limits<unsigned char>::digits / 4;
|
||||
return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1;
|
||||
template <typename F> inline bool operator==(basic_fp<F> x, basic_fp<F> y) {
|
||||
return x.f == y.f && x.e == y.e;
|
||||
}
|
||||
|
||||
// log10(2) = 0x0.4d104d427de7fbcc...
|
||||
static constexpr uint64_t log10_2_significand = 0x4d104d427de7fbcc;
|
||||
|
||||
template <typename T = void> struct basic_impl_data {
|
||||
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
|
||||
// These are generated by support/compute-powers.py.
|
||||
static constexpr uint64_t pow10_significands[87] = {
|
||||
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
|
||||
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
|
||||
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
|
||||
0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
|
||||
0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
|
||||
0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
|
||||
0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
|
||||
0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
|
||||
0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
|
||||
0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
|
||||
0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
|
||||
0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
|
||||
0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
|
||||
0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
|
||||
0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
|
||||
0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
|
||||
0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
|
||||
0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
|
||||
0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
|
||||
0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
|
||||
0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
|
||||
0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
|
||||
0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
|
||||
0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
|
||||
0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
|
||||
0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
|
||||
0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
|
||||
0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
|
||||
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
|
||||
};
|
||||
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wnarrowing"
|
||||
#endif
|
||||
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
|
||||
// to significands above.
|
||||
static constexpr int16_t pow10_exponents[87] = {
|
||||
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
|
||||
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
|
||||
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
|
||||
-343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77,
|
||||
-50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216,
|
||||
242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
|
||||
534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
|
||||
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066};
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
static constexpr uint64_t power_of_10_64[20] = {
|
||||
1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL),
|
||||
10000000000000000000ULL};
|
||||
};
|
||||
|
||||
// This is a struct rather than an alias to avoid shadowing warnings in gcc.
|
||||
struct impl_data : basic_impl_data<> {};
|
||||
|
||||
#if __cplusplus < 201703L
|
||||
template <typename T>
|
||||
constexpr uint64_t basic_impl_data<T>::pow10_significands[];
|
||||
template <typename T> constexpr int16_t basic_impl_data<T>::pow10_exponents[];
|
||||
template <typename T> constexpr uint64_t basic_impl_data<T>::power_of_10_64[];
|
||||
#endif
|
||||
|
||||
template <typename T> struct bits {
|
||||
static FMT_CONSTEXPR_DECL const int value =
|
||||
static_cast<int>(sizeof(T) * std::numeric_limits<unsigned char>::digits);
|
||||
};
|
||||
|
||||
// Returns the number of significand bits in Float excluding the implicit bit.
|
||||
template <typename Float> constexpr int num_significand_bits() {
|
||||
// Subtract 1 to account for an implicit most significant bit in the
|
||||
// normalized form.
|
||||
return std::numeric_limits<Float>::digits - 1;
|
||||
// Compilers should be able to optimize this into the ror instruction.
|
||||
FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept {
|
||||
r &= 31;
|
||||
return (n >> r) | (n << (32 - r));
|
||||
}
|
||||
|
||||
// A floating-point number f * pow(2, e).
|
||||
struct fp {
|
||||
uint64_t f;
|
||||
int e;
|
||||
|
||||
static constexpr const int num_significand_bits = bits<decltype(f)>::value;
|
||||
|
||||
constexpr fp() : f(0), e(0) {}
|
||||
constexpr fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
|
||||
|
||||
// Constructs fp from an IEEE754 floating-point number. It is a template to
|
||||
// prevent compile errors on systems where n is not IEEE754.
|
||||
template <typename Float> explicit FMT_CONSTEXPR fp(Float n) { assign(n); }
|
||||
|
||||
template <typename Float>
|
||||
using is_supported = bool_constant<sizeof(Float) == sizeof(uint64_t) ||
|
||||
sizeof(Float) == sizeof(uint32_t)>;
|
||||
|
||||
// Assigns d to this and return true iff predecessor is closer than successor.
|
||||
template <typename Float, FMT_ENABLE_IF(is_supported<Float>::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<Float>();
|
||||
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<conditional_t<is_double, uint64_t, uint32_t>>(n);
|
||||
f = u & significand_mask;
|
||||
const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask;
|
||||
int biased_e =
|
||||
static_cast<int>((u & exponent_mask) >> num_float_significand_bits);
|
||||
// The predecessor is closer if n is a normalized power of 2 (f == 0) other
|
||||
// than the smallest normalized number (biased_e > 1).
|
||||
bool is_predecessor_closer = f == 0 && biased_e > 1;
|
||||
if (biased_e != 0)
|
||||
f += implicit_bit;
|
||||
else
|
||||
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
|
||||
const int exponent_bias = std::numeric_limits<Float>::max_exponent - 1;
|
||||
e = biased_e - exponent_bias - num_float_significand_bits;
|
||||
return is_predecessor_closer;
|
||||
}
|
||||
|
||||
template <typename Float, FMT_ENABLE_IF(!is_supported<Float>::value)>
|
||||
bool assign(Float) {
|
||||
FMT_ASSERT(false, "");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
|
||||
template <int SHIFT = 0> FMT_CONSTEXPR fp normalize(fp value) {
|
||||
// Handle subnormals.
|
||||
const uint64_t implicit_bit = 1ULL << num_significand_bits<double>();
|
||||
const auto shifted_implicit_bit = implicit_bit << SHIFT;
|
||||
while ((value.f & shifted_implicit_bit) == 0) {
|
||||
value.f <<= 1;
|
||||
--value.e;
|
||||
}
|
||||
// Subtract 1 to account for hidden bit.
|
||||
const auto offset =
|
||||
fp::num_significand_bits - num_significand_bits<double>() - SHIFT - 1;
|
||||
value.f <<= offset;
|
||||
value.e -= offset;
|
||||
return value;
|
||||
FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept {
|
||||
r &= 63;
|
||||
return (n >> r) | (n << (64 - r));
|
||||
}
|
||||
|
||||
inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
|
||||
|
||||
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
|
||||
FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
|
||||
#if FMT_USE_INT128
|
||||
auto product = static_cast<__uint128_t>(lhs) * rhs;
|
||||
auto f = static_cast<uint64_t>(product >> 64);
|
||||
return (static_cast<uint64_t>(product) & (1ULL << 63)) != 0 ? f + 1 : f;
|
||||
#else
|
||||
// Multiply 32-bit parts of significands.
|
||||
uint64_t mask = (1ULL << 32) - 1;
|
||||
uint64_t a = lhs >> 32, b = lhs & mask;
|
||||
uint64_t c = rhs >> 32, d = rhs & mask;
|
||||
uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
|
||||
// Compute mid 64-bit of result and round.
|
||||
uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
|
||||
return ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
|
||||
#endif
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR inline fp operator*(fp x, fp y) {
|
||||
return {multiply(x.f, y.f), x.e + y.e + 64};
|
||||
}
|
||||
|
||||
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
|
||||
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
|
||||
FMT_CONSTEXPR inline fp get_cached_power(int min_exponent,
|
||||
int& pow10_exponent) {
|
||||
const int shift = 32;
|
||||
const auto significand = static_cast<int64_t>(log10_2_significand);
|
||||
int index = static_cast<int>(
|
||||
((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) +
|
||||
((int64_t(1) << shift) - 1)) // ceil
|
||||
>> 32 // arithmetic shift
|
||||
);
|
||||
// Decimal exponent of the first (smallest) cached power of 10.
|
||||
const int first_dec_exp = -348;
|
||||
// Difference between 2 consecutive decimal exponents in cached powers of 10.
|
||||
const int dec_exp_step = 8;
|
||||
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
|
||||
pow10_exponent = first_dec_exp + index * dec_exp_step;
|
||||
return {impl_data::pow10_significands[index],
|
||||
impl_data::pow10_exponents[index]};
|
||||
}
|
||||
|
||||
// A simple accumulator to hold the sums of terms in bigint::square if uint128_t
|
||||
// is not available.
|
||||
struct accumulator {
|
||||
uint64_t lower;
|
||||
uint64_t upper;
|
||||
|
||||
constexpr accumulator() : lower(0), upper(0) {}
|
||||
constexpr explicit operator uint32_t() const {
|
||||
return static_cast<uint32_t>(lower);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void operator+=(uint64_t n) {
|
||||
lower += n;
|
||||
if (lower < n) ++upper;
|
||||
}
|
||||
FMT_CONSTEXPR void operator>>=(int shift) {
|
||||
FMT_ASSERT(shift == 32, "");
|
||||
(void)shift;
|
||||
lower = (upper << 32) | (lower >> 32);
|
||||
upper >>= 32;
|
||||
}
|
||||
};
|
||||
|
||||
class bigint {
|
||||
private:
|
||||
// A bigint is stored as an array of bigits (big digits), with bigit at index
|
||||
// 0 being the least significant one.
|
||||
using bigit = uint32_t;
|
||||
using double_bigit = uint64_t;
|
||||
enum { bigits_capacity = 32 };
|
||||
basic_memory_buffer<bigit, bigits_capacity> bigits_;
|
||||
int exp_;
|
||||
|
||||
FMT_CONSTEXPR20 bigit operator[](int index) const {
|
||||
return bigits_[to_unsigned(index)];
|
||||
}
|
||||
FMT_CONSTEXPR20 bigit& operator[](int index) {
|
||||
return bigits_[to_unsigned(index)];
|
||||
}
|
||||
|
||||
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
|
||||
|
||||
friend struct formatter<bigint>;
|
||||
|
||||
FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) {
|
||||
auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
|
||||
(*this)[index] = static_cast<bigit>(result);
|
||||
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 void remove_leading_zeros() {
|
||||
int num_bigits = static_cast<int>(bigits_.size()) - 1;
|
||||
while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
|
||||
bigits_.resize(to_unsigned(num_bigits + 1));
|
||||
}
|
||||
|
||||
// Computes *this -= other assuming aligned bigints and *this >= other.
|
||||
FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) {
|
||||
FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
|
||||
FMT_ASSERT(compare(*this, other) >= 0, "");
|
||||
bigit borrow = 0;
|
||||
int i = other.exp_ - exp_;
|
||||
for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j)
|
||||
subtract_bigits(i, other.bigits_[j], borrow);
|
||||
while (borrow > 0) subtract_bigits(i, 0, borrow);
|
||||
remove_leading_zeros();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 void multiply(uint32_t value) {
|
||||
const double_bigit wide_value = value;
|
||||
bigit carry = 0;
|
||||
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
|
||||
double_bigit result = bigits_[i] * wide_value + carry;
|
||||
bigits_[i] = static_cast<bigit>(result);
|
||||
carry = static_cast<bigit>(result >> bigit_bits);
|
||||
}
|
||||
if (carry != 0) bigits_.push_back(carry);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 void multiply(uint64_t value) {
|
||||
const bigit mask = ~bigit(0);
|
||||
const double_bigit lower = value & mask;
|
||||
const double_bigit upper = value >> bigit_bits;
|
||||
double_bigit carry = 0;
|
||||
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
|
||||
double_bigit result = bigits_[i] * lower + (carry & mask);
|
||||
carry =
|
||||
bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits);
|
||||
bigits_[i] = static_cast<bigit>(result);
|
||||
}
|
||||
while (carry != 0) {
|
||||
bigits_.push_back(carry & mask);
|
||||
carry >>= bigit_bits;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR20 bigint() : exp_(0) {}
|
||||
explicit bigint(uint64_t n) { assign(n); }
|
||||
FMT_CONSTEXPR20 ~bigint() {
|
||||
FMT_ASSERT(bigits_.capacity() <= bigits_capacity, "");
|
||||
}
|
||||
|
||||
bigint(const bigint&) = delete;
|
||||
void operator=(const bigint&) = delete;
|
||||
|
||||
FMT_CONSTEXPR20 void assign(const bigint& other) {
|
||||
auto size = other.bigits_.size();
|
||||
bigits_.resize(size);
|
||||
auto data = other.bigits_.data();
|
||||
std::copy(data, data + size, make_checked(bigits_.data(), size));
|
||||
exp_ = other.exp_;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 void assign(uint64_t n) {
|
||||
size_t num_bigits = 0;
|
||||
do {
|
||||
bigits_[num_bigits++] = n & ~bigit(0);
|
||||
n >>= bigit_bits;
|
||||
} while (n != 0);
|
||||
bigits_.resize(num_bigits);
|
||||
exp_ = 0;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 int num_bigits() const {
|
||||
return static_cast<int>(bigits_.size()) + exp_;
|
||||
}
|
||||
|
||||
FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) {
|
||||
FMT_ASSERT(shift >= 0, "");
|
||||
exp_ += shift / bigit_bits;
|
||||
shift %= bigit_bits;
|
||||
if (shift == 0) return *this;
|
||||
bigit carry = 0;
|
||||
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
|
||||
bigit c = bigits_[i] >> (bigit_bits - shift);
|
||||
bigits_[i] = (bigits_[i] << shift) + carry;
|
||||
carry = c;
|
||||
}
|
||||
if (carry != 0) bigits_.push_back(carry);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Int> FMT_CONSTEXPR20 bigint& operator*=(Int value) {
|
||||
FMT_ASSERT(value > 0, "");
|
||||
multiply(uint32_or_64_or_128_t<Int>(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) {
|
||||
int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
|
||||
if (num_lhs_bigits != num_rhs_bigits)
|
||||
return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
|
||||
int i = static_cast<int>(lhs.bigits_.size()) - 1;
|
||||
int j = static_cast<int>(rhs.bigits_.size()) - 1;
|
||||
int end = i - j;
|
||||
if (end < 0) end = 0;
|
||||
for (; i >= end; --i, --j) {
|
||||
bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
|
||||
if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
|
||||
}
|
||||
if (i != j) return i > j ? 1 : -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns compare(lhs1 + lhs2, rhs).
|
||||
friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2,
|
||||
const bigint& rhs) {
|
||||
int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits());
|
||||
int num_rhs_bigits = rhs.num_bigits();
|
||||
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
|
||||
if (max_lhs_bigits > num_rhs_bigits) return 1;
|
||||
auto get_bigit = [](const bigint& n, int i) -> bigit {
|
||||
return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
|
||||
};
|
||||
double_bigit borrow = 0;
|
||||
int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
|
||||
for (int i = num_rhs_bigits - 1; i >= min_exp; --i) {
|
||||
double_bigit sum =
|
||||
static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
|
||||
bigit rhs_bigit = get_bigit(rhs, i);
|
||||
if (sum > rhs_bigit + borrow) return 1;
|
||||
borrow = rhs_bigit + borrow - sum;
|
||||
if (borrow > 1) return -1;
|
||||
borrow <<= bigit_bits;
|
||||
}
|
||||
return borrow != 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
// Assigns pow(10, exp) to this bigint.
|
||||
FMT_CONSTEXPR20 void assign_pow10(int exp) {
|
||||
FMT_ASSERT(exp >= 0, "");
|
||||
if (exp == 0) return assign(1);
|
||||
// Find the top bit.
|
||||
int bitmask = 1;
|
||||
while (exp >= bitmask) bitmask <<= 1;
|
||||
bitmask >>= 1;
|
||||
// pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
|
||||
// repeated squaring and multiplication.
|
||||
assign(5);
|
||||
bitmask >>= 1;
|
||||
while (bitmask != 0) {
|
||||
square();
|
||||
if ((exp & bitmask) != 0) *this *= 5;
|
||||
bitmask >>= 1;
|
||||
}
|
||||
*this <<= exp; // Multiply by pow(2, exp) by shifting.
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 void square() {
|
||||
int num_bigits = static_cast<int>(bigits_.size());
|
||||
int num_result_bigits = 2 * num_bigits;
|
||||
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
|
||||
bigits_.resize(to_unsigned(num_result_bigits));
|
||||
using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
|
||||
auto sum = accumulator_t();
|
||||
for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
|
||||
// Compute bigit at position bigit_index of the result by adding
|
||||
// cross-product terms n[i] * n[j] such that i + j == bigit_index.
|
||||
for (int i = 0, j = bigit_index; j >= 0; ++i, --j) {
|
||||
// Most terms are multiplied twice which can be optimized in the future.
|
||||
sum += static_cast<double_bigit>(n[i]) * n[j];
|
||||
}
|
||||
(*this)[bigit_index] = static_cast<bigit>(sum);
|
||||
sum >>= bits<bigit>::value; // Compute the carry.
|
||||
}
|
||||
// Do the same for the top half.
|
||||
for (int bigit_index = num_bigits; bigit_index < num_result_bigits;
|
||||
++bigit_index) {
|
||||
for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
|
||||
sum += static_cast<double_bigit>(n[i++]) * n[j--];
|
||||
(*this)[bigit_index] = static_cast<bigit>(sum);
|
||||
sum >>= bits<bigit>::value;
|
||||
}
|
||||
remove_leading_zeros();
|
||||
exp_ *= 2;
|
||||
}
|
||||
|
||||
// If this bigint has a bigger exponent than other, adds trailing zero to make
|
||||
// exponents equal. This simplifies some operations such as subtraction.
|
||||
FMT_CONSTEXPR20 void align(const bigint& other) {
|
||||
int exp_difference = exp_ - other.exp_;
|
||||
if (exp_difference <= 0) return;
|
||||
int num_bigits = static_cast<int>(bigits_.size());
|
||||
bigits_.resize(to_unsigned(num_bigits + exp_difference));
|
||||
for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
|
||||
bigits_[j] = bigits_[i];
|
||||
std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
|
||||
exp_ -= exp_difference;
|
||||
}
|
||||
|
||||
// Divides this bignum by divisor, assigning the remainder to this and
|
||||
// returning the quotient.
|
||||
FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) {
|
||||
FMT_ASSERT(this != &divisor, "");
|
||||
if (compare(*this, divisor) < 0) return 0;
|
||||
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
|
||||
align(divisor);
|
||||
int quotient = 0;
|
||||
do {
|
||||
subtract_aligned(divisor);
|
||||
++quotient;
|
||||
} while (compare(*this, divisor) >= 0);
|
||||
return quotient;
|
||||
}
|
||||
};
|
||||
|
||||
enum class round_direction { unknown, up, down };
|
||||
|
||||
// Given the divisor (normally a power of 10), the remainder = v % divisor for
|
||||
// some number v and the error, returns whether v should be rounded up, down, or
|
||||
// whether the rounding direction can't be determined due to error.
|
||||
// error should be less than divisor / 2.
|
||||
FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor,
|
||||
uint64_t remainder,
|
||||
uint64_t error) {
|
||||
FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow.
|
||||
FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow.
|
||||
FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
|
||||
// Round down if (remainder + error) * 2 <= divisor.
|
||||
if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
|
||||
return round_direction::down;
|
||||
// Round up if (remainder - error) * 2 >= divisor.
|
||||
if (remainder >= error &&
|
||||
remainder - error >= divisor - (remainder - error)) {
|
||||
return round_direction::up;
|
||||
}
|
||||
return round_direction::unknown;
|
||||
}
|
||||
|
||||
namespace digits {
|
||||
enum result {
|
||||
more, // Generate more digits.
|
||||
done, // Done generating digits.
|
||||
error // Digit generation cancelled due to an error.
|
||||
};
|
||||
}
|
||||
|
||||
struct gen_digits_handler {
|
||||
char* buf;
|
||||
int size;
|
||||
int precision;
|
||||
int exp10;
|
||||
bool fixed;
|
||||
|
||||
FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor,
|
||||
uint64_t remainder, uint64_t error,
|
||||
bool integral) {
|
||||
FMT_ASSERT(remainder < divisor, "");
|
||||
buf[size++] = digit;
|
||||
if (!integral && error >= remainder) return digits::error;
|
||||
if (size < precision) return digits::more;
|
||||
if (!integral) {
|
||||
// Check if error * 2 < divisor with overflow prevention.
|
||||
// The check is not needed for the integral part because error = 1
|
||||
// and divisor > (1 << 32) there.
|
||||
if (error >= divisor || error >= divisor - error) return digits::error;
|
||||
} else {
|
||||
FMT_ASSERT(error == 1 && divisor > 2, "");
|
||||
}
|
||||
auto dir = get_round_direction(divisor, remainder, error);
|
||||
if (dir != round_direction::up)
|
||||
return dir == round_direction::down ? digits::done : digits::error;
|
||||
++buf[size - 1];
|
||||
for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
|
||||
buf[i] = '0';
|
||||
++buf[i - 1];
|
||||
}
|
||||
if (buf[0] > '9') {
|
||||
buf[0] = '1';
|
||||
if (fixed)
|
||||
buf[size++] = '0';
|
||||
else
|
||||
++exp10;
|
||||
}
|
||||
return digits::done;
|
||||
}
|
||||
};
|
||||
|
||||
// Generates output using the Grisu digit-gen algorithm.
|
||||
// error: the size of the region (lower, upper) outside of which numbers
|
||||
// definitely do not round to value (Delta in Grisu3).
|
||||
FMT_INLINE FMT_CONSTEXPR20 digits::result grisu_gen_digits(
|
||||
fp value, uint64_t error, int& exp, gen_digits_handler& handler) {
|
||||
const fp one(1ULL << -value.e, value.e);
|
||||
// The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
|
||||
// zero because it contains a product of two 64-bit numbers with MSB set (due
|
||||
// to normalization) - 1, shifted right by at most 60 bits.
|
||||
auto integral = static_cast<uint32_t>(value.f >> -one.e);
|
||||
FMT_ASSERT(integral != 0, "");
|
||||
FMT_ASSERT(integral == value.f >> -one.e, "");
|
||||
// The fractional part of scaled value (p2 in Grisu) c = value % one.
|
||||
uint64_t fractional = value.f & (one.f - 1);
|
||||
exp = count_digits(integral); // kappa in Grisu.
|
||||
// Non-fixed formats require at least one digit and no precision adjustment.
|
||||
if (handler.fixed) {
|
||||
// Adjust 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<int>() - precision_offset) {
|
||||
FMT_THROW(format_error("number is too big"));
|
||||
}
|
||||
handler.precision += precision_offset;
|
||||
// Check if precision is satisfied just by leading zeros, e.g.
|
||||
// format("{:.2f}", 0.001) gives "0.00" without generating any digits.
|
||||
if (handler.precision <= 0) {
|
||||
if (handler.precision < 0) return digits::done;
|
||||
// Divide by 10 to prevent overflow.
|
||||
uint64_t divisor = impl_data::power_of_10_64[exp - 1] << -one.e;
|
||||
auto dir = get_round_direction(divisor, value.f / 10, error * 10);
|
||||
if (dir == round_direction::unknown) return digits::error;
|
||||
handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0';
|
||||
return digits::done;
|
||||
}
|
||||
}
|
||||
// Generate digits for the integral part. This can produce up to 10 digits.
|
||||
do {
|
||||
uint32_t digit = 0;
|
||||
auto divmod_integral = [&](uint32_t divisor) {
|
||||
digit = integral / divisor;
|
||||
integral %= divisor;
|
||||
};
|
||||
// This optimization by Milo Yip reduces the number of integer divisions by
|
||||
// one per iteration.
|
||||
switch (exp) {
|
||||
case 10:
|
||||
divmod_integral(1000000000);
|
||||
break;
|
||||
case 9:
|
||||
divmod_integral(100000000);
|
||||
break;
|
||||
case 8:
|
||||
divmod_integral(10000000);
|
||||
break;
|
||||
case 7:
|
||||
divmod_integral(1000000);
|
||||
break;
|
||||
case 6:
|
||||
divmod_integral(100000);
|
||||
break;
|
||||
case 5:
|
||||
divmod_integral(10000);
|
||||
break;
|
||||
case 4:
|
||||
divmod_integral(1000);
|
||||
break;
|
||||
case 3:
|
||||
divmod_integral(100);
|
||||
break;
|
||||
case 2:
|
||||
divmod_integral(10);
|
||||
break;
|
||||
case 1:
|
||||
digit = integral;
|
||||
integral = 0;
|
||||
break;
|
||||
default:
|
||||
FMT_ASSERT(false, "invalid number of digits");
|
||||
}
|
||||
--exp;
|
||||
auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
|
||||
auto result = handler.on_digit(static_cast<char>('0' + digit),
|
||||
impl_data::power_of_10_64[exp] << -one.e,
|
||||
remainder, error, true);
|
||||
if (result != digits::more) return result;
|
||||
} while (exp > 0);
|
||||
// Generate digits for the fractional part.
|
||||
for (;;) {
|
||||
fractional *= 10;
|
||||
error *= 10;
|
||||
char digit = static_cast<char>('0' + (fractional >> -one.e));
|
||||
fractional &= one.f - 1;
|
||||
--exp;
|
||||
auto result = handler.on_digit(digit, one.f, fractional, error, false);
|
||||
if (result != digits::more) return result;
|
||||
}
|
||||
}
|
||||
|
||||
// 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<uint128_t>(low) |
|
||||
(static_cast<uint128_t>(high) << 64)} {}
|
||||
|
||||
constexpr uint128_wrapper(uint128_t u) : internal_{u} {}
|
||||
|
||||
constexpr uint64_t high() const FMT_NOEXCEPT {
|
||||
return uint64_t(internal_ >> 64);
|
||||
}
|
||||
constexpr uint64_t low() const FMT_NOEXCEPT { return uint64_t(internal_); }
|
||||
|
||||
uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT {
|
||||
internal_ += n;
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
uint64_t high_;
|
||||
uint64_t low_;
|
||||
|
||||
constexpr uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT
|
||||
: high_{high},
|
||||
low_{low} {}
|
||||
|
||||
constexpr uint64_t high() const FMT_NOEXCEPT { return high_; }
|
||||
constexpr uint64_t low() const FMT_NOEXCEPT { return low_; }
|
||||
|
||||
uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT {
|
||||
# if defined(_MSC_VER) && defined(_M_X64)
|
||||
unsigned char carry = _addcarry_u64(0, low_, n, &low_);
|
||||
_addcarry_u64(carry, high_, 0, &high_);
|
||||
return *this;
|
||||
# else
|
||||
uint64_t sum = low_ + n;
|
||||
high_ += (sum < low_ ? 1 : 0);
|
||||
low_ = sum;
|
||||
return *this;
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox.
|
||||
namespace dragonbox {
|
||||
// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
|
||||
inline uint128_wrapper umul128(uint64_t x, uint64_t y) FMT_NOEXCEPT {
|
||||
#if FMT_USE_INT128
|
||||
return static_cast<uint128_t>(x) * static_cast<uint128_t>(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 = (uint64_t(1) << 32) - uint64_t(1);
|
||||
|
||||
uint64_t a = x >> 32;
|
||||
uint64_t b = x & mask;
|
||||
uint64_t c = y >> 32;
|
||||
uint64_t d = y & mask;
|
||||
|
||||
uint64_t ac = a * c;
|
||||
uint64_t bc = b * c;
|
||||
uint64_t ad = a * d;
|
||||
uint64_t bd = b * d;
|
||||
|
||||
uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask);
|
||||
|
||||
return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32),
|
||||
(intermediate << 32) + (bd & mask)};
|
||||
#endif
|
||||
}
|
||||
|
||||
// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
|
||||
inline uint64_t umul128_upper64(uint64_t x, uint64_t y) FMT_NOEXCEPT {
|
||||
#if FMT_USE_INT128
|
||||
auto p = static_cast<uint128_t>(x) * static_cast<uint128_t>(y);
|
||||
return static_cast<uint64_t>(p >> 64);
|
||||
#elif defined(_MSC_VER) && defined(_M_X64)
|
||||
return __umulh(x, y);
|
||||
#else
|
||||
return umul128(x, y).high();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Computes upper 64 bits of multiplication of a 64-bit unsigned integer and a
|
||||
// 128-bit unsigned integer.
|
||||
inline uint64_t umul192_upper64(uint64_t x, uint128_wrapper y) FMT_NOEXCEPT {
|
||||
uint128_wrapper g0 = umul128(x, y.high());
|
||||
g0 += umul128_upper64(x, y.low());
|
||||
return g0.high();
|
||||
}
|
||||
|
||||
// Computes upper 32 bits of multiplication of a 32-bit unsigned integer and a
|
||||
// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a
|
||||
// 64-bit unsigned integer.
|
||||
inline uint32_t umul96_upper32(uint32_t x, uint64_t y) FMT_NOEXCEPT {
|
||||
return static_cast<uint32_t>(umul128_upper64(x, y));
|
||||
inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept {
|
||||
return umul128_upper64(static_cast<uint64_t>(x) << 32, y);
|
||||
}
|
||||
|
||||
// Computes middle 64 bits of multiplication of a 64-bit unsigned integer and a
|
||||
// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a
|
||||
// 128-bit unsigned integer.
|
||||
inline uint64_t umul192_middle64(uint64_t x, uint128_wrapper y) FMT_NOEXCEPT {
|
||||
uint64_t g01 = x * y.high();
|
||||
uint64_t g10 = umul128_upper64(x, y.low());
|
||||
return g01 + g10;
|
||||
inline uint128_fallback umul192_lower128(uint64_t x,
|
||||
uint128_fallback y) noexcept {
|
||||
uint64_t high = x * y.high();
|
||||
uint128_fallback high_low = umul128(x, y.low());
|
||||
return {high + high_low.high(), high_low.low()};
|
||||
}
|
||||
|
||||
// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a
|
||||
// 64-bit unsigned integer.
|
||||
inline uint64_t umul96_lower64(uint32_t x, uint64_t y) FMT_NOEXCEPT {
|
||||
inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept {
|
||||
return x * y;
|
||||
}
|
||||
|
||||
// Computes floor(log10(pow(2, e))) for e in [-1700, 1700] using the method from
|
||||
// https://fmt.dev/papers/Grisu-Exact.pdf#page=5, section 3.4.
|
||||
inline int floor_log10_pow2(int e) FMT_NOEXCEPT {
|
||||
FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent");
|
||||
const int shift = 22;
|
||||
return (e * static_cast<int>(log10_2_significand >> (64 - shift))) >> shift;
|
||||
}
|
||||
|
||||
// Various fast log computations.
|
||||
inline int floor_log2_pow10(int e) FMT_NOEXCEPT {
|
||||
FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
|
||||
const uint64_t log2_10_integer_part = 3;
|
||||
const uint64_t log2_10_fractional_digits = 0x5269e12f346e2bf9;
|
||||
const int shift_amount = 19;
|
||||
return (e * static_cast<int>(
|
||||
(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<int>(log10_2_significand >> (64 - shift_amount)) -
|
||||
static_cast<int>(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<uint32_t>() && 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<uint64_t>() && x == ((x >> exp) << exp);
|
||||
#endif
|
||||
}
|
||||
FMT_INLINE_VARIABLE constexpr struct {
|
||||
uint32_t divisor;
|
||||
int shift_amount;
|
||||
} div_small_pow10_infos[] = {{10, 16}, {100, 16}};
|
||||
|
||||
// Table entry type for divisibility test.
|
||||
template <typename T> struct divtest_table_entry {
|
||||
T mod_inv;
|
||||
T max_quotient;
|
||||
};
|
||||
|
||||
// Returns true iff x is divisible by pow(5, exp).
|
||||
inline bool divisible_by_power_of_5(uint32_t x, int exp) FMT_NOEXCEPT {
|
||||
FMT_ASSERT(exp <= 10, "too large exponent");
|
||||
static constexpr const divtest_table_entry<uint32_t> 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<uint64_t> divtest_table[] = {
|
||||
{0x0000000000000001, 0xffffffffffffffff},
|
||||
{0xcccccccccccccccd, 0x3333333333333333},
|
||||
{0x8f5c28f5c28f5c29, 0x0a3d70a3d70a3d70},
|
||||
{0x1cac083126e978d5, 0x020c49ba5e353f7c},
|
||||
{0xd288ce703afb7e91, 0x0068db8bac710cb2},
|
||||
{0x5d4e8fb00bcbe61d, 0x0014f8b588e368f0},
|
||||
{0x790fb65668c26139, 0x000431bde82d7b63},
|
||||
{0xe5032477ae8d46a5, 0x0000d6bf94d5e57a},
|
||||
{0xc767074b22e90e21, 0x00002af31dc46118},
|
||||
{0x8e47ce423a2e9c6d, 0x0000089705f4136b},
|
||||
{0x4fa7f60d3ed61f49, 0x000001b7cdfd9d7b},
|
||||
{0x0fee64690c913975, 0x00000057f5ff85e5},
|
||||
{0x3662e0e1cf503eb1, 0x000000119799812d},
|
||||
{0xa47a2cf9f6433fbd, 0x0000000384b84d09},
|
||||
{0x54186f653140a659, 0x00000000b424dc35},
|
||||
{0x7738164770402145, 0x0000000024075f3d},
|
||||
{0xe4a4d1417cd9a041, 0x000000000734aca5},
|
||||
{0xc75429d9e5c5200d, 0x000000000170ef54},
|
||||
{0xc1773b91fac10669, 0x000000000049c977},
|
||||
{0x26b172506559ce15, 0x00000000000ec1e4},
|
||||
{0xd489e3a9addec2d1, 0x000000000002f394},
|
||||
{0x90e860bb892c8d5d, 0x000000000000971d},
|
||||
{0x502e79bf1b6f4f79, 0x0000000000001e39},
|
||||
{0xdcd618596be30fe5, 0x000000000000060b}};
|
||||
return x * divtest_table[exp].mod_inv <= divtest_table[exp].max_quotient;
|
||||
}
|
||||
|
||||
// Replaces n by floor(n / pow(5, N)) returning true if and only if n is
|
||||
// divisible by pow(5, N).
|
||||
// Precondition: n <= 2 * pow(5, N + 1).
|
||||
// Replaces n by floor(n / pow(10, N)) returning true if and only if n is
|
||||
// divisible by pow(10, N).
|
||||
// Precondition: n <= pow(10, N + 1).
|
||||
template <int N>
|
||||
bool check_divisibility_and_divide_by_pow5(uint32_t& n) FMT_NOEXCEPT {
|
||||
static constexpr struct {
|
||||
uint32_t magic_number;
|
||||
int bits_for_comparison;
|
||||
uint32_t threshold;
|
||||
int shift_amount;
|
||||
} infos[] = {{0xcccd, 16, 0x3333, 18}, {0xa429, 8, 0x0a, 20}};
|
||||
constexpr auto info = infos[N - 1];
|
||||
n *= info.magic_number;
|
||||
const uint32_t comparison_mask = (1u << info.bits_for_comparison) - 1;
|
||||
bool result = (n & comparison_mask) <= info.threshold;
|
||||
bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept {
|
||||
// The numbers below are chosen such that:
|
||||
// 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100,
|
||||
// 2. nm mod 2^k < m if and only if n is divisible by d,
|
||||
// where m is magic_number, k is shift_amount
|
||||
// and d is divisor.
|
||||
//
|
||||
// Item 1 is a common technique of replacing division by a constant with
|
||||
// multiplication, see e.g. "Division by Invariant Integers Using
|
||||
// Multiplication" by Granlund and Montgomery (1994). magic_number (m) is set
|
||||
// to ceil(2^k/d) for large enough k.
|
||||
// The idea for item 2 originates from Schubfach.
|
||||
constexpr auto info = div_small_pow10_infos[N - 1];
|
||||
FMT_ASSERT(n <= info.divisor * 10, "n is too large");
|
||||
constexpr uint32_t magic_number =
|
||||
(1u << info.shift_amount) / info.divisor + 1;
|
||||
n *= magic_number;
|
||||
const uint32_t comparison_mask = (1u << info.shift_amount) - 1;
|
||||
bool result = (n & comparison_mask) < magic_number;
|
||||
n >>= info.shift_amount;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Computes floor(n / pow(10, N)) for small n and N.
|
||||
// Precondition: n <= pow(10, N + 1).
|
||||
template <int N> uint32_t small_division_by_pow10(uint32_t n) FMT_NOEXCEPT {
|
||||
static constexpr struct {
|
||||
uint32_t magic_number;
|
||||
int shift_amount;
|
||||
uint32_t divisor_times_10;
|
||||
} infos[] = {{0xcccd, 19, 100}, {0xa3d8, 22, 1000}};
|
||||
constexpr auto info = infos[N - 1];
|
||||
FMT_ASSERT(n <= info.divisor_times_10, "n is too large");
|
||||
return n * info.magic_number >> info.shift_amount;
|
||||
template <int N> uint32_t small_division_by_pow10(uint32_t n) noexcept {
|
||||
constexpr auto info = div_small_pow10_infos[N - 1];
|
||||
FMT_ASSERT(n <= info.divisor * 10, "n is too large");
|
||||
constexpr uint32_t magic_number =
|
||||
(1u << info.shift_amount) / info.divisor + 1;
|
||||
return (n * magic_number) >> info.shift_amount;
|
||||
}
|
||||
|
||||
// Computes floor(n / 10^(kappa + 1)) (float)
|
||||
inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) FMT_NOEXCEPT {
|
||||
return n / float_info<float>::big_divisor;
|
||||
inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept {
|
||||
// 1374389535 = ceil(2^37/100)
|
||||
return static_cast<uint32_t>((static_cast<uint64_t>(n) * 1374389535) >> 37);
|
||||
}
|
||||
// Computes floor(n / 10^(kappa + 1)) (double)
|
||||
inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) FMT_NOEXCEPT {
|
||||
return umul128_upper64(n, 0x83126e978d4fdf3c) >> 9;
|
||||
inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept {
|
||||
// 2361183241434822607 = ceil(2^(64+7)/1000)
|
||||
return umul128_upper64(n, 2361183241434822607ull) >> 7;
|
||||
}
|
||||
|
||||
// Various subroutines using pow10 cache
|
||||
template <class T> struct cache_accessor;
|
||||
template <typename T> struct cache_accessor;
|
||||
|
||||
template <> struct cache_accessor<float> {
|
||||
using carrier_uint = float_info<float>::carrier_uint;
|
||||
using cache_entry_type = uint64_t;
|
||||
|
||||
static uint64_t get_cached_power(int k) FMT_NOEXCEPT {
|
||||
static uint64_t get_cached_power(int k) noexcept {
|
||||
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
|
||||
"k is out of range");
|
||||
static constexpr const uint64_t pow10_significands[] = {
|
||||
@@ -1071,54 +278,65 @@ template <> struct cache_accessor<float> {
|
||||
0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000,
|
||||
0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000,
|
||||
0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0,
|
||||
0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940984,
|
||||
0xa18f07d736b90be5, 0xc9f2c9cd04674ede, 0xfc6f7c4045812296,
|
||||
0x9dc5ada82b70b59d, 0xc5371912364ce305, 0xf684df56c3e01bc6,
|
||||
0x9a130b963a6c115c, 0xc097ce7bc90715b3, 0xf0bdc21abb48db20,
|
||||
0x96769950b50d88f4, 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd,
|
||||
0x92efd1b8d0cf37be, 0xb7abc627050305ad, 0xe596b7b0c643c719,
|
||||
0x8f7e32ce7bea5c6f, 0xb35dbf821ae4f38b, 0xe0352f62a19e306e};
|
||||
0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940985,
|
||||
0xa18f07d736b90be6, 0xc9f2c9cd04674edf, 0xfc6f7c4045812297,
|
||||
0x9dc5ada82b70b59e, 0xc5371912364ce306, 0xf684df56c3e01bc7,
|
||||
0x9a130b963a6c115d, 0xc097ce7bc90715b4, 0xf0bdc21abb48db21,
|
||||
0x96769950b50d88f5, 0xbc143fa4e250eb32, 0xeb194f8e1ae525fe,
|
||||
0x92efd1b8d0cf37bf, 0xb7abc627050305ae, 0xe596b7b0c643c71a,
|
||||
0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f};
|
||||
return pow10_significands[k - float_info<float>::min_k];
|
||||
}
|
||||
|
||||
static carrier_uint compute_mul(carrier_uint u,
|
||||
const cache_entry_type& cache) FMT_NOEXCEPT {
|
||||
return umul96_upper32(u, cache);
|
||||
struct compute_mul_result {
|
||||
carrier_uint result;
|
||||
bool is_integer;
|
||||
};
|
||||
struct compute_mul_parity_result {
|
||||
bool parity;
|
||||
bool is_integer;
|
||||
};
|
||||
|
||||
static compute_mul_result compute_mul(
|
||||
carrier_uint u, const cache_entry_type& cache) noexcept {
|
||||
auto r = umul96_upper64(u, cache);
|
||||
return {static_cast<carrier_uint>(r >> 32),
|
||||
static_cast<carrier_uint>(r) == 0};
|
||||
}
|
||||
|
||||
static uint32_t compute_delta(const cache_entry_type& cache,
|
||||
int beta_minus_1) FMT_NOEXCEPT {
|
||||
return static_cast<uint32_t>(cache >> (64 - 1 - beta_minus_1));
|
||||
int beta) noexcept {
|
||||
return static_cast<uint32_t>(cache >> (64 - 1 - beta));
|
||||
}
|
||||
|
||||
static bool compute_mul_parity(carrier_uint two_f,
|
||||
const cache_entry_type& cache,
|
||||
int beta_minus_1) FMT_NOEXCEPT {
|
||||
FMT_ASSERT(beta_minus_1 >= 1, "");
|
||||
FMT_ASSERT(beta_minus_1 < 64, "");
|
||||
static compute_mul_parity_result compute_mul_parity(
|
||||
carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
|
||||
FMT_ASSERT(beta >= 1, "");
|
||||
FMT_ASSERT(beta < 64, "");
|
||||
|
||||
return ((umul96_lower64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0;
|
||||
auto r = umul96_lower64(two_f, cache);
|
||||
return {((r >> (64 - beta)) & 1) != 0,
|
||||
static_cast<uint32_t>(r >> (32 - beta)) == 0};
|
||||
}
|
||||
|
||||
static carrier_uint compute_left_endpoint_for_shorter_interval_case(
|
||||
const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
|
||||
const cache_entry_type& cache, int beta) noexcept {
|
||||
return static_cast<carrier_uint>(
|
||||
(cache - (cache >> (float_info<float>::significand_bits + 2))) >>
|
||||
(64 - float_info<float>::significand_bits - 1 - beta_minus_1));
|
||||
(cache - (cache >> (num_significand_bits<float>() + 2))) >>
|
||||
(64 - num_significand_bits<float>() - 1 - beta));
|
||||
}
|
||||
|
||||
static carrier_uint compute_right_endpoint_for_shorter_interval_case(
|
||||
const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
|
||||
const cache_entry_type& cache, int beta) noexcept {
|
||||
return static_cast<carrier_uint>(
|
||||
(cache + (cache >> (float_info<float>::significand_bits + 1))) >>
|
||||
(64 - float_info<float>::significand_bits - 1 - beta_minus_1));
|
||||
(cache + (cache >> (num_significand_bits<float>() + 1))) >>
|
||||
(64 - num_significand_bits<float>() - 1 - beta));
|
||||
}
|
||||
|
||||
static carrier_uint compute_round_up_for_shorter_interval_case(
|
||||
const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
|
||||
const cache_entry_type& cache, int beta) noexcept {
|
||||
return (static_cast<carrier_uint>(
|
||||
cache >>
|
||||
(64 - float_info<float>::significand_bits - 2 - beta_minus_1)) +
|
||||
cache >> (64 - num_significand_bits<float>() - 2 - beta)) +
|
||||
1) /
|
||||
2;
|
||||
}
|
||||
@@ -1126,13 +344,13 @@ template <> struct cache_accessor<float> {
|
||||
|
||||
template <> struct cache_accessor<double> {
|
||||
using carrier_uint = float_info<double>::carrier_uint;
|
||||
using cache_entry_type = uint128_wrapper;
|
||||
using cache_entry_type = uint128_fallback;
|
||||
|
||||
static uint128_wrapper get_cached_power(int k) FMT_NOEXCEPT {
|
||||
static uint128_fallback get_cached_power(int k) noexcept {
|
||||
FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
|
||||
"k is out of range");
|
||||
|
||||
static constexpr const uint128_wrapper pow10_significands[] = {
|
||||
static constexpr const uint128_fallback pow10_significands[] = {
|
||||
#if FMT_USE_FULL_CACHE_DRAGONBOX
|
||||
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
|
||||
{0x9faacf3df73609b1, 0x77b191618c54e9ad},
|
||||
@@ -1482,278 +700,293 @@ template <> struct cache_accessor<double> {
|
||||
{0x85a36366eb71f041, 0x47a6da2b7f864750},
|
||||
{0xa70c3c40a64e6c51, 0x999090b65f67d924},
|
||||
{0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d},
|
||||
{0x82818f1281ed449f, 0xbff8f10e7a8921a4},
|
||||
{0xa321f2d7226895c7, 0xaff72d52192b6a0d},
|
||||
{0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490},
|
||||
{0xfee50b7025c36a08, 0x02f236d04753d5b4},
|
||||
{0x9f4f2726179a2245, 0x01d762422c946590},
|
||||
{0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5},
|
||||
{0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2},
|
||||
{0x9b934c3b330c8577, 0x63cc55f49f88eb2f},
|
||||
{0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb},
|
||||
{0xf316271c7fc3908a, 0x8bef464e3945ef7a},
|
||||
{0x97edd871cfda3a56, 0x97758bf0e3cbb5ac},
|
||||
{0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317},
|
||||
{0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd},
|
||||
{0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a},
|
||||
{0xb975d6b6ee39e436, 0xb3e2fd538e122b44},
|
||||
{0xe7d34c64a9c85d44, 0x60dbbca87196b616},
|
||||
{0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd},
|
||||
{0xb51d13aea4a488dd, 0x6babab6398bdbe41},
|
||||
{0xe264589a4dcdab14, 0xc696963c7eed2dd1},
|
||||
{0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2},
|
||||
{0xb0de65388cc8ada8, 0x3b25a55f43294bcb},
|
||||
{0xdd15fe86affad912, 0x49ef0eb713f39ebe},
|
||||
{0x8a2dbf142dfcc7ab, 0x6e3569326c784337},
|
||||
{0xacb92ed9397bf996, 0x49c2c37f07965404},
|
||||
{0xd7e77a8f87daf7fb, 0xdc33745ec97be906},
|
||||
{0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3},
|
||||
{0xa8acd7c0222311bc, 0xc40832ea0d68ce0c},
|
||||
{0xd2d80db02aabd62b, 0xf50a3fa490c30190},
|
||||
{0x83c7088e1aab65db, 0x792667c6da79e0fa},
|
||||
{0xa4b8cab1a1563f52, 0x577001b891185938},
|
||||
{0xcde6fd5e09abcf26, 0xed4c0226b55e6f86},
|
||||
{0x80b05e5ac60b6178, 0x544f8158315b05b4},
|
||||
{0xa0dc75f1778e39d6, 0x696361ae3db1c721},
|
||||
{0xc913936dd571c84c, 0x03bc3a19cd1e38e9},
|
||||
{0xfb5878494ace3a5f, 0x04ab48a04065c723},
|
||||
{0x9d174b2dcec0e47b, 0x62eb0d64283f9c76},
|
||||
{0xc45d1df942711d9a, 0x3ba5d0bd324f8394},
|
||||
{0xf5746577930d6500, 0xca8f44ec7ee36479},
|
||||
{0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb},
|
||||
{0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e},
|
||||
{0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e},
|
||||
{0x95d04aee3b80ece5, 0xbba1f1d158724a12},
|
||||
{0xbb445da9ca61281f, 0x2a8a6e45ae8edc97},
|
||||
{0xea1575143cf97226, 0xf52d09d71a3293bd},
|
||||
{0x924d692ca61be758, 0x593c2626705f9c56},
|
||||
{0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c},
|
||||
{0xe498f455c38b997a, 0x0b6dfb9c0f956447},
|
||||
{0x8edf98b59a373fec, 0x4724bd4189bd5eac},
|
||||
{0xb2977ee300c50fe7, 0x58edec91ec2cb657},
|
||||
{0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed},
|
||||
{0x8b865b215899f46c, 0xbd79e0d20082ee74},
|
||||
{0xae67f1e9aec07187, 0xecd8590680a3aa11},
|
||||
{0xda01ee641a708de9, 0xe80e6f4820cc9495},
|
||||
{0x884134fe908658b2, 0x3109058d147fdcdd},
|
||||
{0xaa51823e34a7eede, 0xbd4b46f0599fd415},
|
||||
{0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a},
|
||||
{0x850fadc09923329e, 0x03e2cf6bc604ddb0},
|
||||
{0xa6539930bf6bff45, 0x84db8346b786151c},
|
||||
{0xcfe87f7cef46ff16, 0xe612641865679a63},
|
||||
{0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e},
|
||||
{0xa26da3999aef7749, 0xe3be5e330f38f09d},
|
||||
{0xcb090c8001ab551c, 0x5cadf5bfd3072cc5},
|
||||
{0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6},
|
||||
{0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa},
|
||||
{0xc646d63501a1511d, 0xb281e1fd541501b8},
|
||||
{0xf7d88bc24209a565, 0x1f225a7ca91a4226},
|
||||
{0x9ae757596946075f, 0x3375788de9b06958},
|
||||
{0xc1a12d2fc3978937, 0x0052d6b1641c83ae},
|
||||
{0xf209787bb47d6b84, 0xc0678c5dbd23a49a},
|
||||
{0x9745eb4d50ce6332, 0xf840b7ba963646e0},
|
||||
{0xbd176620a501fbff, 0xb650e5a93bc3d898},
|
||||
{0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe},
|
||||
{0x93ba47c980e98cdf, 0xc66f336c36b10137},
|
||||
{0xb8a8d9bbe123f017, 0xb80b0047445d4184},
|
||||
{0xe6d3102ad96cec1d, 0xa60dc059157491e5},
|
||||
{0x9043ea1ac7e41392, 0x87c89837ad68db2f},
|
||||
{0xb454e4a179dd1877, 0x29babe4598c311fb},
|
||||
{0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a},
|
||||
{0x8ce2529e2734bb1d, 0x1899e4a65f58660c},
|
||||
{0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f},
|
||||
{0xdc21a1171d42645d, 0x76707543f4fa1f73},
|
||||
{0x899504ae72497eba, 0x6a06494a791c53a8},
|
||||
{0xabfa45da0edbde69, 0x0487db9d17636892},
|
||||
{0xd6f8d7509292d603, 0x45a9d2845d3c42b6},
|
||||
{0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2},
|
||||
{0xa7f26836f282b732, 0x8e6cac7768d7141e},
|
||||
{0xd1ef0244af2364ff, 0x3207d795430cd926},
|
||||
{0x8335616aed761f1f, 0x7f44e6bd49e807b8},
|
||||
{0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6},
|
||||
{0xcd036837130890a1, 0x36dba887c37a8c0f},
|
||||
{0x802221226be55a64, 0xc2494954da2c9789},
|
||||
{0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c},
|
||||
{0xc83553c5c8965d3d, 0x6f92829494e5acc7},
|
||||
{0xfa42a8b73abbf48c, 0xcb772339ba1f17f9},
|
||||
{0x9c69a97284b578d7, 0xff2a760414536efb},
|
||||
{0xc38413cf25e2d70d, 0xfef5138519684aba},
|
||||
{0xf46518c2ef5b8cd1, 0x7eb258665fc25d69},
|
||||
{0x98bf2f79d5993802, 0xef2f773ffbd97a61},
|
||||
{0xbeeefb584aff8603, 0xaafb550ffacfd8fa},
|
||||
{0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38},
|
||||
{0x952ab45cfa97a0b2, 0xdd945a747bf26183},
|
||||
{0xba756174393d88df, 0x94f971119aeef9e4},
|
||||
{0xe912b9d1478ceb17, 0x7a37cd5601aab85d},
|
||||
{0x91abb422ccb812ee, 0xac62e055c10ab33a},
|
||||
{0xb616a12b7fe617aa, 0x577b986b314d6009},
|
||||
{0xe39c49765fdf9d94, 0xed5a7e85fda0b80b},
|
||||
{0x8e41ade9fbebc27d, 0x14588f13be847307},
|
||||
{0xb1d219647ae6b31c, 0x596eb2d8ae258fc8},
|
||||
{0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb},
|
||||
{0x8aec23d680043bee, 0x25de7bb9480d5854},
|
||||
{0xada72ccc20054ae9, 0xaf561aa79a10ae6a},
|
||||
{0xd910f7ff28069da4, 0x1b2ba1518094da04},
|
||||
{0x87aa9aff79042286, 0x90fb44d2f05d0842},
|
||||
{0xa99541bf57452b28, 0x353a1607ac744a53},
|
||||
{0xd3fa922f2d1675f2, 0x42889b8997915ce8},
|
||||
{0x847c9b5d7c2e09b7, 0x69956135febada11},
|
||||
{0xa59bc234db398c25, 0x43fab9837e699095},
|
||||
{0xcf02b2c21207ef2e, 0x94f967e45e03f4bb},
|
||||
{0x8161afb94b44f57d, 0x1d1be0eebac278f5},
|
||||
{0xa1ba1ba79e1632dc, 0x6462d92a69731732},
|
||||
{0xca28a291859bbf93, 0x7d7b8f7503cfdcfe},
|
||||
{0xfcb2cb35e702af78, 0x5cda735244c3d43e},
|
||||
{0x9defbf01b061adab, 0x3a0888136afa64a7},
|
||||
{0xc56baec21c7a1916, 0x088aaa1845b8fdd0},
|
||||
{0xf6c69a72a3989f5b, 0x8aad549e57273d45},
|
||||
{0x9a3c2087a63f6399, 0x36ac54e2f678864b},
|
||||
{0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd},
|
||||
{0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5},
|
||||
{0x969eb7c47859e743, 0x9f644ae5a4b1b325},
|
||||
{0xbc4665b596706114, 0x873d5d9f0dde1fee},
|
||||
{0xeb57ff22fc0c7959, 0xa90cb506d155a7ea},
|
||||
{0x9316ff75dd87cbd8, 0x09a7f12442d588f2},
|
||||
{0xb7dcbf5354e9bece, 0x0c11ed6d538aeb2f},
|
||||
{0xe5d3ef282a242e81, 0x8f1668c8a86da5fa},
|
||||
{0x8fa475791a569d10, 0xf96e017d694487bc},
|
||||
{0xb38d92d760ec4455, 0x37c981dcc395a9ac},
|
||||
{0xe070f78d3927556a, 0x85bbe253f47b1417},
|
||||
{0x8c469ab843b89562, 0x93956d7478ccec8e},
|
||||
{0xaf58416654a6babb, 0x387ac8d1970027b2},
|
||||
{0xdb2e51bfe9d0696a, 0x06997b05fcc0319e},
|
||||
{0x88fcf317f22241e2, 0x441fece3bdf81f03},
|
||||
{0xab3c2fddeeaad25a, 0xd527e81cad7626c3},
|
||||
{0xd60b3bd56a5586f1, 0x8a71e223d8d3b074},
|
||||
{0x85c7056562757456, 0xf6872d5667844e49},
|
||||
{0xa738c6bebb12d16c, 0xb428f8ac016561db},
|
||||
{0xd106f86e69d785c7, 0xe13336d701beba52},
|
||||
{0x82a45b450226b39c, 0xecc0024661173473},
|
||||
{0xa34d721642b06084, 0x27f002d7f95d0190},
|
||||
{0xcc20ce9bd35c78a5, 0x31ec038df7b441f4},
|
||||
{0xff290242c83396ce, 0x7e67047175a15271},
|
||||
{0x9f79a169bd203e41, 0x0f0062c6e984d386},
|
||||
{0xc75809c42c684dd1, 0x52c07b78a3e60868},
|
||||
{0xf92e0c3537826145, 0xa7709a56ccdf8a82},
|
||||
{0x9bbcc7a142b17ccb, 0x88a66076400bb691},
|
||||
{0xc2abf989935ddbfe, 0x6acff893d00ea435},
|
||||
{0xf356f7ebf83552fe, 0x0583f6b8c4124d43},
|
||||
{0x98165af37b2153de, 0xc3727a337a8b704a},
|
||||
{0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c},
|
||||
{0xeda2ee1c7064130c, 0x1162def06f79df73},
|
||||
{0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8},
|
||||
{0xb9a74a0637ce2ee1, 0x6d953e2bd7173692},
|
||||
{0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437},
|
||||
{0x910ab1d4db9914a0, 0x1d9c9892400a22a2},
|
||||
{0xb54d5e4a127f59c8, 0x2503beb6d00cab4b},
|
||||
{0xe2a0b5dc971f303a, 0x2e44ae64840fd61d},
|
||||
{0x8da471a9de737e24, 0x5ceaecfed289e5d2},
|
||||
{0xb10d8e1456105dad, 0x7425a83e872c5f47},
|
||||
{0xdd50f1996b947518, 0xd12f124e28f77719},
|
||||
{0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f},
|
||||
{0xace73cbfdc0bfb7b, 0x636cc64d1001550b},
|
||||
{0xd8210befd30efa5a, 0x3c47f7e05401aa4e},
|
||||
{0x8714a775e3e95c78, 0x65acfaec34810a71},
|
||||
{0xa8d9d1535ce3b396, 0x7f1839a741a14d0d},
|
||||
{0xd31045a8341ca07c, 0x1ede48111209a050},
|
||||
{0x83ea2b892091e44d, 0x934aed0aab460432},
|
||||
{0xa4e4b66b68b65d60, 0xf81da84d5617853f},
|
||||
{0xce1de40642e3f4b9, 0x36251260ab9d668e},
|
||||
{0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019},
|
||||
{0xa1075a24e4421730, 0xb24cf65b8612f81f},
|
||||
{0xc94930ae1d529cfc, 0xdee033f26797b627},
|
||||
{0xfb9b7cd9a4a7443c, 0x169840ef017da3b1},
|
||||
{0x9d412e0806e88aa5, 0x8e1f289560ee864e},
|
||||
{0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2},
|
||||
{0xf5b5d7ec8acb58a2, 0xae10af696774b1db},
|
||||
{0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29},
|
||||
{0xbff610b0cc6edd3f, 0x17fd090a58d32af3},
|
||||
{0xeff394dcff8a948e, 0xddfc4b4cef07f5b0},
|
||||
{0x95f83d0a1fb69cd9, 0x4abdaf101564f98e},
|
||||
{0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1},
|
||||
{0xea53df5fd18d5513, 0x84c86189216dc5ed},
|
||||
{0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4},
|
||||
{0xb7118682dbb66a77, 0x3fbc8c33221dc2a1},
|
||||
{0xe4d5e82392a40515, 0x0fabaf3feaa5334a},
|
||||
{0x8f05b1163ba6832d, 0x29cb4d87f2a7400e},
|
||||
{0xb2c71d5bca9023f8, 0x743e20e9ef511012},
|
||||
{0xdf78e4b2bd342cf6, 0x914da9246b255416},
|
||||
{0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e},
|
||||
{0xae9672aba3d0c320, 0xa184ac2473b529b1},
|
||||
{0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e},
|
||||
{0x8865899617fb1871, 0x7e2fa67c7a658892},
|
||||
{0xaa7eebfb9df9de8d, 0xddbb901b98feeab7},
|
||||
{0xd51ea6fa85785631, 0x552a74227f3ea565},
|
||||
{0x8533285c936b35de, 0xd53a88958f87275f},
|
||||
{0xa67ff273b8460356, 0x8a892abaf368f137},
|
||||
{0xd01fef10a657842c, 0x2d2b7569b0432d85},
|
||||
{0x8213f56a67f6b29b, 0x9c3b29620e29fc73},
|
||||
{0xa298f2c501f45f42, 0x8349f3ba91b47b8f},
|
||||
{0xcb3f2f7642717713, 0x241c70a936219a73},
|
||||
{0xfe0efb53d30dd4d7, 0xed238cd383aa0110},
|
||||
{0x9ec95d1463e8a506, 0xf4363804324a40aa},
|
||||
{0xc67bb4597ce2ce48, 0xb143c6053edcd0d5},
|
||||
{0xf81aa16fdc1b81da, 0xdd94b7868e94050a},
|
||||
{0x9b10a4e5e9913128, 0xca7cf2b4191c8326},
|
||||
{0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0},
|
||||
{0xf24a01a73cf2dccf, 0xbc633b39673c8cec},
|
||||
{0x976e41088617ca01, 0xd5be0503e085d813},
|
||||
{0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18},
|
||||
{0xec9c459d51852ba2, 0xddf8e7d60ed1219e},
|
||||
{0x93e1ab8252f33b45, 0xcabb90e5c942b503},
|
||||
{0xb8da1662e7b00a17, 0x3d6a751f3b936243},
|
||||
{0xe7109bfba19c0c9d, 0x0cc512670a783ad4},
|
||||
{0x906a617d450187e2, 0x27fb2b80668b24c5},
|
||||
{0xb484f9dc9641e9da, 0xb1f9f660802dedf6},
|
||||
{0xe1a63853bbd26451, 0x5e7873f8a0396973},
|
||||
{0x8d07e33455637eb2, 0xdb0b487b6423e1e8},
|
||||
{0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62},
|
||||
{0xdc5c5301c56b75f7, 0x7641a140cc7810fb},
|
||||
{0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d},
|
||||
{0xac2820d9623bf429, 0x546345fa9fbdcd44},
|
||||
{0xd732290fbacaf133, 0xa97c177947ad4095},
|
||||
{0x867f59a9d4bed6c0, 0x49ed8eabcccc485d},
|
||||
{0xa81f301449ee8c70, 0x5c68f256bfff5a74},
|
||||
{0xd226fc195c6a2f8c, 0x73832eec6fff3111},
|
||||
{0x83585d8fd9c25db7, 0xc831fd53c5ff7eab},
|
||||
{0xa42e74f3d032f525, 0xba3e7ca8b77f5e55},
|
||||
{0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb},
|
||||
{0x80444b5e7aa7cf85, 0x7980d163cf5b81b3},
|
||||
{0xa0555e361951c366, 0xd7e105bcc332621f},
|
||||
{0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7},
|
||||
{0xfa856334878fc150, 0xb14f98f6f0feb951},
|
||||
{0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3},
|
||||
{0xc3b8358109e84f07, 0x0a862f80ec4700c8},
|
||||
{0xf4a642e14c6262c8, 0xcd27bb612758c0fa},
|
||||
{0x98e7e9cccfbd7dbd, 0x8038d51cb897789c},
|
||||
{0xbf21e44003acdd2c, 0xe0470a63e6bd56c3},
|
||||
{0xeeea5d5004981478, 0x1858ccfce06cac74},
|
||||
{0x95527a5202df0ccb, 0x0f37801e0c43ebc8},
|
||||
{0xbaa718e68396cffd, 0xd30560258f54e6ba},
|
||||
{0xe950df20247c83fd, 0x47c6b82ef32a2069},
|
||||
{0x91d28b7416cdd27e, 0x4cdc331d57fa5441},
|
||||
{0xb6472e511c81471d, 0xe0133fe4adf8e952},
|
||||
{0xe3d8f9e563a198e5, 0x58180fddd97723a6},
|
||||
{0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648},
|
||||
{0xb201833b35d63f73, 0x2cd2cc6551e513da},
|
||||
{0xde81e40a034bcf4f, 0xf8077f7ea65e58d1},
|
||||
{0x8b112e86420f6191, 0xfb04afaf27faf782},
|
||||
{0xadd57a27d29339f6, 0x79c5db9af1f9b563},
|
||||
{0xd94ad8b1c7380874, 0x18375281ae7822bc},
|
||||
{0x87cec76f1c830548, 0x8f2293910d0b15b5},
|
||||
{0xa9c2794ae3a3c69a, 0xb2eb3875504ddb22},
|
||||
{0xd433179d9c8cb841, 0x5fa60692a46151eb},
|
||||
{0x849feec281d7f328, 0xdbc7c41ba6bcd333},
|
||||
{0xa5c7ea73224deff3, 0x12b9b522906c0800},
|
||||
{0xcf39e50feae16bef, 0xd768226b34870a00},
|
||||
{0x81842f29f2cce375, 0xe6a1158300d46640},
|
||||
{0xa1e53af46f801c53, 0x60495ae3c1097fd0},
|
||||
{0xca5e89b18b602368, 0x385bb19cb14bdfc4},
|
||||
{0xfcf62c1dee382c42, 0x46729e03dd9ed7b5},
|
||||
{0x9e19db92b4e31ba9, 0x6c07a2c26a8346d1},
|
||||
{0xc5a05277621be293, 0xc7098b7305241885},
|
||||
{ 0xf70867153aa2db38,
|
||||
0xb8cbee4fc66d1ea7 }
|
||||
{0x82818f1281ed449f, 0xbff8f10e7a8921a5},
|
||||
{0xa321f2d7226895c7, 0xaff72d52192b6a0e},
|
||||
{0xcbea6f8ceb02bb39, 0x9bf4f8a69f764491},
|
||||
{0xfee50b7025c36a08, 0x02f236d04753d5b5},
|
||||
{0x9f4f2726179a2245, 0x01d762422c946591},
|
||||
{0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef6},
|
||||
{0xf8ebad2b84e0d58b, 0xd2e0898765a7deb3},
|
||||
{0x9b934c3b330c8577, 0x63cc55f49f88eb30},
|
||||
{0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fc},
|
||||
{0xf316271c7fc3908a, 0x8bef464e3945ef7b},
|
||||
{0x97edd871cfda3a56, 0x97758bf0e3cbb5ad},
|
||||
{0xbde94e8e43d0c8ec, 0x3d52eeed1cbea318},
|
||||
{0xed63a231d4c4fb27, 0x4ca7aaa863ee4bde},
|
||||
{0x945e455f24fb1cf8, 0x8fe8caa93e74ef6b},
|
||||
{0xb975d6b6ee39e436, 0xb3e2fd538e122b45},
|
||||
{0xe7d34c64a9c85d44, 0x60dbbca87196b617},
|
||||
{0x90e40fbeea1d3a4a, 0xbc8955e946fe31ce},
|
||||
{0xb51d13aea4a488dd, 0x6babab6398bdbe42},
|
||||
{0xe264589a4dcdab14, 0xc696963c7eed2dd2},
|
||||
{0x8d7eb76070a08aec, 0xfc1e1de5cf543ca3},
|
||||
{0xb0de65388cc8ada8, 0x3b25a55f43294bcc},
|
||||
{0xdd15fe86affad912, 0x49ef0eb713f39ebf},
|
||||
{0x8a2dbf142dfcc7ab, 0x6e3569326c784338},
|
||||
{0xacb92ed9397bf996, 0x49c2c37f07965405},
|
||||
{0xd7e77a8f87daf7fb, 0xdc33745ec97be907},
|
||||
{0x86f0ac99b4e8dafd, 0x69a028bb3ded71a4},
|
||||
{0xa8acd7c0222311bc, 0xc40832ea0d68ce0d},
|
||||
{0xd2d80db02aabd62b, 0xf50a3fa490c30191},
|
||||
{0x83c7088e1aab65db, 0x792667c6da79e0fb},
|
||||
{0xa4b8cab1a1563f52, 0x577001b891185939},
|
||||
{0xcde6fd5e09abcf26, 0xed4c0226b55e6f87},
|
||||
{0x80b05e5ac60b6178, 0x544f8158315b05b5},
|
||||
{0xa0dc75f1778e39d6, 0x696361ae3db1c722},
|
||||
{0xc913936dd571c84c, 0x03bc3a19cd1e38ea},
|
||||
{0xfb5878494ace3a5f, 0x04ab48a04065c724},
|
||||
{0x9d174b2dcec0e47b, 0x62eb0d64283f9c77},
|
||||
{0xc45d1df942711d9a, 0x3ba5d0bd324f8395},
|
||||
{0xf5746577930d6500, 0xca8f44ec7ee3647a},
|
||||
{0x9968bf6abbe85f20, 0x7e998b13cf4e1ecc},
|
||||
{0xbfc2ef456ae276e8, 0x9e3fedd8c321a67f},
|
||||
{0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101f},
|
||||
{0x95d04aee3b80ece5, 0xbba1f1d158724a13},
|
||||
{0xbb445da9ca61281f, 0x2a8a6e45ae8edc98},
|
||||
{0xea1575143cf97226, 0xf52d09d71a3293be},
|
||||
{0x924d692ca61be758, 0x593c2626705f9c57},
|
||||
{0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836d},
|
||||
{0xe498f455c38b997a, 0x0b6dfb9c0f956448},
|
||||
{0x8edf98b59a373fec, 0x4724bd4189bd5ead},
|
||||
{0xb2977ee300c50fe7, 0x58edec91ec2cb658},
|
||||
{0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ee},
|
||||
{0x8b865b215899f46c, 0xbd79e0d20082ee75},
|
||||
{0xae67f1e9aec07187, 0xecd8590680a3aa12},
|
||||
{0xda01ee641a708de9, 0xe80e6f4820cc9496},
|
||||
{0x884134fe908658b2, 0x3109058d147fdcde},
|
||||
{0xaa51823e34a7eede, 0xbd4b46f0599fd416},
|
||||
{0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91b},
|
||||
{0x850fadc09923329e, 0x03e2cf6bc604ddb1},
|
||||
{0xa6539930bf6bff45, 0x84db8346b786151d},
|
||||
{0xcfe87f7cef46ff16, 0xe612641865679a64},
|
||||
{0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07f},
|
||||
{0xa26da3999aef7749, 0xe3be5e330f38f09e},
|
||||
{0xcb090c8001ab551c, 0x5cadf5bfd3072cc6},
|
||||
{0xfdcb4fa002162a63, 0x73d9732fc7c8f7f7},
|
||||
{0x9e9f11c4014dda7e, 0x2867e7fddcdd9afb},
|
||||
{0xc646d63501a1511d, 0xb281e1fd541501b9},
|
||||
{0xf7d88bc24209a565, 0x1f225a7ca91a4227},
|
||||
{0x9ae757596946075f, 0x3375788de9b06959},
|
||||
{0xc1a12d2fc3978937, 0x0052d6b1641c83af},
|
||||
{0xf209787bb47d6b84, 0xc0678c5dbd23a49b},
|
||||
{0x9745eb4d50ce6332, 0xf840b7ba963646e1},
|
||||
{0xbd176620a501fbff, 0xb650e5a93bc3d899},
|
||||
{0xec5d3fa8ce427aff, 0xa3e51f138ab4cebf},
|
||||
{0x93ba47c980e98cdf, 0xc66f336c36b10138},
|
||||
{0xb8a8d9bbe123f017, 0xb80b0047445d4185},
|
||||
{0xe6d3102ad96cec1d, 0xa60dc059157491e6},
|
||||
{0x9043ea1ac7e41392, 0x87c89837ad68db30},
|
||||
{0xb454e4a179dd1877, 0x29babe4598c311fc},
|
||||
{0xe16a1dc9d8545e94, 0xf4296dd6fef3d67b},
|
||||
{0x8ce2529e2734bb1d, 0x1899e4a65f58660d},
|
||||
{0xb01ae745b101e9e4, 0x5ec05dcff72e7f90},
|
||||
{0xdc21a1171d42645d, 0x76707543f4fa1f74},
|
||||
{0x899504ae72497eba, 0x6a06494a791c53a9},
|
||||
{0xabfa45da0edbde69, 0x0487db9d17636893},
|
||||
{0xd6f8d7509292d603, 0x45a9d2845d3c42b7},
|
||||
{0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3},
|
||||
{0xa7f26836f282b732, 0x8e6cac7768d7141f},
|
||||
{0xd1ef0244af2364ff, 0x3207d795430cd927},
|
||||
{0x8335616aed761f1f, 0x7f44e6bd49e807b9},
|
||||
{0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a7},
|
||||
{0xcd036837130890a1, 0x36dba887c37a8c10},
|
||||
{0x802221226be55a64, 0xc2494954da2c978a},
|
||||
{0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6d},
|
||||
{0xc83553c5c8965d3d, 0x6f92829494e5acc8},
|
||||
{0xfa42a8b73abbf48c, 0xcb772339ba1f17fa},
|
||||
{0x9c69a97284b578d7, 0xff2a760414536efc},
|
||||
{0xc38413cf25e2d70d, 0xfef5138519684abb},
|
||||
{0xf46518c2ef5b8cd1, 0x7eb258665fc25d6a},
|
||||
{0x98bf2f79d5993802, 0xef2f773ffbd97a62},
|
||||
{0xbeeefb584aff8603, 0xaafb550ffacfd8fb},
|
||||
{0xeeaaba2e5dbf6784, 0x95ba2a53f983cf39},
|
||||
{0x952ab45cfa97a0b2, 0xdd945a747bf26184},
|
||||
{0xba756174393d88df, 0x94f971119aeef9e5},
|
||||
{0xe912b9d1478ceb17, 0x7a37cd5601aab85e},
|
||||
{0x91abb422ccb812ee, 0xac62e055c10ab33b},
|
||||
{0xb616a12b7fe617aa, 0x577b986b314d600a},
|
||||
{0xe39c49765fdf9d94, 0xed5a7e85fda0b80c},
|
||||
{0x8e41ade9fbebc27d, 0x14588f13be847308},
|
||||
{0xb1d219647ae6b31c, 0x596eb2d8ae258fc9},
|
||||
{0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bc},
|
||||
{0x8aec23d680043bee, 0x25de7bb9480d5855},
|
||||
{0xada72ccc20054ae9, 0xaf561aa79a10ae6b},
|
||||
{0xd910f7ff28069da4, 0x1b2ba1518094da05},
|
||||
{0x87aa9aff79042286, 0x90fb44d2f05d0843},
|
||||
{0xa99541bf57452b28, 0x353a1607ac744a54},
|
||||
{0xd3fa922f2d1675f2, 0x42889b8997915ce9},
|
||||
{0x847c9b5d7c2e09b7, 0x69956135febada12},
|
||||
{0xa59bc234db398c25, 0x43fab9837e699096},
|
||||
{0xcf02b2c21207ef2e, 0x94f967e45e03f4bc},
|
||||
{0x8161afb94b44f57d, 0x1d1be0eebac278f6},
|
||||
{0xa1ba1ba79e1632dc, 0x6462d92a69731733},
|
||||
{0xca28a291859bbf93, 0x7d7b8f7503cfdcff},
|
||||
{0xfcb2cb35e702af78, 0x5cda735244c3d43f},
|
||||
{0x9defbf01b061adab, 0x3a0888136afa64a8},
|
||||
{0xc56baec21c7a1916, 0x088aaa1845b8fdd1},
|
||||
{0xf6c69a72a3989f5b, 0x8aad549e57273d46},
|
||||
{0x9a3c2087a63f6399, 0x36ac54e2f678864c},
|
||||
{0xc0cb28a98fcf3c7f, 0x84576a1bb416a7de},
|
||||
{0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d6},
|
||||
{0x969eb7c47859e743, 0x9f644ae5a4b1b326},
|
||||
{0xbc4665b596706114, 0x873d5d9f0dde1fef},
|
||||
{0xeb57ff22fc0c7959, 0xa90cb506d155a7eb},
|
||||
{0x9316ff75dd87cbd8, 0x09a7f12442d588f3},
|
||||
{0xb7dcbf5354e9bece, 0x0c11ed6d538aeb30},
|
||||
{0xe5d3ef282a242e81, 0x8f1668c8a86da5fb},
|
||||
{0x8fa475791a569d10, 0xf96e017d694487bd},
|
||||
{0xb38d92d760ec4455, 0x37c981dcc395a9ad},
|
||||
{0xe070f78d3927556a, 0x85bbe253f47b1418},
|
||||
{0x8c469ab843b89562, 0x93956d7478ccec8f},
|
||||
{0xaf58416654a6babb, 0x387ac8d1970027b3},
|
||||
{0xdb2e51bfe9d0696a, 0x06997b05fcc0319f},
|
||||
{0x88fcf317f22241e2, 0x441fece3bdf81f04},
|
||||
{0xab3c2fddeeaad25a, 0xd527e81cad7626c4},
|
||||
{0xd60b3bd56a5586f1, 0x8a71e223d8d3b075},
|
||||
{0x85c7056562757456, 0xf6872d5667844e4a},
|
||||
{0xa738c6bebb12d16c, 0xb428f8ac016561dc},
|
||||
{0xd106f86e69d785c7, 0xe13336d701beba53},
|
||||
{0x82a45b450226b39c, 0xecc0024661173474},
|
||||
{0xa34d721642b06084, 0x27f002d7f95d0191},
|
||||
{0xcc20ce9bd35c78a5, 0x31ec038df7b441f5},
|
||||
{0xff290242c83396ce, 0x7e67047175a15272},
|
||||
{0x9f79a169bd203e41, 0x0f0062c6e984d387},
|
||||
{0xc75809c42c684dd1, 0x52c07b78a3e60869},
|
||||
{0xf92e0c3537826145, 0xa7709a56ccdf8a83},
|
||||
{0x9bbcc7a142b17ccb, 0x88a66076400bb692},
|
||||
{0xc2abf989935ddbfe, 0x6acff893d00ea436},
|
||||
{0xf356f7ebf83552fe, 0x0583f6b8c4124d44},
|
||||
{0x98165af37b2153de, 0xc3727a337a8b704b},
|
||||
{0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5d},
|
||||
{0xeda2ee1c7064130c, 0x1162def06f79df74},
|
||||
{0x9485d4d1c63e8be7, 0x8addcb5645ac2ba9},
|
||||
{0xb9a74a0637ce2ee1, 0x6d953e2bd7173693},
|
||||
{0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0438},
|
||||
{0x910ab1d4db9914a0, 0x1d9c9892400a22a3},
|
||||
{0xb54d5e4a127f59c8, 0x2503beb6d00cab4c},
|
||||
{0xe2a0b5dc971f303a, 0x2e44ae64840fd61e},
|
||||
{0x8da471a9de737e24, 0x5ceaecfed289e5d3},
|
||||
{0xb10d8e1456105dad, 0x7425a83e872c5f48},
|
||||
{0xdd50f1996b947518, 0xd12f124e28f7771a},
|
||||
{0x8a5296ffe33cc92f, 0x82bd6b70d99aaa70},
|
||||
{0xace73cbfdc0bfb7b, 0x636cc64d1001550c},
|
||||
{0xd8210befd30efa5a, 0x3c47f7e05401aa4f},
|
||||
{0x8714a775e3e95c78, 0x65acfaec34810a72},
|
||||
{0xa8d9d1535ce3b396, 0x7f1839a741a14d0e},
|
||||
{0xd31045a8341ca07c, 0x1ede48111209a051},
|
||||
{0x83ea2b892091e44d, 0x934aed0aab460433},
|
||||
{0xa4e4b66b68b65d60, 0xf81da84d56178540},
|
||||
{0xce1de40642e3f4b9, 0x36251260ab9d668f},
|
||||
{0x80d2ae83e9ce78f3, 0xc1d72b7c6b42601a},
|
||||
{0xa1075a24e4421730, 0xb24cf65b8612f820},
|
||||
{0xc94930ae1d529cfc, 0xdee033f26797b628},
|
||||
{0xfb9b7cd9a4a7443c, 0x169840ef017da3b2},
|
||||
{0x9d412e0806e88aa5, 0x8e1f289560ee864f},
|
||||
{0xc491798a08a2ad4e, 0xf1a6f2bab92a27e3},
|
||||
{0xf5b5d7ec8acb58a2, 0xae10af696774b1dc},
|
||||
{0x9991a6f3d6bf1765, 0xacca6da1e0a8ef2a},
|
||||
{0xbff610b0cc6edd3f, 0x17fd090a58d32af4},
|
||||
{0xeff394dcff8a948e, 0xddfc4b4cef07f5b1},
|
||||
{0x95f83d0a1fb69cd9, 0x4abdaf101564f98f},
|
||||
{0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f2},
|
||||
{0xea53df5fd18d5513, 0x84c86189216dc5ee},
|
||||
{0x92746b9be2f8552c, 0x32fd3cf5b4e49bb5},
|
||||
{0xb7118682dbb66a77, 0x3fbc8c33221dc2a2},
|
||||
{0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
|
||||
{0x8f05b1163ba6832d, 0x29cb4d87f2a7400f},
|
||||
{0xb2c71d5bca9023f8, 0x743e20e9ef511013},
|
||||
{0xdf78e4b2bd342cf6, 0x914da9246b255417},
|
||||
{0x8bab8eefb6409c1a, 0x1ad089b6c2f7548f},
|
||||
{0xae9672aba3d0c320, 0xa184ac2473b529b2},
|
||||
{0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741f},
|
||||
{0x8865899617fb1871, 0x7e2fa67c7a658893},
|
||||
{0xaa7eebfb9df9de8d, 0xddbb901b98feeab8},
|
||||
{0xd51ea6fa85785631, 0x552a74227f3ea566},
|
||||
{0x8533285c936b35de, 0xd53a88958f872760},
|
||||
{0xa67ff273b8460356, 0x8a892abaf368f138},
|
||||
{0xd01fef10a657842c, 0x2d2b7569b0432d86},
|
||||
{0x8213f56a67f6b29b, 0x9c3b29620e29fc74},
|
||||
{0xa298f2c501f45f42, 0x8349f3ba91b47b90},
|
||||
{0xcb3f2f7642717713, 0x241c70a936219a74},
|
||||
{0xfe0efb53d30dd4d7, 0xed238cd383aa0111},
|
||||
{0x9ec95d1463e8a506, 0xf4363804324a40ab},
|
||||
{0xc67bb4597ce2ce48, 0xb143c6053edcd0d6},
|
||||
{0xf81aa16fdc1b81da, 0xdd94b7868e94050b},
|
||||
{0x9b10a4e5e9913128, 0xca7cf2b4191c8327},
|
||||
{0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f1},
|
||||
{0xf24a01a73cf2dccf, 0xbc633b39673c8ced},
|
||||
{0x976e41088617ca01, 0xd5be0503e085d814},
|
||||
{0xbd49d14aa79dbc82, 0x4b2d8644d8a74e19},
|
||||
{0xec9c459d51852ba2, 0xddf8e7d60ed1219f},
|
||||
{0x93e1ab8252f33b45, 0xcabb90e5c942b504},
|
||||
{0xb8da1662e7b00a17, 0x3d6a751f3b936244},
|
||||
{0xe7109bfba19c0c9d, 0x0cc512670a783ad5},
|
||||
{0x906a617d450187e2, 0x27fb2b80668b24c6},
|
||||
{0xb484f9dc9641e9da, 0xb1f9f660802dedf7},
|
||||
{0xe1a63853bbd26451, 0x5e7873f8a0396974},
|
||||
{0x8d07e33455637eb2, 0xdb0b487b6423e1e9},
|
||||
{0xb049dc016abc5e5f, 0x91ce1a9a3d2cda63},
|
||||
{0xdc5c5301c56b75f7, 0x7641a140cc7810fc},
|
||||
{0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9e},
|
||||
{0xac2820d9623bf429, 0x546345fa9fbdcd45},
|
||||
{0xd732290fbacaf133, 0xa97c177947ad4096},
|
||||
{0x867f59a9d4bed6c0, 0x49ed8eabcccc485e},
|
||||
{0xa81f301449ee8c70, 0x5c68f256bfff5a75},
|
||||
{0xd226fc195c6a2f8c, 0x73832eec6fff3112},
|
||||
{0x83585d8fd9c25db7, 0xc831fd53c5ff7eac},
|
||||
{0xa42e74f3d032f525, 0xba3e7ca8b77f5e56},
|
||||
{0xcd3a1230c43fb26f, 0x28ce1bd2e55f35ec},
|
||||
{0x80444b5e7aa7cf85, 0x7980d163cf5b81b4},
|
||||
{0xa0555e361951c366, 0xd7e105bcc3326220},
|
||||
{0xc86ab5c39fa63440, 0x8dd9472bf3fefaa8},
|
||||
{0xfa856334878fc150, 0xb14f98f6f0feb952},
|
||||
{0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d4},
|
||||
{0xc3b8358109e84f07, 0x0a862f80ec4700c9},
|
||||
{0xf4a642e14c6262c8, 0xcd27bb612758c0fb},
|
||||
{0x98e7e9cccfbd7dbd, 0x8038d51cb897789d},
|
||||
{0xbf21e44003acdd2c, 0xe0470a63e6bd56c4},
|
||||
{0xeeea5d5004981478, 0x1858ccfce06cac75},
|
||||
{0x95527a5202df0ccb, 0x0f37801e0c43ebc9},
|
||||
{0xbaa718e68396cffd, 0xd30560258f54e6bb},
|
||||
{0xe950df20247c83fd, 0x47c6b82ef32a206a},
|
||||
{0x91d28b7416cdd27e, 0x4cdc331d57fa5442},
|
||||
{0xb6472e511c81471d, 0xe0133fe4adf8e953},
|
||||
{0xe3d8f9e563a198e5, 0x58180fddd97723a7},
|
||||
{0x8e679c2f5e44ff8f, 0x570f09eaa7ea7649},
|
||||
{0xb201833b35d63f73, 0x2cd2cc6551e513db},
|
||||
{0xde81e40a034bcf4f, 0xf8077f7ea65e58d2},
|
||||
{0x8b112e86420f6191, 0xfb04afaf27faf783},
|
||||
{0xadd57a27d29339f6, 0x79c5db9af1f9b564},
|
||||
{0xd94ad8b1c7380874, 0x18375281ae7822bd},
|
||||
{0x87cec76f1c830548, 0x8f2293910d0b15b6},
|
||||
{0xa9c2794ae3a3c69a, 0xb2eb3875504ddb23},
|
||||
{0xd433179d9c8cb841, 0x5fa60692a46151ec},
|
||||
{0x849feec281d7f328, 0xdbc7c41ba6bcd334},
|
||||
{0xa5c7ea73224deff3, 0x12b9b522906c0801},
|
||||
{0xcf39e50feae16bef, 0xd768226b34870a01},
|
||||
{0x81842f29f2cce375, 0xe6a1158300d46641},
|
||||
{0xa1e53af46f801c53, 0x60495ae3c1097fd1},
|
||||
{0xca5e89b18b602368, 0x385bb19cb14bdfc5},
|
||||
{0xfcf62c1dee382c42, 0x46729e03dd9ed7b6},
|
||||
{0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2},
|
||||
{0xc5a05277621be293, 0xc7098b7305241886},
|
||||
{0xf70867153aa2db38, 0xb8cbee4fc66d1ea8},
|
||||
{0x9a65406d44a5c903, 0x737f74f1dc043329},
|
||||
{0xc0fe908895cf3b44, 0x505f522e53053ff3},
|
||||
{0xf13e34aabb430a15, 0x647726b9e7c68ff0},
|
||||
{0x96c6e0eab509e64d, 0x5eca783430dc19f6},
|
||||
{0xbc789925624c5fe0, 0xb67d16413d132073},
|
||||
{0xeb96bf6ebadf77d8, 0xe41c5bd18c57e890},
|
||||
{0x933e37a534cbaae7, 0x8e91b962f7b6f15a},
|
||||
{0xb80dc58e81fe95a1, 0x723627bbb5a4adb1},
|
||||
{0xe61136f2227e3b09, 0xcec3b1aaa30dd91d},
|
||||
{0x8fcac257558ee4e6, 0x213a4f0aa5e8a7b2},
|
||||
{0xb3bd72ed2af29e1f, 0xa988e2cd4f62d19e},
|
||||
{0xe0accfa875af45a7, 0x93eb1b80a33b8606},
|
||||
{0x8c6c01c9498d8b88, 0xbc72f130660533c4},
|
||||
{0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5},
|
||||
{ 0xdb68c2ca82ed2a05,
|
||||
0xa67398db9f6820e2 }
|
||||
#else
|
||||
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
|
||||
{0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
|
||||
@@ -1768,17 +1001,17 @@ template <> struct cache_accessor<double> {
|
||||
{0xf1c90080baf72cb1, 0x5324c68b12dd6339},
|
||||
{0xc350000000000000, 0x0000000000000000},
|
||||
{0x9dc5ada82b70b59d, 0xf020000000000000},
|
||||
{0xfee50b7025c36a08, 0x02f236d04753d5b4},
|
||||
{0xcde6fd5e09abcf26, 0xed4c0226b55e6f86},
|
||||
{0xa6539930bf6bff45, 0x84db8346b786151c},
|
||||
{0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2},
|
||||
{0xd910f7ff28069da4, 0x1b2ba1518094da04},
|
||||
{0xaf58416654a6babb, 0x387ac8d1970027b2},
|
||||
{0x8da471a9de737e24, 0x5ceaecfed289e5d2},
|
||||
{0xe4d5e82392a40515, 0x0fabaf3feaa5334a},
|
||||
{0xb8da1662e7b00a17, 0x3d6a751f3b936243},
|
||||
{ 0x95527a5202df0ccb,
|
||||
0x0f37801e0c43ebc8 }
|
||||
{0xfee50b7025c36a08, 0x02f236d04753d5b5},
|
||||
{0xcde6fd5e09abcf26, 0xed4c0226b55e6f87},
|
||||
{0xa6539930bf6bff45, 0x84db8346b786151d},
|
||||
{0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3},
|
||||
{0xd910f7ff28069da4, 0x1b2ba1518094da05},
|
||||
{0xaf58416654a6babb, 0x387ac8d1970027b3},
|
||||
{0x8da471a9de737e24, 0x5ceaecfed289e5d3},
|
||||
{0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
|
||||
{0xb8da1662e7b00a17, 0x3d6a751f3b936244},
|
||||
{0x95527a5202df0ccb, 0x0f37801e0c43ebc9},
|
||||
{0xf13e34aabb430a15, 0x647726b9e7c68ff0}
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -1796,15 +1029,6 @@ template <> struct cache_accessor<double> {
|
||||
0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed,
|
||||
0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9};
|
||||
|
||||
static constexpr const uint32_t pow10_recovery_errors[] = {
|
||||
0x50001400, 0x54044100, 0x54014555, 0x55954415, 0x54115555, 0x00000001,
|
||||
0x50000000, 0x00104000, 0x54010004, 0x05004001, 0x55555544, 0x41545555,
|
||||
0x54040551, 0x15445545, 0x51555514, 0x10000015, 0x00101100, 0x01100015,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04450514, 0x45414110,
|
||||
0x55555145, 0x50544050, 0x15040155, 0x11054140, 0x50111514, 0x11451454,
|
||||
0x00400541, 0x00000000, 0x55555450, 0x10056551, 0x10054011, 0x55551014,
|
||||
0x69514555, 0x05151109, 0x00155555};
|
||||
|
||||
static const int compression_ratio = 27;
|
||||
|
||||
// Compute base index.
|
||||
@@ -1813,7 +1037,7 @@ template <> struct cache_accessor<double> {
|
||||
int offset = k - kb;
|
||||
|
||||
// Get base cache.
|
||||
uint128_wrapper base_cache = pow10_significands[cache_index];
|
||||
uint128_fallback base_cache = pow10_significands[cache_index];
|
||||
if (offset == 0) return base_cache;
|
||||
|
||||
// Compute the required amount of bit-shift.
|
||||
@@ -1822,9 +1046,8 @@ template <> struct cache_accessor<double> {
|
||||
|
||||
// Try to recover the real cache.
|
||||
uint64_t pow5 = powers_of_5_64[offset];
|
||||
uint128_wrapper recovered_cache = umul128(base_cache.high(), pow5);
|
||||
uint128_wrapper middle_low =
|
||||
umul128(base_cache.low() - (kb < 0 ? 1u : 0u), pow5);
|
||||
uint128_fallback recovered_cache = umul128(base_cache.high(), pow5);
|
||||
uint128_fallback middle_low = umul128(base_cache.low(), pow5);
|
||||
|
||||
recovered_cache += middle_low.high();
|
||||
|
||||
@@ -1832,227 +1055,172 @@ template <> struct cache_accessor<double> {
|
||||
uint64_t middle_to_low = recovered_cache.low() << (64 - alpha);
|
||||
|
||||
recovered_cache =
|
||||
uint128_wrapper{(recovered_cache.low() >> alpha) | high_to_middle,
|
||||
((middle_low.low() >> alpha) | middle_to_low)};
|
||||
|
||||
if (kb < 0) recovered_cache += 1;
|
||||
|
||||
// Get error.
|
||||
int error_idx = (k - float_info<double>::min_k) / 16;
|
||||
uint32_t error = (pow10_recovery_errors[error_idx] >>
|
||||
((k - float_info<double>::min_k) % 16) * 2) &
|
||||
0x3;
|
||||
|
||||
// Add the error back.
|
||||
FMT_ASSERT(recovered_cache.low() + error >= recovered_cache.low(), "");
|
||||
return {recovered_cache.high(), recovered_cache.low() + error};
|
||||
uint128_fallback{(recovered_cache.low() >> alpha) | high_to_middle,
|
||||
((middle_low.low() >> alpha) | middle_to_low)};
|
||||
FMT_ASSERT(recovered_cache.low() + 1 != 0, "");
|
||||
return {recovered_cache.high(), recovered_cache.low() + 1};
|
||||
#endif
|
||||
}
|
||||
|
||||
static carrier_uint compute_mul(carrier_uint u,
|
||||
const cache_entry_type& cache) FMT_NOEXCEPT {
|
||||
return umul192_upper64(u, cache);
|
||||
struct compute_mul_result {
|
||||
carrier_uint result;
|
||||
bool is_integer;
|
||||
};
|
||||
struct compute_mul_parity_result {
|
||||
bool parity;
|
||||
bool is_integer;
|
||||
};
|
||||
|
||||
static compute_mul_result compute_mul(
|
||||
carrier_uint u, const cache_entry_type& cache) noexcept {
|
||||
auto r = umul192_upper128(u, cache);
|
||||
return {r.high(), r.low() == 0};
|
||||
}
|
||||
|
||||
static uint32_t compute_delta(cache_entry_type const& cache,
|
||||
int beta_minus_1) FMT_NOEXCEPT {
|
||||
return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta_minus_1));
|
||||
int beta) noexcept {
|
||||
return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
|
||||
}
|
||||
|
||||
static bool compute_mul_parity(carrier_uint two_f,
|
||||
const cache_entry_type& cache,
|
||||
int beta_minus_1) FMT_NOEXCEPT {
|
||||
FMT_ASSERT(beta_minus_1 >= 1, "");
|
||||
FMT_ASSERT(beta_minus_1 < 64, "");
|
||||
static compute_mul_parity_result compute_mul_parity(
|
||||
carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
|
||||
FMT_ASSERT(beta >= 1, "");
|
||||
FMT_ASSERT(beta < 64, "");
|
||||
|
||||
return ((umul192_middle64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0;
|
||||
auto r = umul192_lower128(two_f, cache);
|
||||
return {((r.high() >> (64 - beta)) & 1) != 0,
|
||||
((r.high() << beta) | (r.low() >> (64 - beta))) == 0};
|
||||
}
|
||||
|
||||
static carrier_uint compute_left_endpoint_for_shorter_interval_case(
|
||||
const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
|
||||
const cache_entry_type& cache, int beta) noexcept {
|
||||
return (cache.high() -
|
||||
(cache.high() >> (float_info<double>::significand_bits + 2))) >>
|
||||
(64 - float_info<double>::significand_bits - 1 - beta_minus_1);
|
||||
(cache.high() >> (num_significand_bits<double>() + 2))) >>
|
||||
(64 - num_significand_bits<double>() - 1 - beta);
|
||||
}
|
||||
|
||||
static carrier_uint compute_right_endpoint_for_shorter_interval_case(
|
||||
const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
|
||||
const cache_entry_type& cache, int beta) noexcept {
|
||||
return (cache.high() +
|
||||
(cache.high() >> (float_info<double>::significand_bits + 1))) >>
|
||||
(64 - float_info<double>::significand_bits - 1 - beta_minus_1);
|
||||
(cache.high() >> (num_significand_bits<double>() + 1))) >>
|
||||
(64 - num_significand_bits<double>() - 1 - beta);
|
||||
}
|
||||
|
||||
static carrier_uint compute_round_up_for_shorter_interval_case(
|
||||
const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
|
||||
return ((cache.high() >>
|
||||
(64 - float_info<double>::significand_bits - 2 - beta_minus_1)) +
|
||||
const cache_entry_type& cache, int beta) noexcept {
|
||||
return ((cache.high() >> (64 - num_significand_bits<double>() - 2 - beta)) +
|
||||
1) /
|
||||
2;
|
||||
}
|
||||
};
|
||||
|
||||
// Various integer checks
|
||||
template <class T>
|
||||
bool is_left_endpoint_integer_shorter_interval(int exponent) FMT_NOEXCEPT {
|
||||
return exponent >=
|
||||
float_info<
|
||||
T>::case_shorter_interval_left_endpoint_lower_threshold &&
|
||||
exponent <=
|
||||
float_info<T>::case_shorter_interval_left_endpoint_upper_threshold;
|
||||
}
|
||||
template <class T>
|
||||
bool is_endpoint_integer(typename float_info<T>::carrier_uint two_f,
|
||||
int exponent, int minus_k) FMT_NOEXCEPT {
|
||||
if (exponent < float_info<T>::case_fc_pm_half_lower_threshold) return false;
|
||||
// For k >= 0.
|
||||
if (exponent <= float_info<T>::case_fc_pm_half_upper_threshold) return true;
|
||||
// For k < 0.
|
||||
if (exponent > float_info<T>::divisibility_check_by_5_threshold) return false;
|
||||
return divisible_by_power_of_5(two_f, minus_k);
|
||||
FMT_FUNC uint128_fallback get_cached_power(int k) noexcept {
|
||||
return cache_accessor<double>::get_cached_power(k);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool is_center_integer(typename float_info<T>::carrier_uint two_f, int exponent,
|
||||
int minus_k) FMT_NOEXCEPT {
|
||||
// Exponent for 5 is negative.
|
||||
if (exponent > float_info<T>::divisibility_check_by_5_threshold) return false;
|
||||
if (exponent > float_info<T>::case_fc_upper_threshold)
|
||||
return divisible_by_power_of_5(two_f, minus_k);
|
||||
// Both exponents are nonnegative.
|
||||
if (exponent >= float_info<T>::case_fc_lower_threshold) return true;
|
||||
// Exponent for 2 is negative.
|
||||
return divisible_by_power_of_2(two_f, minus_k - exponent + 1);
|
||||
// Various integer checks
|
||||
template <typename T>
|
||||
bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
|
||||
const int case_shorter_interval_left_endpoint_lower_threshold = 2;
|
||||
const int case_shorter_interval_left_endpoint_upper_threshold = 3;
|
||||
return exponent >= case_shorter_interval_left_endpoint_lower_threshold &&
|
||||
exponent <= case_shorter_interval_left_endpoint_upper_threshold;
|
||||
}
|
||||
|
||||
// Remove trailing zeros from n and return the number of zeros removed (float)
|
||||
FMT_INLINE int remove_trailing_zeros(uint32_t& n) FMT_NOEXCEPT {
|
||||
#ifdef FMT_BUILTIN_CTZ
|
||||
int t = FMT_BUILTIN_CTZ(n);
|
||||
#else
|
||||
int t = ctz(n);
|
||||
#endif
|
||||
if (t > float_info<float>::max_trailing_zeros)
|
||||
t = float_info<float>::max_trailing_zeros;
|
||||
|
||||
const uint32_t mod_inv1 = 0xcccccccd;
|
||||
const uint32_t max_quotient1 = 0x33333333;
|
||||
const uint32_t mod_inv2 = 0xc28f5c29;
|
||||
const uint32_t max_quotient2 = 0x0a3d70a3;
|
||||
FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept {
|
||||
FMT_ASSERT(n != 0, "");
|
||||
// Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
|
||||
// See https://github.com/fmtlib/fmt/issues/3163 for more details.
|
||||
const uint32_t mod_inv_5 = 0xcccccccd;
|
||||
// Casts are needed to workaround a bug in MSVC 19.22 and older.
|
||||
const uint32_t mod_inv_25 =
|
||||
static_cast<uint32_t>(uint64_t(mod_inv_5) * mod_inv_5);
|
||||
|
||||
int s = 0;
|
||||
for (; s < t - 1; s += 2) {
|
||||
if (n * mod_inv2 > max_quotient2) break;
|
||||
n *= mod_inv2;
|
||||
while (true) {
|
||||
auto q = rotr(n * mod_inv_25, 2);
|
||||
if (q > max_value<uint32_t>() / 100) break;
|
||||
n = q;
|
||||
s += 2;
|
||||
}
|
||||
if (s < t && n * mod_inv1 <= max_quotient1) {
|
||||
n *= mod_inv1;
|
||||
++s;
|
||||
auto q = rotr(n * mod_inv_5, 1);
|
||||
if (q <= max_value<uint32_t>() / 10) {
|
||||
n = q;
|
||||
s |= 1;
|
||||
}
|
||||
n >>= s;
|
||||
return s;
|
||||
}
|
||||
|
||||
// Removes trailing zeros and returns the number of zeros removed (double)
|
||||
FMT_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT {
|
||||
#ifdef FMT_BUILTIN_CTZLL
|
||||
int t = FMT_BUILTIN_CTZLL(n);
|
||||
#else
|
||||
int t = ctzll(n);
|
||||
#endif
|
||||
if (t > float_info<double>::max_trailing_zeros)
|
||||
t = float_info<double>::max_trailing_zeros;
|
||||
// Divide by 10^8 and reduce to 32-bits
|
||||
// Since ret_value.significand <= (2^64 - 1) / 1000 < 10^17,
|
||||
// both of the quotient and the r should fit in 32-bits
|
||||
FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
|
||||
FMT_ASSERT(n != 0, "");
|
||||
|
||||
const uint32_t mod_inv1 = 0xcccccccd;
|
||||
const uint32_t max_quotient1 = 0x33333333;
|
||||
const uint64_t mod_inv8 = 0xc767074b22e90e21;
|
||||
const uint64_t max_quotient8 = 0x00002af31dc46118;
|
||||
// This magic number is ceil(2^90 / 10^8).
|
||||
constexpr uint64_t magic_number = 12379400392853802749ull;
|
||||
auto nm = umul128(n, magic_number);
|
||||
|
||||
// If the number is divisible by 1'0000'0000, work with the quotient
|
||||
if (t >= 8) {
|
||||
auto quotient_candidate = n * mod_inv8;
|
||||
// Is n is divisible by 10^8?
|
||||
if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
|
||||
// If yes, work with the quotient.
|
||||
auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
|
||||
|
||||
if (quotient_candidate <= max_quotient8) {
|
||||
auto quotient = static_cast<uint32_t>(quotient_candidate >> 8);
|
||||
const uint32_t mod_inv_5 = 0xcccccccd;
|
||||
const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
|
||||
|
||||
int s = 8;
|
||||
for (; s < t; ++s) {
|
||||
if (quotient * mod_inv1 > max_quotient1) break;
|
||||
quotient *= mod_inv1;
|
||||
}
|
||||
quotient >>= (s - 8);
|
||||
n = quotient;
|
||||
return s;
|
||||
int s = 8;
|
||||
while (true) {
|
||||
auto q = rotr(n32 * mod_inv_25, 2);
|
||||
if (q > max_value<uint32_t>() / 100) break;
|
||||
n32 = q;
|
||||
s += 2;
|
||||
}
|
||||
auto q = rotr(n32 * mod_inv_5, 1);
|
||||
if (q <= max_value<uint32_t>() / 10) {
|
||||
n32 = q;
|
||||
s |= 1;
|
||||
}
|
||||
|
||||
n = n32;
|
||||
return s;
|
||||
}
|
||||
|
||||
// Otherwise, work with the remainder
|
||||
auto quotient = static_cast<uint32_t>(n / 100000000);
|
||||
auto remainder = static_cast<uint32_t>(n - 100000000 * quotient);
|
||||
// If n is not divisible by 10^8, work with n itself.
|
||||
const uint64_t mod_inv_5 = 0xcccccccccccccccd;
|
||||
const uint64_t mod_inv_25 = mod_inv_5 * mod_inv_5;
|
||||
|
||||
if (t == 0 || remainder * mod_inv1 > max_quotient1) {
|
||||
return 0;
|
||||
int s = 0;
|
||||
while (true) {
|
||||
auto q = rotr(n * mod_inv_25, 2);
|
||||
if (q > max_value<uint64_t>() / 100) break;
|
||||
n = q;
|
||||
s += 2;
|
||||
}
|
||||
remainder *= mod_inv1;
|
||||
|
||||
if (t == 1 || remainder * mod_inv1 > max_quotient1) {
|
||||
n = (remainder >> 1) + quotient * 10000000ull;
|
||||
return 1;
|
||||
auto q = rotr(n * mod_inv_5, 1);
|
||||
if (q <= max_value<uint64_t>() / 10) {
|
||||
n = q;
|
||||
s |= 1;
|
||||
}
|
||||
remainder *= mod_inv1;
|
||||
|
||||
if (t == 2 || remainder * mod_inv1 > max_quotient1) {
|
||||
n = (remainder >> 2) + quotient * 1000000ull;
|
||||
return 2;
|
||||
}
|
||||
remainder *= mod_inv1;
|
||||
|
||||
if (t == 3 || remainder * mod_inv1 > max_quotient1) {
|
||||
n = (remainder >> 3) + quotient * 100000ull;
|
||||
return 3;
|
||||
}
|
||||
remainder *= mod_inv1;
|
||||
|
||||
if (t == 4 || remainder * mod_inv1 > max_quotient1) {
|
||||
n = (remainder >> 4) + quotient * 10000ull;
|
||||
return 4;
|
||||
}
|
||||
remainder *= mod_inv1;
|
||||
|
||||
if (t == 5 || remainder * mod_inv1 > max_quotient1) {
|
||||
n = (remainder >> 5) + quotient * 1000ull;
|
||||
return 5;
|
||||
}
|
||||
remainder *= mod_inv1;
|
||||
|
||||
if (t == 6 || remainder * mod_inv1 > max_quotient1) {
|
||||
n = (remainder >> 6) + quotient * 100ull;
|
||||
return 6;
|
||||
}
|
||||
remainder *= mod_inv1;
|
||||
|
||||
n = (remainder >> 7) + quotient * 10ull;
|
||||
return 7;
|
||||
return s;
|
||||
}
|
||||
|
||||
// The main algorithm for shorter interval case
|
||||
template <class T>
|
||||
FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) FMT_NOEXCEPT {
|
||||
template <typename T>
|
||||
FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
|
||||
decimal_fp<T> ret_value;
|
||||
// Compute k and beta
|
||||
const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
|
||||
const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k);
|
||||
const int beta = exponent + floor_log2_pow10(-minus_k);
|
||||
|
||||
// Compute xi and zi
|
||||
using cache_entry_type = typename cache_accessor<T>::cache_entry_type;
|
||||
const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k);
|
||||
|
||||
auto xi = cache_accessor<T>::compute_left_endpoint_for_shorter_interval_case(
|
||||
cache, beta_minus_1);
|
||||
cache, beta);
|
||||
auto zi = cache_accessor<T>::compute_right_endpoint_for_shorter_interval_case(
|
||||
cache, beta_minus_1);
|
||||
cache, beta);
|
||||
|
||||
// If the left endpoint is not an integer, increase it
|
||||
if (!is_left_endpoint_integer_shorter_interval<T>(exponent)) ++xi;
|
||||
@@ -2069,8 +1237,8 @@ FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) FMT_NOEXCEPT {
|
||||
|
||||
// Otherwise, compute the round-up of y
|
||||
ret_value.significand =
|
||||
cache_accessor<T>::compute_round_up_for_shorter_interval_case(
|
||||
cache, beta_minus_1);
|
||||
cache_accessor<T>::compute_round_up_for_shorter_interval_case(cache,
|
||||
beta);
|
||||
ret_value.exponent = minus_k;
|
||||
|
||||
// When tie occurs, choose one of them according to the rule
|
||||
@@ -2085,7 +1253,7 @@ FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) FMT_NOEXCEPT {
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
template <typename T> decimal_fp<T> to_decimal(T x) FMT_NOEXCEPT {
|
||||
template <typename T> decimal_fp<T> to_decimal(T x) noexcept {
|
||||
// Step 1: integer promotion & Schubfach multiplier calculation.
|
||||
|
||||
using carrier_uint = typename float_info<T>::carrier_uint;
|
||||
@@ -2094,23 +1262,25 @@ template <typename T> decimal_fp<T> to_decimal(T x) FMT_NOEXCEPT {
|
||||
|
||||
// Extract significand bits and exponent bits.
|
||||
const carrier_uint significand_mask =
|
||||
(static_cast<carrier_uint>(1) << float_info<T>::significand_bits) - 1;
|
||||
(static_cast<carrier_uint>(1) << num_significand_bits<T>()) - 1;
|
||||
carrier_uint significand = (br & significand_mask);
|
||||
int exponent = static_cast<int>((br & exponent_mask<T>()) >>
|
||||
float_info<T>::significand_bits);
|
||||
int exponent =
|
||||
static_cast<int>((br & exponent_mask<T>()) >> num_significand_bits<T>());
|
||||
|
||||
if (exponent != 0) { // Check if normal.
|
||||
exponent += float_info<T>::exponent_bias - float_info<T>::significand_bits;
|
||||
exponent -= exponent_bias<T>() + num_significand_bits<T>();
|
||||
|
||||
// Shorter interval case; proceed like Schubfach.
|
||||
// In fact, when exponent == 1 and significand == 0, the interval is
|
||||
// regular. However, it can be shown that the end-results are anyway same.
|
||||
if (significand == 0) return shorter_interval_case<T>(exponent);
|
||||
|
||||
significand |=
|
||||
(static_cast<carrier_uint>(1) << float_info<T>::significand_bits);
|
||||
significand |= (static_cast<carrier_uint>(1) << num_significand_bits<T>());
|
||||
} else {
|
||||
// Subnormal case; the interval is always regular.
|
||||
if (significand == 0) return {0, 0};
|
||||
exponent = float_info<T>::min_exponent - float_info<T>::significand_bits;
|
||||
exponent =
|
||||
std::numeric_limits<T>::min_exponent - num_significand_bits<T>() - 1;
|
||||
}
|
||||
|
||||
const bool include_left_endpoint = (significand % 2 == 0);
|
||||
@@ -2119,413 +1289,104 @@ template <typename T> decimal_fp<T> to_decimal(T x) FMT_NOEXCEPT {
|
||||
// Compute k and beta.
|
||||
const int minus_k = floor_log10_pow2(exponent) - float_info<T>::kappa;
|
||||
const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k);
|
||||
const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k);
|
||||
const int beta = exponent + floor_log2_pow10(-minus_k);
|
||||
|
||||
// Compute zi and deltai
|
||||
// Compute zi and deltai.
|
||||
// 10^kappa <= deltai < 10^(kappa + 1)
|
||||
const uint32_t deltai = cache_accessor<T>::compute_delta(cache, beta_minus_1);
|
||||
const uint32_t deltai = cache_accessor<T>::compute_delta(cache, beta);
|
||||
const carrier_uint two_fc = significand << 1;
|
||||
const carrier_uint two_fr = two_fc | 1;
|
||||
const carrier_uint zi =
|
||||
cache_accessor<T>::compute_mul(two_fr << beta_minus_1, cache);
|
||||
|
||||
// Step 2: Try larger divisor; remove trailing zeros if necessary
|
||||
// For the case of binary32, the result of integer check is not correct for
|
||||
// 29711844 * 2^-82
|
||||
// = 6.1442653300000000008655037797566933477355632930994033813476... * 10^-18
|
||||
// and 29711844 * 2^-81
|
||||
// = 1.2288530660000000001731007559513386695471126586198806762695... * 10^-17,
|
||||
// and they are the unique counterexamples. However, since 29711844 is even,
|
||||
// this does not cause any problem for the endpoints calculations; it can only
|
||||
// cause a problem when we need to perform integer check for the center.
|
||||
// Fortunately, with these inputs, that branch is never executed, so we are
|
||||
// fine.
|
||||
const typename cache_accessor<T>::compute_mul_result z_mul =
|
||||
cache_accessor<T>::compute_mul((two_fc | 1) << beta, cache);
|
||||
|
||||
// Step 2: Try larger divisor; remove trailing zeros if necessary.
|
||||
|
||||
// Using an upper bound on zi, we might be able to optimize the division
|
||||
// better than the compiler; we are computing zi / big_divisor here
|
||||
// better than the compiler; we are computing zi / big_divisor here.
|
||||
decimal_fp<T> ret_value;
|
||||
ret_value.significand = divide_by_10_to_kappa_plus_1(zi);
|
||||
uint32_t r = static_cast<uint32_t>(zi - float_info<T>::big_divisor *
|
||||
ret_value.significand);
|
||||
ret_value.significand = divide_by_10_to_kappa_plus_1(z_mul.result);
|
||||
uint32_t r = static_cast<uint32_t>(z_mul.result - float_info<T>::big_divisor *
|
||||
ret_value.significand);
|
||||
|
||||
if (r > deltai) {
|
||||
goto small_divisor_case_label;
|
||||
} else if (r < deltai) {
|
||||
// Exclude the right endpoint if necessary
|
||||
if (r == 0 && !include_right_endpoint &&
|
||||
is_endpoint_integer<T>(two_fr, exponent, minus_k)) {
|
||||
if (r < deltai) {
|
||||
// Exclude the right endpoint if necessary.
|
||||
if (r == 0 && (z_mul.is_integer & !include_right_endpoint)) {
|
||||
--ret_value.significand;
|
||||
r = float_info<T>::big_divisor;
|
||||
goto small_divisor_case_label;
|
||||
}
|
||||
} else if (r > deltai) {
|
||||
goto small_divisor_case_label;
|
||||
} else {
|
||||
// r == deltai; compare fractional parts
|
||||
// Check conditions in the order different from the paper
|
||||
// to take advantage of short-circuiting
|
||||
const carrier_uint two_fl = two_fc - 1;
|
||||
if ((!include_left_endpoint ||
|
||||
!is_endpoint_integer<T>(two_fl, exponent, minus_k)) &&
|
||||
!cache_accessor<T>::compute_mul_parity(two_fl, cache, beta_minus_1)) {
|
||||
// r == deltai; compare fractional parts.
|
||||
const typename cache_accessor<T>::compute_mul_parity_result x_mul =
|
||||
cache_accessor<T>::compute_mul_parity(two_fc - 1, cache, beta);
|
||||
|
||||
if (!(x_mul.parity | (x_mul.is_integer & include_left_endpoint)))
|
||||
goto small_divisor_case_label;
|
||||
}
|
||||
}
|
||||
ret_value.exponent = minus_k + float_info<T>::kappa + 1;
|
||||
|
||||
// We may need to remove trailing zeros
|
||||
// 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<T>::kappa;
|
||||
|
||||
const uint32_t mask = (1u << float_info<T>::kappa) - 1;
|
||||
auto dist = r - (deltai / 2) + (float_info<T>::small_divisor / 2);
|
||||
uint32_t dist = r - (deltai / 2) + (float_info<T>::small_divisor / 2);
|
||||
const bool approx_y_parity =
|
||||
((dist ^ (float_info<T>::small_divisor / 2)) & 1) != 0;
|
||||
|
||||
// Is dist divisible by 2^kappa?
|
||||
if ((dist & mask) == 0) {
|
||||
const bool approx_y_parity =
|
||||
((dist ^ (float_info<T>::small_divisor / 2)) & 1) != 0;
|
||||
dist >>= float_info<T>::kappa;
|
||||
// Is dist divisible by 10^kappa?
|
||||
const bool divisible_by_small_divisor =
|
||||
check_divisibility_and_divide_by_pow10<float_info<T>::kappa>(dist);
|
||||
|
||||
// Is dist divisible by 5^kappa?
|
||||
if (check_divisibility_and_divide_by_pow5<float_info<T>::kappa>(dist)) {
|
||||
ret_value.significand += dist;
|
||||
// Add dist / 10^kappa to the significand.
|
||||
ret_value.significand += dist;
|
||||
|
||||
// Check z^(f) >= epsilon^(f)
|
||||
// We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1,
|
||||
// where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f)
|
||||
// Since there are only 2 possibilities, we only need to care about the
|
||||
// parity. Also, zi and r should have the same parity since the divisor
|
||||
// is an even number
|
||||
if (cache_accessor<T>::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<T>(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<float_info<T>::kappa>(dist);
|
||||
}
|
||||
if (!divisible_by_small_divisor) return ret_value;
|
||||
|
||||
// Check z^(f) >= epsilon^(f).
|
||||
// We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1,
|
||||
// where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f).
|
||||
// Since there are only 2 possibilities, we only need to care about the
|
||||
// parity. Also, zi and r should have the same parity since the divisor
|
||||
// is an even number.
|
||||
const auto y_mul = cache_accessor<T>::compute_mul_parity(two_fc, cache, beta);
|
||||
|
||||
// If z^(f) >= epsilon^(f), we might have a tie when z^(f) == epsilon^(f),
|
||||
// or equivalently, when y is an integer.
|
||||
if (y_mul.parity != approx_y_parity)
|
||||
--ret_value.significand;
|
||||
else if (y_mul.is_integer & (ret_value.significand % 2 != 0))
|
||||
--ret_value.significand;
|
||||
return ret_value;
|
||||
}
|
||||
} // namespace dragonbox
|
||||
|
||||
// Formats a floating-point number using a variation of the Fixed-Precision
|
||||
// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
|
||||
// https://fmt.dev/papers/p372-steele.pdf.
|
||||
FMT_CONSTEXPR20 inline void format_dragon(fp value, bool is_predecessor_closer,
|
||||
int num_digits, buffer<char>& buf,
|
||||
int& exp10) {
|
||||
bigint numerator; // 2 * R in (FPP)^2.
|
||||
bigint denominator; // 2 * S in (FPP)^2.
|
||||
// lower and upper are differences between value and corresponding boundaries.
|
||||
bigint lower; // (M^- in (FPP)^2).
|
||||
bigint upper_store; // upper's value if different from lower.
|
||||
bigint* upper = nullptr; // (M^+ in (FPP)^2).
|
||||
// Shift numerator and denominator by an extra bit or two (if lower boundary
|
||||
// is closer) to make lower and upper integers. This eliminates multiplication
|
||||
// by 2 during later computations.
|
||||
int shift = is_predecessor_closer ? 2 : 1;
|
||||
uint64_t significand = value.f << shift;
|
||||
if (value.e >= 0) {
|
||||
numerator.assign(significand);
|
||||
numerator <<= value.e;
|
||||
lower.assign(1);
|
||||
lower <<= value.e;
|
||||
if (shift != 1) {
|
||||
upper_store.assign(1);
|
||||
upper_store <<= value.e + 1;
|
||||
upper = &upper_store;
|
||||
}
|
||||
denominator.assign_pow10(exp10);
|
||||
denominator <<= shift;
|
||||
} else if (exp10 < 0) {
|
||||
numerator.assign_pow10(-exp10);
|
||||
lower.assign(numerator);
|
||||
if (shift != 1) {
|
||||
upper_store.assign(numerator);
|
||||
upper_store <<= 1;
|
||||
upper = &upper_store;
|
||||
}
|
||||
numerator *= significand;
|
||||
denominator.assign(1);
|
||||
denominator <<= shift - value.e;
|
||||
} else {
|
||||
numerator.assign(significand);
|
||||
denominator.assign_pow10(exp10);
|
||||
denominator <<= shift - value.e;
|
||||
lower.assign(1);
|
||||
if (shift != 1) {
|
||||
upper_store.assign(1ULL << 1);
|
||||
upper = &upper_store;
|
||||
}
|
||||
}
|
||||
// Invariant: value == (numerator / denominator) * pow(10, exp10).
|
||||
if (num_digits < 0) {
|
||||
// Generate the shortest representation.
|
||||
if (!upper) upper = &lower;
|
||||
bool even = (value.f & 1) == 0;
|
||||
num_digits = 0;
|
||||
char* data = buf.data();
|
||||
for (;;) {
|
||||
int digit = numerator.divmod_assign(denominator);
|
||||
bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower.
|
||||
// numerator + upper >[=] pow10:
|
||||
bool high = add_compare(numerator, *upper, denominator) + even > 0;
|
||||
data[num_digits++] = static_cast<char>('0' + digit);
|
||||
if (low || high) {
|
||||
if (!low) {
|
||||
++data[num_digits - 1];
|
||||
} else if (high) {
|
||||
int result = add_compare(numerator, numerator, denominator);
|
||||
// Round half to even.
|
||||
if (result > 0 || (result == 0 && (digit % 2) != 0))
|
||||
++data[num_digits - 1];
|
||||
}
|
||||
buf.try_resize(to_unsigned(num_digits));
|
||||
exp10 -= num_digits - 1;
|
||||
return;
|
||||
}
|
||||
numerator *= 10;
|
||||
lower *= 10;
|
||||
if (upper != &lower) *upper *= 10;
|
||||
}
|
||||
}
|
||||
// Generate the given number of digits.
|
||||
exp10 -= num_digits - 1;
|
||||
if (num_digits == 0) {
|
||||
denominator *= 10;
|
||||
auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
|
||||
buf.push_back(digit);
|
||||
return;
|
||||
}
|
||||
buf.try_resize(to_unsigned(num_digits));
|
||||
for (int i = 0; i < num_digits - 1; ++i) {
|
||||
int digit = numerator.divmod_assign(denominator);
|
||||
buf[i] = static_cast<char>('0' + digit);
|
||||
numerator *= 10;
|
||||
}
|
||||
int digit = numerator.divmod_assign(denominator);
|
||||
auto result = add_compare(numerator, numerator, denominator);
|
||||
if (result > 0 || (result == 0 && (digit % 2) != 0)) {
|
||||
if (digit == 9) {
|
||||
const auto overflow = '0' + 10;
|
||||
buf[num_digits - 1] = overflow;
|
||||
// Propagate the carry.
|
||||
for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) {
|
||||
buf[i] = '0';
|
||||
++buf[i - 1];
|
||||
}
|
||||
if (buf[0] == overflow) {
|
||||
buf[0] = '1';
|
||||
++exp10;
|
||||
}
|
||||
return;
|
||||
}
|
||||
++digit;
|
||||
}
|
||||
buf[num_digits - 1] = static_cast<char>('0' + digit);
|
||||
}
|
||||
|
||||
template <typename Float>
|
||||
FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision,
|
||||
float_specs specs,
|
||||
buffer<char>& buf) {
|
||||
// float is passed as double to reduce the number of instantiations.
|
||||
static_assert(!std::is_same<Float, float>::value, "");
|
||||
FMT_ASSERT(value >= 0, "value is negative");
|
||||
|
||||
const bool fixed = specs.format == float_format::fixed;
|
||||
if (value <= 0) { // <= instead of == to silence a warning.
|
||||
if (precision <= 0 || !fixed) {
|
||||
buf.push_back('0');
|
||||
return 0;
|
||||
}
|
||||
buf.try_resize(to_unsigned(precision));
|
||||
fill_n(buf.data(), precision, '0');
|
||||
return -precision;
|
||||
}
|
||||
|
||||
if (specs.fallback) return snprintf_float(value, precision, specs, buf);
|
||||
|
||||
if (!is_constant_evaluated() && precision < 0) {
|
||||
// Use Dragonbox for the shortest format.
|
||||
if (specs.binary32) {
|
||||
auto dec = dragonbox::to_decimal(static_cast<float>(value));
|
||||
write<char>(buffer_appender<char>(buf), dec.significand);
|
||||
return dec.exponent;
|
||||
}
|
||||
auto dec = dragonbox::to_decimal(static_cast<double>(value));
|
||||
write<char>(buffer_appender<char>(buf), dec.significand);
|
||||
return dec.exponent;
|
||||
}
|
||||
|
||||
int exp = 0;
|
||||
bool use_dragon = true;
|
||||
if (is_fast_float<Float>()) {
|
||||
// Use Grisu + Dragon4 for the given precision:
|
||||
// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
|
||||
const int min_exp = -60; // alpha in Grisu.
|
||||
int cached_exp10 = 0; // K in Grisu.
|
||||
fp normalized = normalize(fp(value));
|
||||
const auto cached_pow = get_cached_power(
|
||||
min_exp - (normalized.e + fp::num_significand_bits), cached_exp10);
|
||||
normalized = normalized * cached_pow;
|
||||
gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
|
||||
if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error &&
|
||||
!is_constant_evaluated()) {
|
||||
exp += handler.exp10;
|
||||
buf.try_resize(to_unsigned(handler.size));
|
||||
use_dragon = false;
|
||||
} else {
|
||||
exp += handler.size - cached_exp10 - 1;
|
||||
precision = handler.precision;
|
||||
}
|
||||
}
|
||||
if (use_dragon) {
|
||||
auto f = fp();
|
||||
bool is_predecessor_closer =
|
||||
specs.binary32 ? f.assign(static_cast<float>(value)) : f.assign(value);
|
||||
// Limit precision to the maximum possible number of significant digits in
|
||||
// an IEEE754 double because we don't need to generate zeros.
|
||||
const int max_double_digits = 767;
|
||||
if (precision > max_double_digits) precision = max_double_digits;
|
||||
format_dragon(f, is_predecessor_closer, precision, buf, exp);
|
||||
}
|
||||
if (!fixed && !specs.showpoint) {
|
||||
// Remove trailing zeros.
|
||||
auto num_digits = buf.size();
|
||||
while (num_digits > 0 && buf[num_digits - 1] == '0') {
|
||||
--num_digits;
|
||||
++exp;
|
||||
}
|
||||
buf.try_resize(num_digits);
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int snprintf_float(T value, int precision, float_specs specs,
|
||||
buffer<char>& 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<T, float>::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<T, long double>()) *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<int>(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<int>(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<detail::bigint> {
|
||||
FMT_CONSTEXPR format_parse_context::iterator parse(
|
||||
format_parse_context& ctx) {
|
||||
FMT_CONSTEXPR auto parse(format_parse_context& ctx)
|
||||
-> format_parse_context::iterator {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
format_context::iterator format(const detail::bigint& n,
|
||||
format_context& ctx) {
|
||||
auto format(const detail::bigint& n, format_context& ctx) const
|
||||
-> format_context::iterator {
|
||||
auto out = ctx.out();
|
||||
bool first = true;
|
||||
for (auto i = n.bigits_.size(); i > 0; --i) {
|
||||
@@ -2560,7 +1421,7 @@ FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
||||
}
|
||||
|
||||
FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
|
||||
const char* message) FMT_NOEXCEPT {
|
||||
const char* message) noexcept {
|
||||
FMT_TRY {
|
||||
auto ec = std::error_code(error_code, std::generic_category());
|
||||
write(std::back_inserter(out), std::system_error(ec, message).what());
|
||||
@@ -2571,16 +1432,10 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
|
||||
}
|
||||
|
||||
FMT_FUNC void report_system_error(int error_code,
|
||||
const char* message) FMT_NOEXCEPT {
|
||||
const char* message) noexcept {
|
||||
report_error(format_system_error, error_code, message);
|
||||
}
|
||||
|
||||
// DEPRECATED!
|
||||
// This function is defined here and not inline for ABI compatiblity.
|
||||
FMT_FUNC void detail::error_handler::on_error(const char* message) {
|
||||
throw_format_error(message);
|
||||
}
|
||||
|
||||
FMT_FUNC std::string vformat(string_view fmt, format_args args) {
|
||||
// Don't optimize the "{}" case to keep the binary size small and because it
|
||||
// can be better optimized in fmt::format anyway.
|
||||
@@ -2589,54 +1444,237 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) {
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace detail {
|
||||
#ifndef _WIN32
|
||||
FMT_FUNC bool write_console(std::FILE*, string_view) { return false; }
|
||||
#else
|
||||
using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
|
||||
extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
|
||||
void*, const void*, dword, dword*, void*);
|
||||
} // namespace detail
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
FMT_FUNC void print(std::FILE* f, string_view text) {
|
||||
#ifdef _WIN32
|
||||
FMT_FUNC bool write_console(std::FILE* f, string_view text) {
|
||||
auto fd = _fileno(f);
|
||||
if (_isatty(fd)) {
|
||||
detail::utf8_to_utf16 u16(string_view(text.data(), text.size()));
|
||||
auto written = detail::dword();
|
||||
if (detail::WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)),
|
||||
u16.c_str(), static_cast<uint32_t>(u16.size()),
|
||||
&written, nullptr)) {
|
||||
return;
|
||||
}
|
||||
// Fallback to fwrite on failure. It can happen if the output has been
|
||||
// redirected to NUL.
|
||||
}
|
||||
#endif
|
||||
detail::fwrite_fully(text.data(), 1, text.size(), f);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
|
||||
memory_buffer buffer;
|
||||
detail::vformat_to(buffer, format_str, args);
|
||||
detail::print(f, {buffer.data(), buffer.size()});
|
||||
if (!_isatty(fd)) return false;
|
||||
auto u16 = utf8_to_utf16(text);
|
||||
auto written = dword();
|
||||
return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
|
||||
static_cast<uint32_t>(u16.size()), &written, nullptr);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Print assuming legacy (non-Unicode) encoding.
|
||||
FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str,
|
||||
format_args args) {
|
||||
memory_buffer buffer;
|
||||
detail::vformat_to(buffer, format_str,
|
||||
FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) {
|
||||
auto buffer = memory_buffer();
|
||||
detail::vformat_to(buffer, fmt,
|
||||
basic_format_args<buffer_context<char>>(args));
|
||||
fwrite_fully(buffer.data(), 1, buffer.size(), f);
|
||||
}
|
||||
#endif
|
||||
|
||||
FMT_FUNC void vprint(string_view format_str, format_args args) {
|
||||
vprint(stdout, format_str, args);
|
||||
FMT_FUNC void print(std::FILE* f, string_view text) {
|
||||
if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) {
|
||||
auto buffer = memory_buffer();
|
||||
detail::vformat_to(buffer, fmt, args);
|
||||
detail::print(f, {buffer.data(), buffer.size()});
|
||||
}
|
||||
|
||||
FMT_FUNC void vprint(string_view fmt, format_args args) {
|
||||
vprint(stdout, fmt, args);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct singleton {
|
||||
unsigned char upper;
|
||||
unsigned char lower_count;
|
||||
};
|
||||
|
||||
inline auto is_printable(uint16_t x, const singleton* singletons,
|
||||
size_t singletons_size,
|
||||
const unsigned char* singleton_lowers,
|
||||
const unsigned char* normal, size_t normal_size)
|
||||
-> bool {
|
||||
auto upper = x >> 8;
|
||||
auto lower_start = 0;
|
||||
for (size_t i = 0; i < singletons_size; ++i) {
|
||||
auto s = singletons[i];
|
||||
auto lower_end = lower_start + s.lower_count;
|
||||
if (upper < s.upper) break;
|
||||
if (upper == s.upper) {
|
||||
for (auto j = lower_start; j < lower_end; ++j) {
|
||||
if (singleton_lowers[j] == (x & 0xff)) return false;
|
||||
}
|
||||
}
|
||||
lower_start = lower_end;
|
||||
}
|
||||
|
||||
auto xsigned = static_cast<int>(x);
|
||||
auto current = true;
|
||||
for (size_t i = 0; i < normal_size; ++i) {
|
||||
auto v = static_cast<int>(normal[i]);
|
||||
auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v;
|
||||
xsigned -= len;
|
||||
if (xsigned < 0) break;
|
||||
current = !current;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
// This code is generated by support/printable.py.
|
||||
FMT_FUNC auto is_printable(uint32_t cp) -> bool {
|
||||
static constexpr singleton singletons0[] = {
|
||||
{0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8},
|
||||
{0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13},
|
||||
{0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5},
|
||||
{0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22},
|
||||
{0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3},
|
||||
{0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8},
|
||||
{0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9},
|
||||
};
|
||||
static constexpr unsigned char singletons0_lower[] = {
|
||||
0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90,
|
||||
0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f,
|
||||
0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
|
||||
0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04,
|
||||
0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d,
|
||||
0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
|
||||
0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
|
||||
0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d,
|
||||
0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
|
||||
0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d,
|
||||
0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5,
|
||||
0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
|
||||
0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49,
|
||||
0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7,
|
||||
0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
|
||||
0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e,
|
||||
0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16,
|
||||
0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
|
||||
0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f,
|
||||
0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf,
|
||||
0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
|
||||
0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27,
|
||||
0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91,
|
||||
0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
|
||||
0xfe, 0xff,
|
||||
};
|
||||
static constexpr singleton singletons1[] = {
|
||||
{0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2},
|
||||
{0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5},
|
||||
{0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5},
|
||||
{0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2},
|
||||
{0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5},
|
||||
{0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2},
|
||||
{0xfa, 2}, {0xfb, 1},
|
||||
};
|
||||
static constexpr unsigned char singletons1_lower[] = {
|
||||
0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07,
|
||||
0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36,
|
||||
0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
|
||||
0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
|
||||
0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b,
|
||||
0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
|
||||
0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66,
|
||||
0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27,
|
||||
0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
|
||||
0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7,
|
||||
0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6,
|
||||
0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
|
||||
0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66,
|
||||
0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0,
|
||||
0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
|
||||
};
|
||||
static constexpr unsigned char normal0[] = {
|
||||
0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04,
|
||||
0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0,
|
||||
0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
|
||||
0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03,
|
||||
0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03,
|
||||
0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
|
||||
0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15,
|
||||
0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f,
|
||||
0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
|
||||
0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07,
|
||||
0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06,
|
||||
0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
|
||||
0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac,
|
||||
0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c,
|
||||
0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
|
||||
0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c,
|
||||
0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b,
|
||||
0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
|
||||
0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03,
|
||||
0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80,
|
||||
0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
|
||||
0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c,
|
||||
0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17,
|
||||
0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
|
||||
0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80,
|
||||
0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d,
|
||||
};
|
||||
static constexpr unsigned char normal1[] = {
|
||||
0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f,
|
||||
0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e,
|
||||
0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
|
||||
0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09,
|
||||
0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16,
|
||||
0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
|
||||
0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36,
|
||||
0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33,
|
||||
0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
|
||||
0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e,
|
||||
0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41,
|
||||
0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
|
||||
0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22,
|
||||
0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04,
|
||||
0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
|
||||
0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03,
|
||||
0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81,
|
||||
0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
|
||||
0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1,
|
||||
0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a,
|
||||
0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
|
||||
0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09,
|
||||
0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89,
|
||||
0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
|
||||
0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09,
|
||||
0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50,
|
||||
0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
|
||||
0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83,
|
||||
0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05,
|
||||
0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
|
||||
0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80,
|
||||
0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07,
|
||||
0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
|
||||
0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07,
|
||||
0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06,
|
||||
};
|
||||
auto lower = static_cast<uint16_t>(cp);
|
||||
if (cp < 0x10000) {
|
||||
return is_printable(lower, singletons0,
|
||||
sizeof(singletons0) / sizeof(*singletons0),
|
||||
singletons0_lower, normal0, sizeof(normal0));
|
||||
}
|
||||
if (cp < 0x20000) {
|
||||
return is_printable(lower, singletons1,
|
||||
sizeof(singletons1) / sizeof(*singletons1),
|
||||
singletons1_lower, normal1, sizeof(normal1));
|
||||
}
|
||||
if (0x2a6de <= cp && cp < 0x2a700) return false;
|
||||
if (0x2b735 <= cp && cp < 0x2b740) return false;
|
||||
if (0x2b81e <= cp && cp < 0x2b820) return false;
|
||||
if (0x2cea2 <= cp && cp < 0x2ceb0) return false;
|
||||
if (0x2ebe1 <= cp && cp < 0x2f800) return false;
|
||||
if (0x2fa1e <= cp && cp < 0x30000) return false;
|
||||
if (0x3134b <= cp && cp < 0xe0100) return false;
|
||||
if (0xe01f0 <= cp && cp < 0x110000) return false;
|
||||
return cp < 0x110000;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
|
||||
3143
extern/fmtlib/include/fmt/format.h
vendored
3143
extern/fmtlib/include/fmt/format.h
vendored
@@ -1,45 +1,46 @@
|
||||
/*
|
||||
Formatting library for C++
|
||||
Formatting library for C++
|
||||
|
||||
Copyright (c) 2012 - present, Victor Zverovich
|
||||
Copyright (c) 2012 - present, Victor Zverovich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--- Optional exception to the license ---
|
||||
--- Optional exception to the license ---
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into a machine-executable object form of such
|
||||
source code, you may redistribute such embedded portions in such object form
|
||||
without including the above copyright and permission notices.
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into a machine-executable object form of such
|
||||
source code, you may redistribute such embedded portions in such object form
|
||||
without including the above copyright and permission notices.
|
||||
*/
|
||||
|
||||
#ifndef FMT_FORMAT_H_
|
||||
#define FMT_FORMAT_H_
|
||||
|
||||
#include <cmath> // std::signbit
|
||||
#include <cstdint> // uint32_t
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <memory> // std::uninitialized_copy
|
||||
#include <stdexcept> // std::runtime_error
|
||||
#include <system_error> // std::system_error
|
||||
#include <utility> // std::swap
|
||||
#include <cmath> // std::signbit
|
||||
#include <cstdint> // uint32_t
|
||||
#include <cstring> // std::memcpy
|
||||
#include <initializer_list> // std::initializer_list
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <memory> // std::uninitialized_copy
|
||||
#include <stdexcept> // std::runtime_error
|
||||
#include <system_error> // std::system_error
|
||||
|
||||
#ifdef __cpp_lib_bit_cast
|
||||
# include <bit> // std::bitcast
|
||||
@@ -47,6 +48,36 @@
|
||||
|
||||
#include "core.h"
|
||||
|
||||
#ifndef FMT_BEGIN_DETAIL_NAMESPACE
|
||||
# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
|
||||
# define FMT_END_DETAIL_NAMESPACE }
|
||||
#endif
|
||||
|
||||
#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
|
||||
# define FMT_FALLTHROUGH [[fallthrough]]
|
||||
#elif defined(__clang__)
|
||||
# define FMT_FALLTHROUGH [[clang::fallthrough]]
|
||||
#elif FMT_GCC_VERSION >= 700 && \
|
||||
(!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
|
||||
# define FMT_FALLTHROUGH [[gnu::fallthrough]]
|
||||
#else
|
||||
# define FMT_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
#ifndef FMT_DEPRECATED
|
||||
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900
|
||||
# define FMT_DEPRECATED [[deprecated]]
|
||||
# else
|
||||
# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
|
||||
# define FMT_DEPRECATED __attribute__((deprecated))
|
||||
# elif FMT_MSC_VERSION
|
||||
# define FMT_DEPRECATED __declspec(deprecated)
|
||||
# else
|
||||
# define FMT_DEPRECATED /* deprecated */
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if FMT_GCC_VERSION
|
||||
# define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden")))
|
||||
#else
|
||||
@@ -71,15 +102,9 @@
|
||||
# define FMT_NOINLINE
|
||||
#endif
|
||||
|
||||
#if FMT_MSC_VER
|
||||
# define FMT_MSC_DEFAULT = default
|
||||
#else
|
||||
# define FMT_MSC_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef FMT_THROW
|
||||
# if FMT_EXCEPTIONS
|
||||
# if FMT_MSC_VER || FMT_NVCC
|
||||
# if FMT_MSC_VERSION || defined(__NVCC__)
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
template <typename Exception> inline void do_throw(const Exception& x) {
|
||||
@@ -118,17 +143,10 @@ FMT_END_NAMESPACE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers.
|
||||
#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC
|
||||
# define FMT_DEPRECATED_ALIAS
|
||||
#else
|
||||
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
|
||||
#endif
|
||||
|
||||
#ifndef FMT_USE_USER_DEFINED_LITERALS
|
||||
// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs.
|
||||
# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \
|
||||
FMT_MSC_VER >= 1900) && \
|
||||
FMT_MSC_VERSION >= 1900) && \
|
||||
(!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480)
|
||||
# define FMT_USE_USER_DEFINED_LITERALS 1
|
||||
# else
|
||||
@@ -146,7 +164,7 @@ FMT_END_NAMESPACE
|
||||
|
||||
// __builtin_clz is broken in clang with Microsoft CodeGen:
|
||||
// https://github.com/fmtlib/fmt/issues/519.
|
||||
#if !FMT_MSC_VER
|
||||
#if !FMT_MSC_VERSION
|
||||
# if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION
|
||||
# define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
|
||||
# endif
|
||||
@@ -158,22 +176,25 @@ FMT_END_NAMESPACE
|
||||
// __builtin_ctz is broken in Intel Compiler Classic on Windows:
|
||||
// https://github.com/fmtlib/fmt/issues/2510.
|
||||
#ifndef __ICL
|
||||
# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION
|
||||
# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \
|
||||
defined(__NVCOMPILER)
|
||||
# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n)
|
||||
# endif
|
||||
# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || FMT_ICC_VERSION
|
||||
# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \
|
||||
FMT_ICC_VERSION || defined(__NVCOMPILER)
|
||||
# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if FMT_MSC_VER
|
||||
#if FMT_MSC_VERSION
|
||||
# include <intrin.h> // _BitScanReverse[64], _BitScanForward[64], _umul128
|
||||
#endif
|
||||
|
||||
// Some compilers masquerade as both MSVC and GCC-likes or otherwise support
|
||||
// __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the
|
||||
// MSVC intrinsics if the clz and clzll builtins are not available.
|
||||
#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(FMT_BUILTIN_CTZLL)
|
||||
#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) && \
|
||||
!defined(FMT_BUILTIN_CTZLL)
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning.
|
||||
@@ -204,7 +225,8 @@ inline auto clzll(uint64_t x) -> int {
|
||||
_BitScanReverse64(&r, x);
|
||||
# else
|
||||
// Scan the high 32 bits.
|
||||
if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32))) return 63 ^ (r + 32);
|
||||
if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32)))
|
||||
return 63 ^ static_cast<int>(r + 32);
|
||||
// Scan the low 32 bits.
|
||||
_BitScanReverse(&r, static_cast<uint32_t>(x));
|
||||
# endif
|
||||
@@ -243,15 +265,41 @@ inline auto ctzll(uint64_t x) -> int {
|
||||
FMT_END_NAMESPACE
|
||||
#endif
|
||||
|
||||
#ifdef FMT_HEADER_ONLY
|
||||
# define FMT_HEADER_ONLY_CONSTEXPR20 FMT_CONSTEXPR20
|
||||
#else
|
||||
# define FMT_HEADER_ONLY_CONSTEXPR20
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
template <typename...> struct disjunction : std::false_type {};
|
||||
template <typename P> struct disjunction<P> : P {};
|
||||
template <typename P1, typename... Pn>
|
||||
struct disjunction<P1, Pn...>
|
||||
: conditional_t<bool(P1::value), P1, disjunction<Pn...>> {};
|
||||
|
||||
template <typename...> struct conjunction : std::true_type {};
|
||||
template <typename P> struct conjunction<P> : P {};
|
||||
template <typename P1, typename... Pn>
|
||||
struct conjunction<P1, Pn...>
|
||||
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
|
||||
|
||||
namespace detail {
|
||||
|
||||
FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) {
|
||||
ignore_unused(condition);
|
||||
#ifdef FMT_FUZZ
|
||||
if (condition) throw std::runtime_error("fuzzing limit reached");
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename CharT, CharT... C> struct string_literal {
|
||||
static constexpr CharT value[sizeof...(C)] = {C...};
|
||||
constexpr operator basic_string_view<CharT>() const {
|
||||
return {value, sizeof...(C)};
|
||||
}
|
||||
};
|
||||
|
||||
#if FMT_CPLUSPLUS < 201703L
|
||||
template <typename CharT, CharT... C>
|
||||
constexpr CharT string_literal<CharT, C...>::value[sizeof...(C)];
|
||||
#endif
|
||||
|
||||
template <typename Streambuf> class formatbuf : public Streambuf {
|
||||
private:
|
||||
using char_type = typename Streambuf::char_type;
|
||||
@@ -284,14 +332,14 @@ template <typename Streambuf> class formatbuf : public Streambuf {
|
||||
};
|
||||
|
||||
// Implementation of std::bit_cast for pre-C++20.
|
||||
template <typename To, typename From>
|
||||
template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) == sizeof(From))>
|
||||
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<To>(from);
|
||||
#endif
|
||||
auto to = To();
|
||||
std::memcpy(&to, &from, sizeof(to));
|
||||
// The cast suppresses a bogus -Wclass-memaccess on GCC.
|
||||
std::memcpy(static_cast<void*>(&to), &from, sizeof(to));
|
||||
return to;
|
||||
}
|
||||
|
||||
@@ -310,29 +358,125 @@ inline auto is_big_endian() -> bool {
|
||||
#endif
|
||||
}
|
||||
|
||||
// A fallback implementation of uintptr_t for systems that lack it.
|
||||
struct fallback_uintptr {
|
||||
unsigned char value[sizeof(void*)];
|
||||
class uint128_fallback {
|
||||
private:
|
||||
uint64_t lo_, hi_;
|
||||
|
||||
fallback_uintptr() = default;
|
||||
explicit fallback_uintptr(const void* p) {
|
||||
*this = bit_cast<fallback_uintptr>(p);
|
||||
if (const_check(is_big_endian())) {
|
||||
for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j)
|
||||
std::swap(value[i], value[j]);
|
||||
friend uint128_fallback umul128(uint64_t x, uint64_t y) noexcept;
|
||||
|
||||
public:
|
||||
constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
|
||||
constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {}
|
||||
|
||||
constexpr uint64_t high() const noexcept { return hi_; }
|
||||
constexpr uint64_t low() const noexcept { return lo_; }
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
constexpr explicit operator T() const {
|
||||
return static_cast<T>(lo_);
|
||||
}
|
||||
|
||||
friend constexpr auto operator==(const uint128_fallback& lhs,
|
||||
const uint128_fallback& rhs) -> bool {
|
||||
return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_;
|
||||
}
|
||||
friend constexpr auto operator!=(const uint128_fallback& lhs,
|
||||
const uint128_fallback& rhs) -> bool {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
friend constexpr auto operator>(const uint128_fallback& lhs,
|
||||
const uint128_fallback& rhs) -> bool {
|
||||
return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_;
|
||||
}
|
||||
friend constexpr auto operator|(const uint128_fallback& lhs,
|
||||
const uint128_fallback& rhs)
|
||||
-> uint128_fallback {
|
||||
return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_};
|
||||
}
|
||||
friend constexpr auto operator&(const uint128_fallback& lhs,
|
||||
const uint128_fallback& rhs)
|
||||
-> uint128_fallback {
|
||||
return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_};
|
||||
}
|
||||
friend constexpr auto operator~(const uint128_fallback& n)
|
||||
-> uint128_fallback {
|
||||
return {~n.hi_, ~n.lo_};
|
||||
}
|
||||
friend auto operator+(const uint128_fallback& lhs,
|
||||
const uint128_fallback& rhs) -> uint128_fallback {
|
||||
auto result = uint128_fallback(lhs);
|
||||
result += rhs;
|
||||
return result;
|
||||
}
|
||||
friend auto operator*(const uint128_fallback& lhs, uint32_t rhs)
|
||||
-> uint128_fallback {
|
||||
FMT_ASSERT(lhs.hi_ == 0, "");
|
||||
uint64_t hi = (lhs.lo_ >> 32) * rhs;
|
||||
uint64_t lo = (lhs.lo_ & ~uint32_t()) * rhs;
|
||||
uint64_t new_lo = (hi << 32) + lo;
|
||||
return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo};
|
||||
}
|
||||
friend auto operator-(const uint128_fallback& lhs, uint64_t rhs)
|
||||
-> uint128_fallback {
|
||||
return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs};
|
||||
}
|
||||
FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback {
|
||||
if (shift == 64) return {0, hi_};
|
||||
if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64);
|
||||
return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)};
|
||||
}
|
||||
FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback {
|
||||
if (shift == 64) return {lo_, 0};
|
||||
if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64);
|
||||
return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)};
|
||||
}
|
||||
FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& {
|
||||
return *this = *this >> shift;
|
||||
}
|
||||
FMT_CONSTEXPR void operator+=(uint128_fallback n) {
|
||||
uint64_t new_lo = lo_ + n.lo_;
|
||||
uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0);
|
||||
FMT_ASSERT(new_hi >= hi_, "");
|
||||
lo_ = new_lo;
|
||||
hi_ = new_hi;
|
||||
}
|
||||
FMT_CONSTEXPR void operator&=(uint128_fallback n) {
|
||||
lo_ &= n.lo_;
|
||||
hi_ &= n.hi_;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept {
|
||||
if (is_constant_evaluated()) {
|
||||
lo_ += n;
|
||||
hi_ += (lo_ < n ? 1 : 0);
|
||||
return *this;
|
||||
}
|
||||
#if FMT_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__)
|
||||
unsigned long long carry;
|
||||
lo_ = __builtin_addcll(lo_, n, 0, &carry);
|
||||
hi_ += carry;
|
||||
#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__)
|
||||
unsigned long long result;
|
||||
auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result);
|
||||
lo_ = result;
|
||||
hi_ += carry;
|
||||
#elif defined(_MSC_VER) && defined(_M_X64)
|
||||
auto carry = _addcarry_u64(0, lo_, n, &lo_);
|
||||
_addcarry_u64(carry, hi_, 0, &hi_);
|
||||
#else
|
||||
lo_ += n;
|
||||
hi_ += (lo_ < n ? 1 : 0);
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
using uint128_t = conditional_t<FMT_USE_INT128, uint128_opt, uint128_fallback>;
|
||||
|
||||
#ifdef UINTPTR_MAX
|
||||
using uintptr_t = ::uintptr_t;
|
||||
inline auto to_uintptr(const void* p) -> uintptr_t {
|
||||
return bit_cast<uintptr_t>(p);
|
||||
}
|
||||
#else
|
||||
using uintptr_t = fallback_uintptr;
|
||||
inline auto to_uintptr(const void* p) -> fallback_uintptr {
|
||||
return fallback_uintptr(p);
|
||||
}
|
||||
using uintptr_t = uint128_t;
|
||||
#endif
|
||||
|
||||
// Returns the largest possible value for type T. Same as
|
||||
@@ -344,16 +488,53 @@ template <typename T> constexpr auto num_bits() -> int {
|
||||
return std::numeric_limits<T>::digits;
|
||||
}
|
||||
// std::numeric_limits<T>::digits may return 0 for 128-bit ints.
|
||||
template <> constexpr auto num_bits<int128_t>() -> int { return 128; }
|
||||
template <> constexpr auto num_bits<int128_opt>() -> int { return 128; }
|
||||
template <> constexpr auto num_bits<uint128_t>() -> int { return 128; }
|
||||
template <> constexpr auto num_bits<fallback_uintptr>() -> int {
|
||||
return static_cast<int>(sizeof(void*) *
|
||||
std::numeric_limits<unsigned char>::digits);
|
||||
|
||||
// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t
|
||||
// and 128-bit pointers to uint128_fallback.
|
||||
template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) > sizeof(From))>
|
||||
inline auto bit_cast(const From& from) -> To {
|
||||
constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned));
|
||||
struct data_t {
|
||||
unsigned value[static_cast<unsigned>(size)];
|
||||
} data = bit_cast<data_t>(from);
|
||||
auto result = To();
|
||||
if (const_check(is_big_endian())) {
|
||||
for (int i = 0; i < size; ++i)
|
||||
result = (result << num_bits<unsigned>()) | data.value[i];
|
||||
} else {
|
||||
for (int i = size - 1; i >= 0; --i)
|
||||
result = (result << num_bits<unsigned>()) | data.value[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename UInt>
|
||||
FMT_CONSTEXPR20 inline auto countl_zero_fallback(UInt n) -> int {
|
||||
int lz = 0;
|
||||
constexpr UInt msb_mask = static_cast<UInt>(1) << (num_bits<UInt>() - 1);
|
||||
for (; (n & msb_mask) == 0; n <<= 1) lz++;
|
||||
return lz;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 inline auto countl_zero(uint32_t n) -> int {
|
||||
#ifdef FMT_BUILTIN_CLZ
|
||||
if (!is_constant_evaluated()) return FMT_BUILTIN_CLZ(n);
|
||||
#endif
|
||||
return countl_zero_fallback(n);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 inline auto countl_zero(uint64_t n) -> int {
|
||||
#ifdef FMT_BUILTIN_CLZLL
|
||||
if (!is_constant_evaluated()) return FMT_BUILTIN_CLZLL(n);
|
||||
#endif
|
||||
return countl_zero_fallback(n);
|
||||
}
|
||||
|
||||
FMT_INLINE void assume(bool condition) {
|
||||
(void)condition;
|
||||
#if FMT_HAS_BUILTIN(__builtin_assume)
|
||||
#if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION
|
||||
__builtin_assume(condition);
|
||||
#endif
|
||||
}
|
||||
@@ -495,19 +676,24 @@ FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e)
|
||||
constexpr const int shiftc[] = {0, 18, 12, 6, 0};
|
||||
constexpr const int shifte[] = {0, 6, 4, 2, 0};
|
||||
|
||||
int len = code_point_length(s);
|
||||
const char* next = s + len;
|
||||
int len = "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"
|
||||
[static_cast<unsigned char>(*s) >> 3];
|
||||
// Compute the pointer to the next character early so that the next
|
||||
// iteration can start working on the next character. Neither Clang
|
||||
// nor GCC figure out this reordering on their own.
|
||||
const char* next = s + len + !len;
|
||||
|
||||
using uchar = unsigned char;
|
||||
|
||||
// Assume a four-byte character and load four bytes. Unused bits are
|
||||
// shifted out.
|
||||
*c = uint32_t(s[0] & masks[len]) << 18;
|
||||
*c |= uint32_t(s[1] & 0x3f) << 12;
|
||||
*c |= uint32_t(s[2] & 0x3f) << 6;
|
||||
*c |= uint32_t(s[3] & 0x3f) << 0;
|
||||
*c = uint32_t(uchar(s[0]) & masks[len]) << 18;
|
||||
*c |= uint32_t(uchar(s[1]) & 0x3f) << 12;
|
||||
*c |= uint32_t(uchar(s[2]) & 0x3f) << 6;
|
||||
*c |= uint32_t(uchar(s[3]) & 0x3f) << 0;
|
||||
*c >>= shiftc[len];
|
||||
|
||||
// Accumulate the various error conditions.
|
||||
using uchar = unsigned char;
|
||||
*e = (*c < mins[len]) << 6; // non-canonical encoding
|
||||
*e |= ((*c >> 11) == 0x1b) << 7; // surrogate half?
|
||||
*e |= (*c > 0x10FFFF) << 8; // out of range?
|
||||
@@ -520,7 +706,7 @@ FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e)
|
||||
return next;
|
||||
}
|
||||
|
||||
constexpr uint32_t invalid_code_point = ~uint32_t();
|
||||
constexpr FMT_INLINE_VARIABLE uint32_t invalid_code_point = ~uint32_t();
|
||||
|
||||
// Invokes f(cp, sv) for every code point cp in s with sv being the string view
|
||||
// corresponding to the code point. cp is invalid_code_point on error.
|
||||
@@ -531,8 +717,8 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) {
|
||||
auto error = 0;
|
||||
auto end = utf8_decode(buf_ptr, &cp, &error);
|
||||
bool result = f(error ? invalid_code_point : cp,
|
||||
string_view(ptr, to_unsigned(end - buf_ptr)));
|
||||
return result ? end : nullptr;
|
||||
string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr)));
|
||||
return result ? (error ? buf_ptr + 1 : end) : nullptr;
|
||||
};
|
||||
auto p = s.data();
|
||||
const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars.
|
||||
@@ -590,13 +776,14 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
// We could avoid branches by using utf8_decode directly.
|
||||
for_each_codepoint(s, count_code_points{&num_code_points});
|
||||
return num_code_points;
|
||||
}
|
||||
|
||||
inline auto compute_width(basic_string_view<char8_type> s) -> size_t {
|
||||
return compute_width(basic_string_view<char>(
|
||||
reinterpret_cast<const char*>(s.data()), s.size()));
|
||||
return compute_width(
|
||||
string_view(reinterpret_cast<const char*>(s.data()), s.size()));
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
@@ -606,9 +793,8 @@ inline auto code_point_index(basic_string_view<Char> s, size_t n) -> size_t {
|
||||
}
|
||||
|
||||
// Calculates the index of the nth code point in a UTF-8 string.
|
||||
inline auto code_point_index(basic_string_view<char8_type> s, size_t n)
|
||||
-> size_t {
|
||||
const char8_type* data = s.data();
|
||||
inline auto code_point_index(string_view s, size_t n) -> size_t {
|
||||
const char* data = s.data();
|
||||
size_t num_code_points = 0;
|
||||
for (size_t i = 0, size = s.size(); i != size; ++i) {
|
||||
if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i;
|
||||
@@ -616,11 +802,73 @@ inline auto code_point_index(basic_string_view<char8_type> s, size_t n)
|
||||
return s.size();
|
||||
}
|
||||
|
||||
inline auto code_point_index(basic_string_view<char8_type> s, size_t n)
|
||||
-> size_t {
|
||||
return code_point_index(
|
||||
string_view(reinterpret_cast<const char*>(s.data()), s.size()), n);
|
||||
}
|
||||
|
||||
template <typename T> struct is_integral : std::is_integral<T> {};
|
||||
template <> struct is_integral<int128_opt> : std::true_type {};
|
||||
template <> struct is_integral<uint128_t> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
using is_signed =
|
||||
std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
|
||||
std::is_same<T, int128_opt>::value>;
|
||||
|
||||
template <typename T>
|
||||
using is_integer =
|
||||
bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
|
||||
!std::is_same<T, char>::value &&
|
||||
!std::is_same<T, wchar_t>::value>;
|
||||
|
||||
#ifndef FMT_USE_FLOAT
|
||||
# define FMT_USE_FLOAT 1
|
||||
#endif
|
||||
#ifndef FMT_USE_DOUBLE
|
||||
# define FMT_USE_DOUBLE 1
|
||||
#endif
|
||||
#ifndef FMT_USE_LONG_DOUBLE
|
||||
# define FMT_USE_LONG_DOUBLE 1
|
||||
#endif
|
||||
|
||||
#ifndef FMT_USE_FLOAT128
|
||||
# ifdef __clang__
|
||||
// Clang emulates GCC, so it has to appear early.
|
||||
# if FMT_HAS_INCLUDE(<quadmath.h>)
|
||||
# define FMT_USE_FLOAT128 1
|
||||
# endif
|
||||
# elif defined(__GNUC__)
|
||||
// GNU C++:
|
||||
# if defined(_GLIBCXX_USE_FLOAT128) && !defined(__STRICT_ANSI__)
|
||||
# define FMT_USE_FLOAT128 1
|
||||
# endif
|
||||
# endif
|
||||
# ifndef FMT_USE_FLOAT128
|
||||
# define FMT_USE_FLOAT128 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if FMT_USE_FLOAT128
|
||||
using float128 = __float128;
|
||||
#else
|
||||
using float128 = void;
|
||||
#endif
|
||||
template <typename T> using is_float128 = std::is_same<T, float128>;
|
||||
|
||||
template <typename T>
|
||||
using is_floating_point =
|
||||
bool_constant<std::is_floating_point<T>::value || is_float128<T>::value>;
|
||||
|
||||
template <typename T, bool = std::is_floating_point<T>::value>
|
||||
struct is_fast_float : bool_constant<std::numeric_limits<T>::is_iec559 &&
|
||||
sizeof(T) <= sizeof(double)> {};
|
||||
template <typename T> struct is_fast_float<T, false> : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
using is_double_double = bool_constant<std::numeric_limits<T>::digits == 106>;
|
||||
|
||||
#ifndef FMT_USE_FULL_CACHE_DRAGONBOX
|
||||
# define FMT_USE_FULL_CACHE_DRAGONBOX 0
|
||||
#endif
|
||||
@@ -645,7 +893,7 @@ template <typename T>
|
||||
struct is_locale<T, void_t<decltype(T::classic())>> : std::true_type {};
|
||||
} // namespace detail
|
||||
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
FMT_BEGIN_EXPORT
|
||||
|
||||
// The number of characters to store in the basic_memory_buffer object itself
|
||||
// to avoid dynamic memory allocation.
|
||||
@@ -688,7 +936,27 @@ class basic_memory_buffer final : public detail::buffer<T> {
|
||||
}
|
||||
|
||||
protected:
|
||||
FMT_CONSTEXPR20 void grow(size_t size) override;
|
||||
FMT_CONSTEXPR20 void grow(size_t size) override {
|
||||
detail::abort_fuzzing_if(size > 5000);
|
||||
const size_t max_size = std::allocator_traits<Allocator>::max_size(alloc_);
|
||||
size_t old_capacity = this->capacity();
|
||||
size_t new_capacity = old_capacity + old_capacity / 2;
|
||||
if (size > new_capacity)
|
||||
new_capacity = size;
|
||||
else if (new_capacity > max_size)
|
||||
new_capacity = size > max_size ? size : max_size;
|
||||
T* old_data = this->data();
|
||||
T* new_data =
|
||||
std::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
|
||||
// The following code doesn't throw, so the raw pointer above doesn't leak.
|
||||
std::uninitialized_copy(old_data, old_data + this->size(),
|
||||
detail::make_checked(new_data, new_capacity));
|
||||
this->set(new_data, new_capacity);
|
||||
// deallocate must not throw according to the standard, but even if it does,
|
||||
// the buffer already uses the new storage and will deallocate it in
|
||||
// destructor.
|
||||
if (old_data != store_) alloc_.deallocate(old_data, old_capacity);
|
||||
}
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
@@ -698,9 +966,7 @@ class basic_memory_buffer final : public detail::buffer<T> {
|
||||
const Allocator& alloc = Allocator())
|
||||
: alloc_(alloc) {
|
||||
this->set(store_, SIZE);
|
||||
if (detail::is_constant_evaluated()) {
|
||||
detail::fill_n(store_, SIZE, T{});
|
||||
}
|
||||
if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T());
|
||||
}
|
||||
FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); }
|
||||
|
||||
@@ -712,18 +978,14 @@ class basic_memory_buffer final : public detail::buffer<T> {
|
||||
size_t size = other.size(), capacity = other.capacity();
|
||||
if (data == other.store_) {
|
||||
this->set(store_, capacity);
|
||||
if (detail::is_constant_evaluated()) {
|
||||
detail::copy_str<T>(other.store_, other.store_ + size,
|
||||
detail::make_checked(store_, capacity));
|
||||
} else {
|
||||
std::uninitialized_copy(other.store_, other.store_ + size,
|
||||
detail::make_checked(store_, capacity));
|
||||
}
|
||||
detail::copy_str<T>(other.store_, other.store_ + size,
|
||||
detail::make_checked(store_, capacity));
|
||||
} else {
|
||||
this->set(data, capacity);
|
||||
// Set pointer to the inline array so that delete is not called
|
||||
// when deallocating.
|
||||
other.set(other.store_, 0);
|
||||
other.clear();
|
||||
}
|
||||
this->resize(size);
|
||||
}
|
||||
@@ -735,8 +997,7 @@ class basic_memory_buffer final : public detail::buffer<T> {
|
||||
of the other object to it.
|
||||
\endrst
|
||||
*/
|
||||
FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other)
|
||||
FMT_NOEXCEPT {
|
||||
FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept {
|
||||
move(other);
|
||||
}
|
||||
|
||||
@@ -745,8 +1006,7 @@ class basic_memory_buffer final : public detail::buffer<T> {
|
||||
Moves the content of the other ``basic_memory_buffer`` object to this one.
|
||||
\endrst
|
||||
*/
|
||||
auto operator=(basic_memory_buffer&& other) FMT_NOEXCEPT
|
||||
-> basic_memory_buffer& {
|
||||
auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& {
|
||||
FMT_ASSERT(this != &other, "");
|
||||
deallocate();
|
||||
move(other);
|
||||
@@ -773,86 +1033,38 @@ class basic_memory_buffer final : public detail::buffer<T> {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, size_t SIZE, typename Allocator>
|
||||
FMT_CONSTEXPR20 void basic_memory_buffer<T, SIZE, Allocator>::grow(
|
||||
size_t size) {
|
||||
#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<Allocator>::max_size(alloc_);
|
||||
size_t old_capacity = this->capacity();
|
||||
size_t new_capacity = old_capacity + old_capacity / 2;
|
||||
if (size > new_capacity)
|
||||
new_capacity = size;
|
||||
else if (new_capacity > max_size)
|
||||
new_capacity = size > max_size ? size : max_size;
|
||||
T* old_data = this->data();
|
||||
T* new_data =
|
||||
std::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
|
||||
// The following code doesn't throw, so the raw pointer above doesn't leak.
|
||||
std::uninitialized_copy(old_data, old_data + this->size(),
|
||||
detail::make_checked(new_data, new_capacity));
|
||||
this->set(new_data, new_capacity);
|
||||
// deallocate must not throw according to the standard, but even if it does,
|
||||
// the buffer already uses the new storage and will deallocate it in
|
||||
// destructor.
|
||||
if (old_data != store_) alloc_.deallocate(old_data, old_capacity);
|
||||
}
|
||||
|
||||
using memory_buffer = basic_memory_buffer<char>;
|
||||
|
||||
template <typename T, size_t SIZE, typename Allocator>
|
||||
struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
|
||||
};
|
||||
|
||||
FMT_END_EXPORT
|
||||
namespace detail {
|
||||
FMT_API bool write_console(std::FILE* f, string_view text);
|
||||
FMT_API void print(std::FILE*, string_view);
|
||||
}
|
||||
} // namespace detail
|
||||
FMT_BEGIN_EXPORT
|
||||
|
||||
/** A formatting error such as invalid format string. */
|
||||
FMT_CLASS_API
|
||||
// Suppress a misleading warning in older versions of clang.
|
||||
#if FMT_CLANG_VERSION
|
||||
# pragma clang diagnostic ignored "-Wweak-vtables"
|
||||
#endif
|
||||
|
||||
/** An error reported from a formatting function. */
|
||||
class FMT_API format_error : public std::runtime_error {
|
||||
public:
|
||||
explicit format_error(const char* message) : std::runtime_error(message) {}
|
||||
explicit format_error(const std::string& message)
|
||||
: std::runtime_error(message) {}
|
||||
format_error(const format_error&) = default;
|
||||
format_error& operator=(const format_error&) = default;
|
||||
format_error(format_error&&) = default;
|
||||
format_error& operator=(format_error&&) = default;
|
||||
~format_error() FMT_NOEXCEPT override FMT_MSC_DEFAULT;
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a `~fmt::format_arg_store` object that contains references
|
||||
to arguments and can be implicitly converted to `~fmt::format_args`.
|
||||
If ``fmt`` is a compile-time string then `make_args_checked` checks
|
||||
its validity at compile time.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args, typename S, typename Char = char_t<S>>
|
||||
FMT_INLINE auto make_args_checked(const S& fmt,
|
||||
const remove_reference_t<Args>&... args)
|
||||
-> format_arg_store<buffer_context<Char>, remove_reference_t<Args>...> {
|
||||
static_assert(
|
||||
detail::count<(
|
||||
std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
|
||||
std::is_reference<Args>::value)...>() == 0,
|
||||
"passing views as lvalues is disallowed");
|
||||
detail::check_format_string<Args...>(fmt);
|
||||
return {args...};
|
||||
}
|
||||
|
||||
// compile-time support
|
||||
namespace detail_exported {
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
template <typename Char, size_t N> struct fixed_string {
|
||||
constexpr fixed_string(const Char (&str)[N]) {
|
||||
detail::copy_str<Char, const Char*, Char*>(static_cast<const Char*>(str),
|
||||
str + N, data);
|
||||
}
|
||||
Char data[N]{};
|
||||
Char data[N] = {};
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -871,33 +1083,70 @@ constexpr auto compile_string_to_view(detail::std_string_view<Char> s)
|
||||
}
|
||||
} // namespace detail_exported
|
||||
|
||||
class loc_value {
|
||||
private:
|
||||
basic_format_arg<format_context> value_;
|
||||
|
||||
public:
|
||||
template <typename T, FMT_ENABLE_IF(!detail::is_float128<T>::value)>
|
||||
loc_value(T value) : value_(detail::make_arg<format_context>(value)) {}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(detail::is_float128<T>::value)>
|
||||
loc_value(T) {}
|
||||
|
||||
template <typename Visitor> auto visit(Visitor&& vis) -> decltype(vis(0)) {
|
||||
return visit_format_arg(vis, value_);
|
||||
}
|
||||
};
|
||||
|
||||
// A locale facet that formats values in UTF-8.
|
||||
// It is parameterized on the locale to avoid the heavy <locale> include.
|
||||
template <typename Locale> class format_facet : public Locale::facet {
|
||||
private:
|
||||
std::string separator_;
|
||||
std::string grouping_;
|
||||
std::string decimal_point_;
|
||||
|
||||
protected:
|
||||
virtual auto do_put(appender out, loc_value val,
|
||||
const format_specs<>& specs) const -> bool;
|
||||
|
||||
public:
|
||||
static FMT_API typename Locale::id id;
|
||||
|
||||
explicit format_facet(Locale& loc);
|
||||
explicit format_facet(string_view sep = "",
|
||||
std::initializer_list<unsigned char> g = {3},
|
||||
std::string decimal_point = ".")
|
||||
: separator_(sep.data(), sep.size()),
|
||||
grouping_(g.begin(), g.end()),
|
||||
decimal_point_(decimal_point) {}
|
||||
|
||||
auto put(appender out, loc_value val, const format_specs<>& specs) const
|
||||
-> bool {
|
||||
return do_put(out, val, specs);
|
||||
}
|
||||
};
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
template <typename T> struct is_integral : std::is_integral<T> {};
|
||||
template <> struct is_integral<int128_t> : std::true_type {};
|
||||
template <> struct is_integral<uint128_t> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
using is_signed =
|
||||
std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
|
||||
std::is_same<T, int128_t>::value>;
|
||||
|
||||
// Returns true if value is negative, false otherwise.
|
||||
// Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
|
||||
template <typename T, FMT_ENABLE_IF(is_signed<T>::value)>
|
||||
FMT_CONSTEXPR auto is_negative(T value) -> bool {
|
||||
constexpr auto is_negative(T value) -> bool {
|
||||
return value < 0;
|
||||
}
|
||||
template <typename T, FMT_ENABLE_IF(!is_signed<T>::value)>
|
||||
FMT_CONSTEXPR auto is_negative(T) -> bool {
|
||||
constexpr auto is_negative(T) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
FMT_CONSTEXPR auto is_supported_floating_point(T) -> uint16_t {
|
||||
return (std::is_same<T, float>::value && FMT_USE_FLOAT) ||
|
||||
(std::is_same<T, double>::value && FMT_USE_DOUBLE) ||
|
||||
(std::is_same<T, long double>::value && FMT_USE_LONG_DOUBLE);
|
||||
template <typename T>
|
||||
FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool {
|
||||
if (std::is_same<T, float>()) return FMT_USE_FLOAT;
|
||||
if (std::is_same<T, double>()) return FMT_USE_DOUBLE;
|
||||
if (std::is_same<T, long double>()) return FMT_USE_LONG_DOUBLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
|
||||
@@ -948,7 +1197,7 @@ template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
|
||||
}
|
||||
}
|
||||
#if FMT_USE_INT128
|
||||
FMT_CONSTEXPR inline auto count_digits(uint128_t n) -> int {
|
||||
FMT_CONSTEXPR inline auto count_digits(uint128_opt n) -> int {
|
||||
return count_digits_fallback(n);
|
||||
}
|
||||
#endif
|
||||
@@ -989,7 +1238,7 @@ FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int {
|
||||
template <int BITS, typename UInt>
|
||||
FMT_CONSTEXPR auto count_digits(UInt n) -> int {
|
||||
#ifdef FMT_BUILTIN_CLZ
|
||||
if (num_bits<UInt>() == 32)
|
||||
if (!is_constant_evaluated() && num_bits<UInt>() == 32)
|
||||
return (FMT_BUILTIN_CLZ(static_cast<uint32_t>(n) | 1) ^ 31) / BITS + 1;
|
||||
#endif
|
||||
// Lambda avoids unreachable code warnings from NVHPC.
|
||||
@@ -1002,8 +1251,6 @@ FMT_CONSTEXPR auto count_digits(UInt n) -> int {
|
||||
}(n);
|
||||
}
|
||||
|
||||
template <> auto count_digits<4>(detail::fallback_uintptr n) -> int;
|
||||
|
||||
#ifdef FMT_BUILTIN_CLZ
|
||||
// It is a separate function rather than a part of count_digits to workaround
|
||||
// the lack of static constexpr in constexpr functions.
|
||||
@@ -1039,15 +1286,11 @@ FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int {
|
||||
return count_digits_fallback(n);
|
||||
}
|
||||
|
||||
template <typename Int> constexpr auto digits10() FMT_NOEXCEPT -> int {
|
||||
template <typename Int> constexpr auto digits10() noexcept -> int {
|
||||
return std::numeric_limits<Int>::digits10;
|
||||
}
|
||||
template <> constexpr auto digits10<int128_t>() FMT_NOEXCEPT -> int {
|
||||
return 38;
|
||||
}
|
||||
template <> constexpr auto digits10<uint128_t>() FMT_NOEXCEPT -> int {
|
||||
return 38;
|
||||
}
|
||||
template <> constexpr auto digits10<int128_opt>() noexcept -> int { return 38; }
|
||||
template <> constexpr auto digits10<uint128_t>() noexcept -> int { return 38; }
|
||||
|
||||
template <typename Char> struct thousands_sep_result {
|
||||
std::string grouping;
|
||||
@@ -1127,10 +1370,10 @@ FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size)
|
||||
|
||||
template <typename Char, typename UInt, typename Iterator,
|
||||
FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)>
|
||||
inline auto format_decimal(Iterator out, UInt value, int size)
|
||||
FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size)
|
||||
-> format_decimal_result<Iterator> {
|
||||
// Buffer is large enough to hold all digits (digits10 + 1).
|
||||
Char buffer[digits10<UInt>() + 1];
|
||||
Char buffer[digits10<UInt>() + 1] = {};
|
||||
auto end = format_decimal(buffer, value, size).end;
|
||||
return {out, detail::copy_str_noinline<Char>(buffer, end, out)};
|
||||
}
|
||||
@@ -1142,35 +1385,13 @@ FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits,
|
||||
Char* end = buffer;
|
||||
do {
|
||||
const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||
unsigned digit = (value & ((1 << BASE_BITS) - 1));
|
||||
unsigned digit = static_cast<unsigned>(value & ((1 << BASE_BITS) - 1));
|
||||
*--buffer = static_cast<Char>(BASE_BITS < 4 ? static_cast<char>('0' + digit)
|
||||
: digits[digit]);
|
||||
} while ((value >>= BASE_BITS) != 0);
|
||||
return end;
|
||||
}
|
||||
|
||||
template <unsigned BASE_BITS, typename Char>
|
||||
auto format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits,
|
||||
bool = false) -> Char* {
|
||||
auto char_digits = std::numeric_limits<unsigned char>::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<BASE_BITS>(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<Char>("0123456789abcdef"[digit]);
|
||||
value >>= BASE_BITS;
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
template <unsigned BASE_BITS, typename Char, typename It, typename UInt>
|
||||
inline auto format_uint(It out, UInt value, int num_digits, bool upper = false)
|
||||
-> It {
|
||||
@@ -1197,61 +1418,174 @@ class utf8_to_utf16 {
|
||||
auto str() const -> std::wstring { return {&buffer_[0], size()}; }
|
||||
};
|
||||
|
||||
// A converter from UTF-16/UTF-32 (host endian) to UTF-8.
|
||||
template <typename WChar, typename Buffer = memory_buffer>
|
||||
class unicode_to_utf8 {
|
||||
private:
|
||||
Buffer buffer_;
|
||||
|
||||
public:
|
||||
unicode_to_utf8() {}
|
||||
explicit unicode_to_utf8(basic_string_view<WChar> s) {
|
||||
static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4,
|
||||
"Expect utf16 or utf32");
|
||||
|
||||
if (!convert(s))
|
||||
FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16"
|
||||
: "invalid utf32"));
|
||||
}
|
||||
operator string_view() const { return string_view(&buffer_[0], size()); }
|
||||
size_t size() const { return buffer_.size() - 1; }
|
||||
const char* c_str() const { return &buffer_[0]; }
|
||||
std::string str() const { return std::string(&buffer_[0], size()); }
|
||||
|
||||
// Performs conversion returning a bool instead of throwing exception on
|
||||
// conversion error. This method may still throw in case of memory allocation
|
||||
// error.
|
||||
bool convert(basic_string_view<WChar> s) {
|
||||
if (!convert(buffer_, s)) return false;
|
||||
buffer_.push_back(0);
|
||||
return true;
|
||||
}
|
||||
static bool convert(Buffer& buf, basic_string_view<WChar> s) {
|
||||
for (auto p = s.begin(); p != s.end(); ++p) {
|
||||
uint32_t c = static_cast<uint32_t>(*p);
|
||||
if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) {
|
||||
// surrogate pair
|
||||
++p;
|
||||
if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) {
|
||||
return false;
|
||||
}
|
||||
c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
|
||||
}
|
||||
if (c < 0x80) {
|
||||
buf.push_back(static_cast<char>(c));
|
||||
} else if (c < 0x800) {
|
||||
buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
|
||||
buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
|
||||
} else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
|
||||
buf.push_back(static_cast<char>(0xe0 | (c >> 12)));
|
||||
buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
|
||||
buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
|
||||
} else if (c >= 0x10000 && c <= 0x10ffff) {
|
||||
buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
|
||||
buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
|
||||
buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
|
||||
buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
|
||||
inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept {
|
||||
#if FMT_USE_INT128
|
||||
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
|
||||
return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
|
||||
#elif defined(_MSC_VER) && defined(_M_X64)
|
||||
auto result = uint128_fallback();
|
||||
result.lo_ = _umul128(x, y, &result.hi_);
|
||||
return result;
|
||||
#else
|
||||
const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>());
|
||||
|
||||
uint64_t a = x >> 32;
|
||||
uint64_t b = x & mask;
|
||||
uint64_t c = y >> 32;
|
||||
uint64_t d = y & mask;
|
||||
|
||||
uint64_t ac = a * c;
|
||||
uint64_t bc = b * c;
|
||||
uint64_t ad = a * d;
|
||||
uint64_t bd = b * d;
|
||||
|
||||
uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask);
|
||||
|
||||
return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32),
|
||||
(intermediate << 32) + (bd & mask)};
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace dragonbox {
|
||||
// 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;
|
||||
}
|
||||
|
||||
inline int floor_log2_pow10(int e) noexcept {
|
||||
FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
|
||||
return (e * 1741647) >> 19;
|
||||
}
|
||||
|
||||
// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
|
||||
inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept {
|
||||
#if FMT_USE_INT128
|
||||
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
|
||||
return static_cast<uint64_t>(p >> 64);
|
||||
#elif defined(_MSC_VER) && defined(_M_X64)
|
||||
return __umulh(x, y);
|
||||
#else
|
||||
return umul128(x, y).high();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
|
||||
// 128-bit unsigned integer.
|
||||
inline uint128_fallback umul192_upper128(uint64_t x,
|
||||
uint128_fallback y) noexcept {
|
||||
uint128_fallback r = umul128(x, y.high());
|
||||
r += umul128_upper64(x, y.low());
|
||||
return r;
|
||||
}
|
||||
|
||||
FMT_API uint128_fallback get_cached_power(int k) noexcept;
|
||||
|
||||
// Type-specific information that Dragonbox uses.
|
||||
template <class T> struct float_info;
|
||||
template <typename T, typename Enable = void> struct float_info;
|
||||
|
||||
template <> struct float_info<float> {
|
||||
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<double> {
|
||||
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 max_k = 341;
|
||||
static const int shorter_interval_tie_lower_threshold = -77;
|
||||
static const int shorter_interval_tie_upper_threshold = -77;
|
||||
static const int max_trailing_zeros = 16;
|
||||
};
|
||||
|
||||
// An 80- or 128-bit floating point number.
|
||||
template <typename T>
|
||||
struct float_info<T, enable_if_t<std::numeric_limits<T>::digits == 64 ||
|
||||
std::numeric_limits<T>::digits == 113 ||
|
||||
is_float128<T>::value>> {
|
||||
using carrier_uint = detail::uint128_t;
|
||||
static const int exponent_bits = 15;
|
||||
};
|
||||
|
||||
// A double-double floating point number.
|
||||
template <typename T>
|
||||
struct float_info<T, enable_if_t<is_double_double<T>::value>> {
|
||||
using carrier_uint = detail::uint128_t;
|
||||
};
|
||||
|
||||
template <typename T> struct decimal_fp {
|
||||
@@ -1260,16 +1594,35 @@ template <typename T> struct decimal_fp {
|
||||
int exponent;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
FMT_API auto to_decimal(T x) FMT_NOEXCEPT -> decimal_fp<T>;
|
||||
template <typename T> FMT_API auto to_decimal(T x) noexcept -> decimal_fp<T>;
|
||||
} // namespace dragonbox
|
||||
|
||||
template <typename T>
|
||||
// Returns true iff Float has the implicit bit which is not stored.
|
||||
template <typename Float> constexpr bool has_implicit_bit() {
|
||||
// An 80-bit FP number has a 64-bit significand an no implicit bit.
|
||||
return std::numeric_limits<Float>::digits != 64;
|
||||
}
|
||||
|
||||
// Returns the number of significand bits stored in Float. The implicit bit is
|
||||
// not counted since it is not stored.
|
||||
template <typename Float> constexpr int num_significand_bits() {
|
||||
// std::numeric_limits may not support __float128.
|
||||
return is_float128<Float>() ? 112
|
||||
: (std::numeric_limits<Float>::digits -
|
||||
(has_implicit_bit<Float>() ? 1 : 0));
|
||||
}
|
||||
|
||||
template <typename Float>
|
||||
constexpr auto exponent_mask() ->
|
||||
typename dragonbox::float_info<T>::carrier_uint {
|
||||
using uint = typename dragonbox::float_info<T>::carrier_uint;
|
||||
return ((uint(1) << dragonbox::float_info<T>::exponent_bits) - 1)
|
||||
<< dragonbox::float_info<T>::significand_bits;
|
||||
typename dragonbox::float_info<Float>::carrier_uint {
|
||||
using float_uint = typename dragonbox::float_info<Float>::carrier_uint;
|
||||
return ((float_uint(1) << dragonbox::float_info<Float>::exponent_bits) - 1)
|
||||
<< num_significand_bits<Float>();
|
||||
}
|
||||
template <typename Float> constexpr auto exponent_bias() -> int {
|
||||
// std::numeric_limits may not support __float128.
|
||||
return is_float128<Float>() ? 16383
|
||||
: std::numeric_limits<Float>::max_exponent - 1;
|
||||
}
|
||||
|
||||
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
|
||||
@@ -1294,21 +1647,213 @@ FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It {
|
||||
return it;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
FMT_HEADER_ONLY_CONSTEXPR20 auto format_float(T value, int precision,
|
||||
float_specs specs,
|
||||
buffer<char>& buf) -> int;
|
||||
// A floating-point number f * pow(2, e) where F is an unsigned type.
|
||||
template <typename F> struct basic_fp {
|
||||
F f;
|
||||
int e;
|
||||
|
||||
// Formats a floating-point number with snprintf.
|
||||
template <typename T>
|
||||
auto snprintf_float(T value, int precision, float_specs specs,
|
||||
buffer<char>& buf) -> int;
|
||||
static constexpr const int num_significand_bits =
|
||||
static_cast<int>(sizeof(F) * num_bits<unsigned char>());
|
||||
|
||||
template <typename T> constexpr auto promote_float(T value) -> T {
|
||||
constexpr basic_fp() : f(0), e(0) {}
|
||||
constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
|
||||
|
||||
// Constructs fp from an IEEE754 floating-point number.
|
||||
template <typename Float> FMT_CONSTEXPR basic_fp(Float n) { assign(n); }
|
||||
|
||||
// Assigns n to this and return true iff predecessor is closer than successor.
|
||||
template <typename Float, FMT_ENABLE_IF(!is_double_double<Float>::value)>
|
||||
FMT_CONSTEXPR auto assign(Float n) -> bool {
|
||||
static_assert(std::numeric_limits<Float>::digits <= 113, "unsupported FP");
|
||||
// Assume Float is in the format [sign][exponent][significand].
|
||||
using carrier_uint = typename dragonbox::float_info<Float>::carrier_uint;
|
||||
const auto num_float_significand_bits =
|
||||
detail::num_significand_bits<Float>();
|
||||
const auto implicit_bit = carrier_uint(1) << num_float_significand_bits;
|
||||
const auto significand_mask = implicit_bit - 1;
|
||||
auto u = bit_cast<carrier_uint>(n);
|
||||
f = static_cast<F>(u & significand_mask);
|
||||
auto biased_e = static_cast<int>((u & exponent_mask<Float>()) >>
|
||||
num_float_significand_bits);
|
||||
// The predecessor is closer if n is a normalized power of 2 (f == 0)
|
||||
// other than the smallest normalized number (biased_e > 1).
|
||||
auto is_predecessor_closer = f == 0 && biased_e > 1;
|
||||
if (biased_e == 0)
|
||||
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
|
||||
else if (has_implicit_bit<Float>())
|
||||
f += static_cast<F>(implicit_bit);
|
||||
e = biased_e - exponent_bias<Float>() - num_float_significand_bits;
|
||||
if (!has_implicit_bit<Float>()) ++e;
|
||||
return is_predecessor_closer;
|
||||
}
|
||||
|
||||
template <typename Float, FMT_ENABLE_IF(is_double_double<Float>::value)>
|
||||
FMT_CONSTEXPR auto assign(Float n) -> bool {
|
||||
static_assert(std::numeric_limits<double>::is_iec559, "unsupported FP");
|
||||
return assign(static_cast<double>(n));
|
||||
}
|
||||
};
|
||||
|
||||
using fp = basic_fp<unsigned long long>;
|
||||
|
||||
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
|
||||
template <int SHIFT = 0, typename F>
|
||||
FMT_CONSTEXPR basic_fp<F> normalize(basic_fp<F> value) {
|
||||
// Handle subnormals.
|
||||
const auto implicit_bit = F(1) << num_significand_bits<double>();
|
||||
const auto shifted_implicit_bit = implicit_bit << SHIFT;
|
||||
while ((value.f & shifted_implicit_bit) == 0) {
|
||||
value.f <<= 1;
|
||||
--value.e;
|
||||
}
|
||||
// Subtract 1 to account for hidden bit.
|
||||
const auto offset = basic_fp<F>::num_significand_bits -
|
||||
num_significand_bits<double>() - SHIFT - 1;
|
||||
value.f <<= offset;
|
||||
value.e -= offset;
|
||||
return value;
|
||||
}
|
||||
constexpr auto promote_float(float value) -> double {
|
||||
return static_cast<double>(value);
|
||||
|
||||
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
|
||||
FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
|
||||
#if FMT_USE_INT128
|
||||
auto product = static_cast<__uint128_t>(lhs) * rhs;
|
||||
auto f = static_cast<uint64_t>(product >> 64);
|
||||
return (static_cast<uint64_t>(product) & (1ULL << 63)) != 0 ? f + 1 : f;
|
||||
#else
|
||||
// Multiply 32-bit parts of significands.
|
||||
uint64_t mask = (1ULL << 32) - 1;
|
||||
uint64_t a = lhs >> 32, b = lhs & mask;
|
||||
uint64_t c = rhs >> 32, d = rhs & mask;
|
||||
uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
|
||||
// Compute mid 64-bit of result and round.
|
||||
uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
|
||||
return ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
|
||||
#endif
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR inline fp operator*(fp x, fp y) {
|
||||
return {multiply(x.f, y.f), x.e + y.e + 64};
|
||||
}
|
||||
|
||||
template <typename T = void> struct basic_data {
|
||||
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
|
||||
// These are generated by support/compute-powers.py.
|
||||
static constexpr uint64_t pow10_significands[87] = {
|
||||
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
|
||||
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
|
||||
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
|
||||
0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
|
||||
0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
|
||||
0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
|
||||
0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
|
||||
0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
|
||||
0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
|
||||
0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
|
||||
0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
|
||||
0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
|
||||
0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
|
||||
0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
|
||||
0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
|
||||
0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
|
||||
0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
|
||||
0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
|
||||
0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
|
||||
0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
|
||||
0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
|
||||
0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
|
||||
0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
|
||||
0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
|
||||
0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
|
||||
0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
|
||||
0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
|
||||
0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
|
||||
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
|
||||
};
|
||||
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wnarrowing"
|
||||
#endif
|
||||
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
|
||||
// to significands above.
|
||||
static constexpr int16_t pow10_exponents[87] = {
|
||||
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
|
||||
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
|
||||
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
|
||||
-343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77,
|
||||
-50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216,
|
||||
242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
|
||||
534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
|
||||
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066};
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
static constexpr uint64_t power_of_10_64[20] = {
|
||||
1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL),
|
||||
10000000000000000000ULL};
|
||||
|
||||
// For checking rounding thresholds.
|
||||
// The kth entry is chosen to be the smallest integer such that the
|
||||
// upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k.
|
||||
static constexpr uint32_t fractional_part_rounding_thresholds[8] = {
|
||||
2576980378, // ceil(2^31 + 2^32/10^1)
|
||||
2190433321, // ceil(2^31 + 2^32/10^2)
|
||||
2151778616, // ceil(2^31 + 2^32/10^3)
|
||||
2147913145, // ceil(2^31 + 2^32/10^4)
|
||||
2147526598, // ceil(2^31 + 2^32/10^5)
|
||||
2147487943, // ceil(2^31 + 2^32/10^6)
|
||||
2147484078, // ceil(2^31 + 2^32/10^7)
|
||||
2147483691 // ceil(2^31 + 2^32/10^8)
|
||||
};
|
||||
};
|
||||
|
||||
#if FMT_CPLUSPLUS < 201703L
|
||||
template <typename T> constexpr uint64_t basic_data<T>::pow10_significands[];
|
||||
template <typename T> constexpr int16_t basic_data<T>::pow10_exponents[];
|
||||
template <typename T> constexpr uint64_t basic_data<T>::power_of_10_64[];
|
||||
template <typename T>
|
||||
constexpr uint32_t basic_data<T>::fractional_part_rounding_thresholds[];
|
||||
#endif
|
||||
|
||||
// This is a struct rather than an alias to avoid shadowing warnings in gcc.
|
||||
struct data : basic_data<> {};
|
||||
|
||||
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
|
||||
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
|
||||
FMT_CONSTEXPR inline fp get_cached_power(int min_exponent,
|
||||
int& pow10_exponent) {
|
||||
const int shift = 32;
|
||||
// log10(2) = 0x0.4d104d427de7fbcc...
|
||||
const int64_t significand = 0x4d104d427de7fbcc;
|
||||
int index = static_cast<int>(
|
||||
((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) +
|
||||
((int64_t(1) << shift) - 1)) // ceil
|
||||
>> 32 // arithmetic shift
|
||||
);
|
||||
// Decimal exponent of the first (smallest) cached power of 10.
|
||||
const int first_dec_exp = -348;
|
||||
// Difference between 2 consecutive decimal exponents in cached powers of 10.
|
||||
const int dec_exp_step = 8;
|
||||
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
|
||||
pow10_exponent = first_dec_exp + index * dec_exp_step;
|
||||
// Using *(x + index) instead of x[index] avoids an issue with some compilers
|
||||
// using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode).
|
||||
return {*(data::pow10_significands + index),
|
||||
*(data::pow10_exponents + index)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using convert_float_result =
|
||||
conditional_t<std::is_same<T, float>::value ||
|
||||
std::numeric_limits<T>::digits ==
|
||||
std::numeric_limits<double>::digits,
|
||||
double, T>;
|
||||
|
||||
template <typename T>
|
||||
constexpr auto convert_float(T value) -> convert_float_result<T> {
|
||||
return static_cast<convert_float_result<T>>(value);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
@@ -1327,8 +1872,7 @@ FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n,
|
||||
// width: output display width in (terminal) column positions.
|
||||
template <align::type align = align::left, typename OutputIt, typename Char,
|
||||
typename F>
|
||||
FMT_CONSTEXPR auto write_padded(OutputIt out,
|
||||
const basic_format_specs<Char>& specs,
|
||||
FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs<Char>& specs,
|
||||
size_t size, size_t width, F&& f) -> OutputIt {
|
||||
static_assert(align == align::left || align == align::right, "");
|
||||
unsigned spec_width = to_unsigned(specs.width);
|
||||
@@ -1347,15 +1891,14 @@ FMT_CONSTEXPR auto write_padded(OutputIt out,
|
||||
|
||||
template <align::type align = align::left, typename OutputIt, typename Char,
|
||||
typename F>
|
||||
constexpr auto write_padded(OutputIt out, const basic_format_specs<Char>& specs,
|
||||
constexpr auto write_padded(OutputIt out, const format_specs<Char>& specs,
|
||||
size_t size, F&& f) -> OutputIt {
|
||||
return write_padded<align>(out, specs, size, size, f);
|
||||
}
|
||||
|
||||
template <align::type align = align::left, typename Char, typename OutputIt>
|
||||
FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes,
|
||||
const basic_format_specs<Char>& specs)
|
||||
-> OutputIt {
|
||||
const format_specs<Char>& specs) -> OutputIt {
|
||||
return write_padded<align>(
|
||||
out, specs, bytes.size(), [bytes](reserve_iterator<OutputIt> it) {
|
||||
const char* data = bytes.data();
|
||||
@@ -1364,8 +1907,8 @@ FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes,
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename UIntPtr>
|
||||
auto write_ptr(OutputIt out, UIntPtr value,
|
||||
const basic_format_specs<Char>* specs) -> OutputIt {
|
||||
auto write_ptr(OutputIt out, UIntPtr value, const format_specs<Char>* specs)
|
||||
-> OutputIt {
|
||||
int num_digits = count_digits<4>(value);
|
||||
auto size = to_unsigned(num_digits) + size_t(2);
|
||||
auto write = [=](reserve_iterator<OutputIt> it) {
|
||||
@@ -1377,22 +1920,183 @@ 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 <typename Char> struct find_escape_result {
|
||||
const Char* begin;
|
||||
const Char* end;
|
||||
uint32_t cp;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
using make_unsigned_char =
|
||||
typename conditional_t<std::is_integral<Char>::value,
|
||||
std::make_unsigned<Char>,
|
||||
type_identity<uint32_t>>::type;
|
||||
|
||||
template <typename Char>
|
||||
auto find_escape(const Char* begin, const Char* end)
|
||||
-> find_escape_result<Char> {
|
||||
for (; begin != end; ++begin) {
|
||||
uint32_t cp = static_cast<make_unsigned_char<Char>>(*begin);
|
||||
if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue;
|
||||
if (needs_escape(cp)) return {begin, begin + 1, cp};
|
||||
}
|
||||
return {begin, nullptr, 0};
|
||||
}
|
||||
|
||||
inline auto find_escape(const char* begin, const char* end)
|
||||
-> find_escape_result<char> {
|
||||
if (!is_utf8()) return find_escape<char>(begin, end);
|
||||
auto result = find_escape_result<char>{end, nullptr, 0};
|
||||
for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
|
||||
[&](uint32_t cp, string_view sv) {
|
||||
if (needs_escape(cp)) {
|
||||
result = {sv.begin(), sv.end(), cp};
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
#define FMT_STRING_IMPL(s, base, explicit) \
|
||||
[] { \
|
||||
/* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \
|
||||
/* Use a macro-like name to avoid shadowing warnings. */ \
|
||||
struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \
|
||||
using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t<decltype(s[0])>; \
|
||||
FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \
|
||||
operator fmt::basic_string_view<char_type>() const { \
|
||||
return fmt::detail_exported::compile_string_to_view<char_type>(s); \
|
||||
} \
|
||||
}; \
|
||||
return FMT_COMPILE_STRING(); \
|
||||
}()
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a compile-time format string from a string literal *s*.
|
||||
|
||||
**Example**::
|
||||
|
||||
// A compile-time error because 'd' is an invalid specifier for strings.
|
||||
std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
|
||||
\endrst
|
||||
*/
|
||||
#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, )
|
||||
|
||||
template <size_t width, typename Char, typename OutputIt>
|
||||
auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt {
|
||||
*out++ = static_cast<Char>('\\');
|
||||
*out++ = static_cast<Char>(prefix);
|
||||
Char buf[width];
|
||||
fill_n(buf, width, static_cast<Char>('0'));
|
||||
format_uint<4>(buf, cp, width);
|
||||
return copy_str<Char>(buf, buf + width, out);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
auto write_escaped_cp(OutputIt out, const find_escape_result<Char>& escape)
|
||||
-> OutputIt {
|
||||
auto c = static_cast<Char>(escape.cp);
|
||||
switch (escape.cp) {
|
||||
case '\n':
|
||||
*out++ = static_cast<Char>('\\');
|
||||
c = static_cast<Char>('n');
|
||||
break;
|
||||
case '\r':
|
||||
*out++ = static_cast<Char>('\\');
|
||||
c = static_cast<Char>('r');
|
||||
break;
|
||||
case '\t':
|
||||
*out++ = static_cast<Char>('\\');
|
||||
c = static_cast<Char>('t');
|
||||
break;
|
||||
case '"':
|
||||
FMT_FALLTHROUGH;
|
||||
case '\'':
|
||||
FMT_FALLTHROUGH;
|
||||
case '\\':
|
||||
*out++ = static_cast<Char>('\\');
|
||||
break;
|
||||
default:
|
||||
if (escape.cp < 0x100) {
|
||||
return write_codepoint<2, Char>(out, 'x', escape.cp);
|
||||
}
|
||||
if (escape.cp < 0x10000) {
|
||||
return write_codepoint<4, Char>(out, 'u', escape.cp);
|
||||
}
|
||||
if (escape.cp < 0x110000) {
|
||||
return write_codepoint<8, Char>(out, 'U', escape.cp);
|
||||
}
|
||||
for (Char escape_char : basic_string_view<Char>(
|
||||
escape.begin, to_unsigned(escape.end - escape.begin))) {
|
||||
out = write_codepoint<2, Char>(out, 'x',
|
||||
static_cast<uint32_t>(escape_char) & 0xFF);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
*out++ = c;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
auto write_escaped_string(OutputIt out, basic_string_view<Char> str)
|
||||
-> OutputIt {
|
||||
*out++ = static_cast<Char>('"');
|
||||
auto begin = str.begin(), end = str.end();
|
||||
do {
|
||||
auto escape = find_escape(begin, end);
|
||||
out = copy_str<Char>(begin, escape.begin, out);
|
||||
begin = escape.end;
|
||||
if (!begin) break;
|
||||
out = write_escaped_cp<OutputIt, Char>(out, escape);
|
||||
} while (begin != end);
|
||||
*out++ = static_cast<Char>('"');
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
auto write_escaped_char(OutputIt out, Char v) -> OutputIt {
|
||||
*out++ = static_cast<Char>('\'');
|
||||
if ((needs_escape(static_cast<uint32_t>(v)) && v != static_cast<Char>('"')) ||
|
||||
v == static_cast<Char>('\'')) {
|
||||
out = write_escaped_cp(
|
||||
out, find_escape_result<Char>{&v, &v + 1, static_cast<uint32_t>(v)});
|
||||
} else {
|
||||
*out++ = v;
|
||||
}
|
||||
*out++ = static_cast<Char>('\'');
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
FMT_CONSTEXPR auto write_char(OutputIt out, Char value,
|
||||
const basic_format_specs<Char>& specs)
|
||||
-> OutputIt {
|
||||
const format_specs<Char>& specs) -> OutputIt {
|
||||
bool is_debug = specs.type == presentation_type::debug;
|
||||
return write_padded(out, specs, 1, [=](reserve_iterator<OutputIt> it) {
|
||||
if (is_debug) return write_escaped_char(it, value);
|
||||
*it++ = value;
|
||||
return it;
|
||||
});
|
||||
}
|
||||
template <typename Char, typename OutputIt>
|
||||
FMT_CONSTEXPR auto write(OutputIt out, Char value,
|
||||
const basic_format_specs<Char>& specs,
|
||||
locale_ref loc = {}) -> OutputIt {
|
||||
const format_specs<Char>& specs, locale_ref loc = {})
|
||||
-> OutputIt {
|
||||
// char is formatted as unsigned char for consistency across platforms.
|
||||
using unsigned_type =
|
||||
conditional_t<std::is_same<Char, char>::value, unsigned char, unsigned>;
|
||||
return check_char_specs(specs)
|
||||
? write_char(out, value, specs)
|
||||
: write(out, static_cast<int>(value), specs, loc);
|
||||
: write(out, static_cast<unsigned_type>(value), specs, loc);
|
||||
}
|
||||
|
||||
// Data for write_int that doesn't depend on output iterator type. It is used to
|
||||
@@ -1402,7 +2106,7 @@ template <typename Char> struct write_int_data {
|
||||
size_t padding;
|
||||
|
||||
FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix,
|
||||
const basic_format_specs<Char>& specs)
|
||||
const format_specs<Char>& specs)
|
||||
: size((prefix >> 24) + to_unsigned(num_digits)), padding(0) {
|
||||
if (specs.align == align::numeric) {
|
||||
auto width = to_unsigned(specs.width);
|
||||
@@ -1424,7 +2128,7 @@ template <typename Char> struct write_int_data {
|
||||
template <typename OutputIt, typename Char, typename W>
|
||||
FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits,
|
||||
unsigned prefix,
|
||||
const basic_format_specs<Char>& specs,
|
||||
const format_specs<Char>& specs,
|
||||
W write_digits) -> OutputIt {
|
||||
// Slightly faster check for specs.width == 0 && specs.precision == -1.
|
||||
if ((specs.width | (specs.precision + 1)) == 0) {
|
||||
@@ -1447,19 +2151,19 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits,
|
||||
|
||||
template <typename Char> class digit_grouping {
|
||||
private:
|
||||
thousands_sep_result<Char> sep_;
|
||||
std::string grouping_;
|
||||
std::basic_string<Char> thousands_sep_;
|
||||
|
||||
struct next_state {
|
||||
std::string::const_iterator group;
|
||||
int pos;
|
||||
};
|
||||
next_state initial_state() const { return {sep_.grouping.begin(), 0}; }
|
||||
next_state initial_state() const { return {grouping_.begin(), 0}; }
|
||||
|
||||
// Returns the next digit group separator position.
|
||||
int next(next_state& state) const {
|
||||
if (!sep_.thousands_sep) return max_value<int>();
|
||||
if (state.group == sep_.grouping.end())
|
||||
return state.pos += sep_.grouping.back();
|
||||
if (thousands_sep_.empty()) return max_value<int>();
|
||||
if (state.group == grouping_.end()) return state.pos += grouping_.back();
|
||||
if (*state.group <= 0 || *state.group == max_value<char>())
|
||||
return max_value<int>();
|
||||
state.pos += *state.group++;
|
||||
@@ -1468,14 +2172,15 @@ template <typename Char> class digit_grouping {
|
||||
|
||||
public:
|
||||
explicit digit_grouping(locale_ref loc, bool localized = true) {
|
||||
if (localized)
|
||||
sep_ = thousands_sep<Char>(loc);
|
||||
else
|
||||
sep_.thousands_sep = Char();
|
||||
if (!localized) return;
|
||||
auto sep = thousands_sep<Char>(loc);
|
||||
grouping_ = sep.grouping;
|
||||
if (sep.thousands_sep) thousands_sep_.assign(1, sep.thousands_sep);
|
||||
}
|
||||
explicit digit_grouping(thousands_sep_result<Char> sep) : sep_(sep) {}
|
||||
digit_grouping(std::string grouping, std::basic_string<Char> sep)
|
||||
: grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {}
|
||||
|
||||
Char separator() const { return sep_.thousands_sep; }
|
||||
bool has_separator() const { return !thousands_sep_.empty(); }
|
||||
|
||||
int count_separators(int num_digits) const {
|
||||
int count = 0;
|
||||
@@ -1498,7 +2203,9 @@ template <typename Char> class digit_grouping {
|
||||
for (int i = 0, sep_index = static_cast<int>(separators.size() - 1);
|
||||
i < num_digits; ++i) {
|
||||
if (num_digits - i == separators[sep_index]) {
|
||||
*out++ = separator();
|
||||
out =
|
||||
copy_str<Char>(thousands_sep_.data(),
|
||||
thousands_sep_.data() + thousands_sep_.size(), out);
|
||||
--sep_index;
|
||||
}
|
||||
*out++ = static_cast<Char>(digits[to_unsigned(i)]);
|
||||
@@ -1507,10 +2214,11 @@ template <typename Char> class digit_grouping {
|
||||
}
|
||||
};
|
||||
|
||||
// Writes a decimal integer with digit grouping.
|
||||
template <typename OutputIt, typename UInt, typename Char>
|
||||
auto write_int_localized(OutputIt out, UInt value, unsigned prefix,
|
||||
const basic_format_specs<Char>& specs,
|
||||
const digit_grouping<Char>& grouping) -> OutputIt {
|
||||
auto write_int(OutputIt out, UInt value, unsigned prefix,
|
||||
const format_specs<Char>& specs,
|
||||
const digit_grouping<Char>& grouping) -> OutputIt {
|
||||
static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
|
||||
int num_digits = count_digits(value);
|
||||
char digits[40];
|
||||
@@ -1519,18 +2227,21 @@ auto write_int_localized(OutputIt out, UInt value, unsigned prefix,
|
||||
grouping.count_separators(num_digits));
|
||||
return write_padded<align::right>(
|
||||
out, specs, size, size, [&](reserve_iterator<OutputIt> it) {
|
||||
if (prefix != 0) *it++ = static_cast<Char>(prefix);
|
||||
if (prefix != 0) {
|
||||
char sign = static_cast<char>(prefix);
|
||||
*it++ = static_cast<Char>(sign);
|
||||
}
|
||||
return grouping.apply(it, string_view(digits, to_unsigned(num_digits)));
|
||||
});
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename UInt, typename Char>
|
||||
auto write_int_localized(OutputIt& out, UInt value, unsigned prefix,
|
||||
const basic_format_specs<Char>& specs, locale_ref loc)
|
||||
-> bool {
|
||||
auto grouping = digit_grouping<Char>(loc);
|
||||
out = write_int_localized(out, value, prefix, specs, grouping);
|
||||
return true;
|
||||
// Writes a localized value.
|
||||
FMT_API auto write_loc(appender out, loc_value value,
|
||||
const format_specs<>& specs, locale_ref loc) -> bool;
|
||||
template <typename OutputIt, typename Char>
|
||||
inline auto write_loc(OutputIt, loc_value, const format_specs<Char>&,
|
||||
locale_ref) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
|
||||
@@ -1559,21 +2270,37 @@ FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign)
|
||||
return {abs_value, prefix};
|
||||
}
|
||||
|
||||
template <typename Char = char> struct loc_writer {
|
||||
buffer_appender<Char> out;
|
||||
const format_specs<Char>& specs;
|
||||
std::basic_string<Char> sep;
|
||||
std::string grouping;
|
||||
std::basic_string<Char> decimal_point;
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
|
||||
auto operator()(T value) -> bool {
|
||||
auto arg = make_write_int_arg(value, specs.sign);
|
||||
write_int(out, static_cast<uint64_or_128_t<T>>(arg.abs_value), arg.prefix,
|
||||
specs, digit_grouping<Char>(grouping, sep));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
|
||||
auto operator()(T) -> bool {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename OutputIt, typename T>
|
||||
FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
|
||||
const basic_format_specs<Char>& specs,
|
||||
locale_ref loc) -> OutputIt {
|
||||
const format_specs<Char>& specs,
|
||||
locale_ref) -> OutputIt {
|
||||
static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
|
||||
auto abs_value = arg.abs_value;
|
||||
auto prefix = arg.prefix;
|
||||
switch (specs.type) {
|
||||
case presentation_type::none:
|
||||
case presentation_type::dec: {
|
||||
if (specs.localized &&
|
||||
write_int_localized(out, static_cast<uint64_or_128_t<T>>(abs_value),
|
||||
prefix, specs, loc)) {
|
||||
return out;
|
||||
}
|
||||
auto num_digits = count_digits(abs_value);
|
||||
return write_int(
|
||||
out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
|
||||
@@ -1616,13 +2343,13 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
|
||||
case presentation_type::chr:
|
||||
return write_char(out, static_cast<Char>(abs_value), specs);
|
||||
default:
|
||||
throw_format_error("invalid type specifier");
|
||||
throw_format_error("invalid format specifier");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Char, typename OutputIt, typename T>
|
||||
FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(
|
||||
OutputIt out, write_int_arg<T> arg, const basic_format_specs<Char>& specs,
|
||||
OutputIt out, write_int_arg<T> arg, const format_specs<Char>& specs,
|
||||
locale_ref loc) -> OutputIt {
|
||||
return write_int(out, arg, specs, loc);
|
||||
}
|
||||
@@ -1631,8 +2358,9 @@ template <typename Char, typename OutputIt, typename T,
|
||||
!std::is_same<T, bool>::value &&
|
||||
std::is_same<OutputIt, buffer_appender<Char>>::value)>
|
||||
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
|
||||
const basic_format_specs<Char>& specs,
|
||||
const format_specs<Char>& specs,
|
||||
locale_ref loc) -> OutputIt {
|
||||
if (specs.localized && write_loc(out, value, specs, loc)) return out;
|
||||
return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs,
|
||||
loc);
|
||||
}
|
||||
@@ -1642,48 +2370,179 @@ template <typename Char, typename OutputIt, typename T,
|
||||
!std::is_same<T, bool>::value &&
|
||||
!std::is_same<OutputIt, buffer_appender<Char>>::value)>
|
||||
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
|
||||
const basic_format_specs<Char>& specs,
|
||||
const format_specs<Char>& specs,
|
||||
locale_ref loc) -> OutputIt {
|
||||
if (specs.localized && write_loc(out, value, specs, loc)) return out;
|
||||
return write_int(out, make_write_int_arg(value, specs.sign), specs, loc);
|
||||
}
|
||||
|
||||
// An output iterator that counts the number of objects written to it and
|
||||
// discards them.
|
||||
class counting_iterator {
|
||||
private:
|
||||
size_t count_;
|
||||
|
||||
public:
|
||||
using iterator_category = std::output_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = void;
|
||||
using reference = void;
|
||||
FMT_UNCHECKED_ITERATOR(counting_iterator);
|
||||
|
||||
struct value_type {
|
||||
template <typename T> FMT_CONSTEXPR void operator=(const T&) {}
|
||||
};
|
||||
|
||||
FMT_CONSTEXPR counting_iterator() : count_(0) {}
|
||||
|
||||
FMT_CONSTEXPR size_t count() const { return count_; }
|
||||
|
||||
FMT_CONSTEXPR counting_iterator& operator++() {
|
||||
++count_;
|
||||
return *this;
|
||||
}
|
||||
FMT_CONSTEXPR counting_iterator operator++(int) {
|
||||
auto it = *this;
|
||||
++*this;
|
||||
return it;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it,
|
||||
difference_type n) {
|
||||
it.count_ += static_cast<size_t>(n);
|
||||
return it;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR value_type operator*() const { return {}; }
|
||||
};
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s,
|
||||
const basic_format_specs<Char>& specs) -> OutputIt {
|
||||
const format_specs<Char>& specs) -> OutputIt {
|
||||
auto data = s.data();
|
||||
auto size = s.size();
|
||||
if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
|
||||
size = code_point_index(s, to_unsigned(specs.precision));
|
||||
auto width =
|
||||
specs.width != 0 ? compute_width(basic_string_view<Char>(data, size)) : 0;
|
||||
bool is_debug = specs.type == presentation_type::debug;
|
||||
size_t width = 0;
|
||||
if (specs.width != 0) {
|
||||
if (is_debug)
|
||||
width = write_escaped_string(counting_iterator{}, s).count();
|
||||
else
|
||||
width = compute_width(basic_string_view<Char>(data, size));
|
||||
}
|
||||
return write_padded(out, specs, size, width,
|
||||
[=](reserve_iterator<OutputIt> it) {
|
||||
if (is_debug) return write_escaped_string(it, s);
|
||||
return copy_str<Char>(data, data + size, it);
|
||||
});
|
||||
}
|
||||
template <typename Char, typename OutputIt>
|
||||
FMT_CONSTEXPR auto write(OutputIt out,
|
||||
basic_string_view<type_identity_t<Char>> s,
|
||||
const basic_format_specs<Char>& specs, locale_ref)
|
||||
const format_specs<Char>& specs, locale_ref)
|
||||
-> OutputIt {
|
||||
check_string_type_spec(specs.type);
|
||||
return write(out, s, specs);
|
||||
}
|
||||
template <typename Char, typename OutputIt>
|
||||
FMT_CONSTEXPR auto write(OutputIt out, const Char* s,
|
||||
const basic_format_specs<Char>& specs, locale_ref)
|
||||
const format_specs<Char>& specs, locale_ref)
|
||||
-> OutputIt {
|
||||
return check_cstring_type_spec(specs.type)
|
||||
return specs.type != presentation_type::pointer
|
||||
? write(out, basic_string_view<Char>(s), specs, {})
|
||||
: write_ptr<Char>(out, to_uintptr(s), &specs);
|
||||
: write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(is_integral<T>::value &&
|
||||
!std::is_same<T, bool>::value &&
|
||||
!std::is_same<T, Char>::value)>
|
||||
FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
|
||||
auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
|
||||
bool negative = is_negative(value);
|
||||
// Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
|
||||
if (negative) abs_value = ~abs_value + 1;
|
||||
int num_digits = count_digits(abs_value);
|
||||
auto size = (negative ? 1 : 0) + static_cast<size_t>(num_digits);
|
||||
auto it = reserve(out, size);
|
||||
if (auto ptr = to_pointer<Char>(it, size)) {
|
||||
if (negative) *ptr++ = static_cast<Char>('-');
|
||||
format_decimal<Char>(ptr, abs_value, num_digits);
|
||||
return out;
|
||||
}
|
||||
if (negative) *it++ = static_cast<Char>('-');
|
||||
it = format_decimal<Char>(it, abs_value, num_digits).end;
|
||||
return base_iterator(out, it);
|
||||
}
|
||||
|
||||
// A floating-point presentation format.
|
||||
enum class float_format : unsigned char {
|
||||
general, // General: exponent notation or fixed point based on magnitude.
|
||||
exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3.
|
||||
fixed, // Fixed point with the default precision of 6, e.g. 0.0012.
|
||||
hex
|
||||
};
|
||||
|
||||
struct float_specs {
|
||||
int precision;
|
||||
float_format format : 8;
|
||||
sign_t sign : 8;
|
||||
bool upper : 1;
|
||||
bool locale : 1;
|
||||
bool binary32 : 1;
|
||||
bool showpoint : 1;
|
||||
};
|
||||
|
||||
template <typename ErrorHandler = error_handler, typename Char>
|
||||
FMT_CONSTEXPR auto parse_float_type_spec(const format_specs<Char>& specs,
|
||||
ErrorHandler&& eh = {})
|
||||
-> float_specs {
|
||||
auto result = float_specs();
|
||||
result.showpoint = specs.alt;
|
||||
result.locale = specs.localized;
|
||||
switch (specs.type) {
|
||||
case presentation_type::none:
|
||||
result.format = float_format::general;
|
||||
break;
|
||||
case presentation_type::general_upper:
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case presentation_type::general_lower:
|
||||
result.format = float_format::general;
|
||||
break;
|
||||
case presentation_type::exp_upper:
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case presentation_type::exp_lower:
|
||||
result.format = float_format::exp;
|
||||
result.showpoint |= specs.precision != 0;
|
||||
break;
|
||||
case presentation_type::fixed_upper:
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case presentation_type::fixed_lower:
|
||||
result.format = float_format::fixed;
|
||||
result.showpoint |= specs.precision != 0;
|
||||
break;
|
||||
case presentation_type::hexfloat_upper:
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case presentation_type::hexfloat_lower:
|
||||
result.format = float_format::hex;
|
||||
break;
|
||||
default:
|
||||
eh.on_error("invalid format specifier");
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isinf,
|
||||
basic_format_specs<Char> specs,
|
||||
FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan,
|
||||
format_specs<Char> specs,
|
||||
const float_specs& fspecs) -> OutputIt {
|
||||
auto str =
|
||||
isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan");
|
||||
isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf");
|
||||
constexpr size_t str_size = 3;
|
||||
auto sign = fspecs.sign;
|
||||
auto size = str_size + (sign ? 1 : 0);
|
||||
@@ -1704,12 +2563,12 @@ struct big_decimal_fp {
|
||||
int exponent;
|
||||
};
|
||||
|
||||
constexpr auto get_significand_size(const big_decimal_fp& fp) -> int {
|
||||
return fp.significand_size;
|
||||
constexpr auto get_significand_size(const big_decimal_fp& f) -> int {
|
||||
return f.significand_size;
|
||||
}
|
||||
template <typename T>
|
||||
inline auto get_significand_size(const dragonbox::decimal_fp<T>& fp) -> int {
|
||||
return count_digits(fp.significand);
|
||||
inline auto get_significand_size(const dragonbox::decimal_fp<T>& f) -> int {
|
||||
return count_digits(f.significand);
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
@@ -1726,7 +2585,7 @@ template <typename Char, typename OutputIt, typename T, typename Grouping>
|
||||
FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
|
||||
int significand_size, int exponent,
|
||||
const Grouping& grouping) -> OutputIt {
|
||||
if (!grouping.separator()) {
|
||||
if (!grouping.has_separator()) {
|
||||
out = write_significand<Char>(out, significand, significand_size);
|
||||
return detail::fill_n(out, exponent, static_cast<Char>('0'));
|
||||
}
|
||||
@@ -1747,7 +2606,7 @@ inline auto write_significand(Char* out, UInt significand, int significand_size,
|
||||
int floating_size = significand_size - integral_size;
|
||||
for (int i = floating_size / 2; i > 0; --i) {
|
||||
out -= 2;
|
||||
copy2(out, digits2(significand % 100));
|
||||
copy2(out, digits2(static_cast<std::size_t>(significand % 100)));
|
||||
significand /= 100;
|
||||
}
|
||||
if (floating_size % 2 != 0) {
|
||||
@@ -1788,7 +2647,7 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
|
||||
int significand_size, int integral_size,
|
||||
Char decimal_point,
|
||||
const Grouping& grouping) -> OutputIt {
|
||||
if (!grouping.separator()) {
|
||||
if (!grouping.has_separator()) {
|
||||
return write_significand(out, significand, significand_size, integral_size,
|
||||
decimal_point);
|
||||
}
|
||||
@@ -1803,13 +2662,13 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
|
||||
|
||||
template <typename OutputIt, typename DecimalFP, typename Char,
|
||||
typename Grouping = digit_grouping<Char>>
|
||||
FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp,
|
||||
const basic_format_specs<Char>& specs,
|
||||
FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
|
||||
const format_specs<Char>& specs,
|
||||
float_specs fspecs, locale_ref loc)
|
||||
-> OutputIt {
|
||||
auto significand = fp.significand;
|
||||
int significand_size = get_significand_size(fp);
|
||||
constexpr Char zero = static_cast<Char>('0');
|
||||
auto significand = f.significand;
|
||||
int significand_size = get_significand_size(f);
|
||||
const Char zero = static_cast<Char>('0');
|
||||
auto sign = fspecs.sign;
|
||||
size_t size = to_unsigned(significand_size) + (sign ? 1 : 0);
|
||||
using iterator = reserve_iterator<OutputIt>;
|
||||
@@ -1817,7 +2676,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp,
|
||||
Char decimal_point =
|
||||
fspecs.locale ? detail::decimal_point<Char>(loc) : static_cast<Char>('.');
|
||||
|
||||
int output_exp = fp.exponent + significand_size - 1;
|
||||
int output_exp = f.exponent + significand_size - 1;
|
||||
auto use_exp_format = [=]() {
|
||||
if (fspecs.format == float_format::exp) return true;
|
||||
if (fspecs.format != float_format::general) return false;
|
||||
@@ -1855,25 +2714,23 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp,
|
||||
: base_iterator(out, write(reserve(out, size)));
|
||||
}
|
||||
|
||||
int exp = fp.exponent + significand_size;
|
||||
if (fp.exponent >= 0) {
|
||||
int exp = f.exponent + significand_size;
|
||||
if (f.exponent >= 0) {
|
||||
// 1234e5 -> 123400000[.0+]
|
||||
size += to_unsigned(fp.exponent);
|
||||
size += to_unsigned(f.exponent);
|
||||
int num_zeros = fspecs.precision - exp;
|
||||
#ifdef FMT_FUZZ
|
||||
if (num_zeros > 5000)
|
||||
throw std::runtime_error("fuzz mode - avoiding excessive cpu use");
|
||||
#endif
|
||||
abort_fuzzing_if(num_zeros > 5000);
|
||||
if (fspecs.showpoint) {
|
||||
if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1;
|
||||
if (num_zeros > 0) size += to_unsigned(num_zeros) + 1;
|
||||
++size;
|
||||
if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 0;
|
||||
if (num_zeros > 0) size += to_unsigned(num_zeros);
|
||||
}
|
||||
auto grouping = Grouping(loc, fspecs.locale);
|
||||
size += to_unsigned(grouping.count_separators(significand_size));
|
||||
size += to_unsigned(grouping.count_separators(exp));
|
||||
return write_padded<align::right>(out, specs, size, [&](iterator it) {
|
||||
if (sign) *it++ = detail::sign<Char>(sign);
|
||||
it = write_significand<Char>(it, significand, significand_size,
|
||||
fp.exponent, grouping);
|
||||
f.exponent, grouping);
|
||||
if (!fspecs.showpoint) return it;
|
||||
*it++ = decimal_point;
|
||||
return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
|
||||
@@ -1883,7 +2740,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp,
|
||||
int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0;
|
||||
size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0);
|
||||
auto grouping = Grouping(loc, fspecs.locale);
|
||||
size += to_unsigned(grouping.count_separators(significand_size));
|
||||
size += to_unsigned(grouping.count_separators(exp));
|
||||
return write_padded<align::right>(out, specs, size, [&](iterator it) {
|
||||
if (sign) *it++ = detail::sign<Char>(sign);
|
||||
it = write_significand(it, significand, significand_size, exp,
|
||||
@@ -1913,7 +2770,7 @@ template <typename Char> class fallback_digit_grouping {
|
||||
public:
|
||||
constexpr fallback_digit_grouping(locale_ref, bool) {}
|
||||
|
||||
constexpr Char separator() const { return Char(); }
|
||||
constexpr bool has_separator() const { return false; }
|
||||
|
||||
constexpr int count_separators(int) const { return 0; }
|
||||
|
||||
@@ -1924,67 +2781,1044 @@ template <typename Char> class fallback_digit_grouping {
|
||||
};
|
||||
|
||||
template <typename OutputIt, typename DecimalFP, typename Char>
|
||||
FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& fp,
|
||||
const basic_format_specs<Char>& specs,
|
||||
FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f,
|
||||
const format_specs<Char>& specs,
|
||||
float_specs fspecs, locale_ref loc)
|
||||
-> OutputIt {
|
||||
if (is_constant_evaluated()) {
|
||||
return do_write_float<OutputIt, DecimalFP, Char,
|
||||
fallback_digit_grouping<Char>>(out, fp, specs, fspecs,
|
||||
fallback_digit_grouping<Char>>(out, f, specs, fspecs,
|
||||
loc);
|
||||
} else {
|
||||
return do_write_float(out, fp, specs, fspecs, loc);
|
||||
return do_write_float(out, f, specs, fspecs, loc);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
FMT_CONSTEXPR20 bool isinf(T value) {
|
||||
if (is_constant_evaluated()) {
|
||||
#if defined(__cpp_if_constexpr)
|
||||
if constexpr (std::numeric_limits<double>::is_iec559) {
|
||||
auto bits = detail::bit_cast<uint64_t>(static_cast<double>(value));
|
||||
constexpr auto significand_bits =
|
||||
dragonbox::float_info<double>::significand_bits;
|
||||
return (bits & exponent_mask<double>()) &&
|
||||
!(bits & ((uint64_t(1) << significand_bits) - 1));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return std::isinf(value);
|
||||
template <typename T> constexpr bool isnan(T value) {
|
||||
return !(value >= value); // std::isnan doesn't support __float128.
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
template <typename T, typename Enable = void>
|
||||
struct has_isfinite : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_isfinite<T, enable_if_t<sizeof(std::isfinite(T())) != 0>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value&&
|
||||
has_isfinite<T>::value)>
|
||||
FMT_CONSTEXPR20 bool isfinite(T value) {
|
||||
if (is_constant_evaluated()) {
|
||||
#if defined(__cpp_if_constexpr)
|
||||
if constexpr (std::numeric_limits<double>::is_iec559) {
|
||||
auto bits = detail::bit_cast<uint64_t>(static_cast<double>(value));
|
||||
return (bits & exponent_mask<double>()) != exponent_mask<double>();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
constexpr T inf = T(std::numeric_limits<double>::infinity());
|
||||
if (is_constant_evaluated())
|
||||
return !detail::isnan(value) && value < inf && value > -inf;
|
||||
return std::isfinite(value);
|
||||
}
|
||||
template <typename T, FMT_ENABLE_IF(!has_isfinite<T>::value)>
|
||||
FMT_CONSTEXPR bool isfinite(T value) {
|
||||
T inf = T(std::numeric_limits<double>::infinity());
|
||||
// std::isfinite doesn't support __float128.
|
||||
return !detail::isnan(value) && value < inf && value > -inf;
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
template <typename T, FMT_ENABLE_IF(is_floating_point<T>::value)>
|
||||
FMT_INLINE FMT_CONSTEXPR bool signbit(T value) {
|
||||
if (is_constant_evaluated()) {
|
||||
#ifdef __cpp_if_constexpr
|
||||
if constexpr (std::numeric_limits<double>::is_iec559) {
|
||||
auto bits = detail::bit_cast<uint64_t>(static_cast<double>(value));
|
||||
return (bits & (uint64_t(1) << (num_bits<uint64_t>() - 1))) != 0;
|
||||
return (bits >> (num_bits<uint64_t>() - 1)) != 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return std::signbit(value);
|
||||
return std::signbit(static_cast<double>(value));
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
FMT_CONSTEXPR20 auto write(OutputIt out, T value,
|
||||
basic_format_specs<Char> specs, locale_ref loc = {})
|
||||
enum class round_direction { unknown, up, down };
|
||||
|
||||
// Given the divisor (normally a power of 10), the remainder = v % divisor for
|
||||
// some number v and the error, returns whether v should be rounded up, down, or
|
||||
// whether the rounding direction can't be determined due to error.
|
||||
// error should be less than divisor / 2.
|
||||
FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor,
|
||||
uint64_t remainder,
|
||||
uint64_t error) {
|
||||
FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow.
|
||||
FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow.
|
||||
FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
|
||||
// Round down if (remainder + error) * 2 <= divisor.
|
||||
if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
|
||||
return round_direction::down;
|
||||
// Round up if (remainder - error) * 2 >= divisor.
|
||||
if (remainder >= error &&
|
||||
remainder - error >= divisor - (remainder - error)) {
|
||||
return round_direction::up;
|
||||
}
|
||||
return round_direction::unknown;
|
||||
}
|
||||
|
||||
namespace digits {
|
||||
enum result {
|
||||
more, // Generate more digits.
|
||||
done, // Done generating digits.
|
||||
error // Digit generation cancelled due to an error.
|
||||
};
|
||||
}
|
||||
|
||||
struct gen_digits_handler {
|
||||
char* buf;
|
||||
int size;
|
||||
int precision;
|
||||
int exp10;
|
||||
bool fixed;
|
||||
|
||||
FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor,
|
||||
uint64_t remainder, uint64_t error,
|
||||
bool integral) {
|
||||
FMT_ASSERT(remainder < divisor, "");
|
||||
buf[size++] = digit;
|
||||
if (!integral && error >= remainder) return digits::error;
|
||||
if (size < precision) return digits::more;
|
||||
if (!integral) {
|
||||
// Check if error * 2 < divisor with overflow prevention.
|
||||
// The check is not needed for the integral part because error = 1
|
||||
// and divisor > (1 << 32) there.
|
||||
if (error >= divisor || error >= divisor - error) return digits::error;
|
||||
} else {
|
||||
FMT_ASSERT(error == 1 && divisor > 2, "");
|
||||
}
|
||||
auto dir = get_round_direction(divisor, remainder, error);
|
||||
if (dir != round_direction::up)
|
||||
return dir == round_direction::down ? digits::done : digits::error;
|
||||
++buf[size - 1];
|
||||
for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
|
||||
buf[i] = '0';
|
||||
++buf[i - 1];
|
||||
}
|
||||
if (buf[0] > '9') {
|
||||
buf[0] = '1';
|
||||
if (fixed)
|
||||
buf[size++] = '0';
|
||||
else
|
||||
++exp10;
|
||||
}
|
||||
return digits::done;
|
||||
}
|
||||
};
|
||||
|
||||
inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) {
|
||||
// Adjust fixed precision by exponent because it is relative to decimal
|
||||
// point.
|
||||
if (exp10 > 0 && precision > max_value<int>() - exp10)
|
||||
FMT_THROW(format_error("number is too big"));
|
||||
precision += exp10;
|
||||
}
|
||||
|
||||
// Generates output using the Grisu digit-gen algorithm.
|
||||
// error: the size of the region (lower, upper) outside of which numbers
|
||||
// definitely do not round to value (Delta in Grisu3).
|
||||
FMT_INLINE FMT_CONSTEXPR20 auto grisu_gen_digits(fp value, uint64_t error,
|
||||
int& exp,
|
||||
gen_digits_handler& handler)
|
||||
-> digits::result {
|
||||
const fp one(1ULL << -value.e, value.e);
|
||||
// The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
|
||||
// zero because it contains a product of two 64-bit numbers with MSB set (due
|
||||
// to normalization) - 1, shifted right by at most 60 bits.
|
||||
auto integral = static_cast<uint32_t>(value.f >> -one.e);
|
||||
FMT_ASSERT(integral != 0, "");
|
||||
FMT_ASSERT(integral == value.f >> -one.e, "");
|
||||
// The fractional part of scaled value (p2 in Grisu) c = value % one.
|
||||
uint64_t fractional = value.f & (one.f - 1);
|
||||
exp = count_digits(integral); // kappa in Grisu.
|
||||
// Non-fixed formats require at least one digit and no precision adjustment.
|
||||
if (handler.fixed) {
|
||||
adjust_precision(handler.precision, exp + handler.exp10);
|
||||
// Check if precision is satisfied just by leading zeros, e.g.
|
||||
// format("{:.2f}", 0.001) gives "0.00" without generating any digits.
|
||||
if (handler.precision <= 0) {
|
||||
if (handler.precision < 0) return digits::done;
|
||||
// Divide by 10 to prevent overflow.
|
||||
uint64_t divisor = data::power_of_10_64[exp - 1] << -one.e;
|
||||
auto dir = get_round_direction(divisor, value.f / 10, error * 10);
|
||||
if (dir == round_direction::unknown) return digits::error;
|
||||
handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0';
|
||||
return digits::done;
|
||||
}
|
||||
}
|
||||
// Generate digits for the integral part. This can produce up to 10 digits.
|
||||
do {
|
||||
uint32_t digit = 0;
|
||||
auto divmod_integral = [&](uint32_t divisor) {
|
||||
digit = integral / divisor;
|
||||
integral %= divisor;
|
||||
};
|
||||
// This optimization by Milo Yip reduces the number of integer divisions by
|
||||
// one per iteration.
|
||||
switch (exp) {
|
||||
case 10:
|
||||
divmod_integral(1000000000);
|
||||
break;
|
||||
case 9:
|
||||
divmod_integral(100000000);
|
||||
break;
|
||||
case 8:
|
||||
divmod_integral(10000000);
|
||||
break;
|
||||
case 7:
|
||||
divmod_integral(1000000);
|
||||
break;
|
||||
case 6:
|
||||
divmod_integral(100000);
|
||||
break;
|
||||
case 5:
|
||||
divmod_integral(10000);
|
||||
break;
|
||||
case 4:
|
||||
divmod_integral(1000);
|
||||
break;
|
||||
case 3:
|
||||
divmod_integral(100);
|
||||
break;
|
||||
case 2:
|
||||
divmod_integral(10);
|
||||
break;
|
||||
case 1:
|
||||
digit = integral;
|
||||
integral = 0;
|
||||
break;
|
||||
default:
|
||||
FMT_ASSERT(false, "invalid number of digits");
|
||||
}
|
||||
--exp;
|
||||
auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
|
||||
auto result = handler.on_digit(static_cast<char>('0' + digit),
|
||||
data::power_of_10_64[exp] << -one.e,
|
||||
remainder, error, true);
|
||||
if (result != digits::more) return result;
|
||||
} while (exp > 0);
|
||||
// Generate digits for the fractional part.
|
||||
for (;;) {
|
||||
fractional *= 10;
|
||||
error *= 10;
|
||||
char digit = static_cast<char>('0' + (fractional >> -one.e));
|
||||
fractional &= one.f - 1;
|
||||
--exp;
|
||||
auto result = handler.on_digit(digit, one.f, fractional, error, false);
|
||||
if (result != digits::more) return result;
|
||||
}
|
||||
}
|
||||
|
||||
class bigint {
|
||||
private:
|
||||
// A bigint is stored as an array of bigits (big digits), with bigit at index
|
||||
// 0 being the least significant one.
|
||||
using bigit = uint32_t;
|
||||
using double_bigit = uint64_t;
|
||||
enum { bigits_capacity = 32 };
|
||||
basic_memory_buffer<bigit, bigits_capacity> bigits_;
|
||||
int exp_;
|
||||
|
||||
FMT_CONSTEXPR20 bigit operator[](int index) const {
|
||||
return bigits_[to_unsigned(index)];
|
||||
}
|
||||
FMT_CONSTEXPR20 bigit& operator[](int index) {
|
||||
return bigits_[to_unsigned(index)];
|
||||
}
|
||||
|
||||
static constexpr const int bigit_bits = num_bits<bigit>();
|
||||
|
||||
friend struct formatter<bigint>;
|
||||
|
||||
FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) {
|
||||
auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
|
||||
(*this)[index] = static_cast<bigit>(result);
|
||||
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 void remove_leading_zeros() {
|
||||
int num_bigits = static_cast<int>(bigits_.size()) - 1;
|
||||
while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
|
||||
bigits_.resize(to_unsigned(num_bigits + 1));
|
||||
}
|
||||
|
||||
// Computes *this -= other assuming aligned bigints and *this >= other.
|
||||
FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) {
|
||||
FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
|
||||
FMT_ASSERT(compare(*this, other) >= 0, "");
|
||||
bigit borrow = 0;
|
||||
int i = other.exp_ - exp_;
|
||||
for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j)
|
||||
subtract_bigits(i, other.bigits_[j], borrow);
|
||||
while (borrow > 0) subtract_bigits(i, 0, borrow);
|
||||
remove_leading_zeros();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 void multiply(uint32_t value) {
|
||||
const double_bigit wide_value = value;
|
||||
bigit carry = 0;
|
||||
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
|
||||
double_bigit result = bigits_[i] * wide_value + carry;
|
||||
bigits_[i] = static_cast<bigit>(result);
|
||||
carry = static_cast<bigit>(result >> bigit_bits);
|
||||
}
|
||||
if (carry != 0) bigits_.push_back(carry);
|
||||
}
|
||||
|
||||
template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
|
||||
std::is_same<UInt, uint128_t>::value)>
|
||||
FMT_CONSTEXPR20 void multiply(UInt value) {
|
||||
using half_uint =
|
||||
conditional_t<std::is_same<UInt, uint128_t>::value, uint64_t, uint32_t>;
|
||||
const int shift = num_bits<half_uint>() - bigit_bits;
|
||||
const UInt lower = static_cast<half_uint>(value);
|
||||
const UInt upper = value >> num_bits<half_uint>();
|
||||
UInt carry = 0;
|
||||
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
|
||||
UInt result = lower * bigits_[i] + static_cast<bigit>(carry);
|
||||
carry = (upper * bigits_[i] << shift) + (result >> bigit_bits) +
|
||||
(carry >> bigit_bits);
|
||||
bigits_[i] = static_cast<bigit>(result);
|
||||
}
|
||||
while (carry != 0) {
|
||||
bigits_.push_back(static_cast<bigit>(carry));
|
||||
carry >>= bigit_bits;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
|
||||
std::is_same<UInt, uint128_t>::value)>
|
||||
FMT_CONSTEXPR20 void assign(UInt n) {
|
||||
size_t num_bigits = 0;
|
||||
do {
|
||||
bigits_[num_bigits++] = static_cast<bigit>(n);
|
||||
n >>= bigit_bits;
|
||||
} while (n != 0);
|
||||
bigits_.resize(num_bigits);
|
||||
exp_ = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR20 bigint() : exp_(0) {}
|
||||
explicit bigint(uint64_t n) { assign(n); }
|
||||
|
||||
bigint(const bigint&) = delete;
|
||||
void operator=(const bigint&) = delete;
|
||||
|
||||
FMT_CONSTEXPR20 void assign(const bigint& other) {
|
||||
auto size = other.bigits_.size();
|
||||
bigits_.resize(size);
|
||||
auto data = other.bigits_.data();
|
||||
std::copy(data, data + size, make_checked(bigits_.data(), size));
|
||||
exp_ = other.exp_;
|
||||
}
|
||||
|
||||
template <typename Int> FMT_CONSTEXPR20 void operator=(Int n) {
|
||||
FMT_ASSERT(n > 0, "");
|
||||
assign(uint64_or_128_t<Int>(n));
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 int num_bigits() const {
|
||||
return static_cast<int>(bigits_.size()) + exp_;
|
||||
}
|
||||
|
||||
FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) {
|
||||
FMT_ASSERT(shift >= 0, "");
|
||||
exp_ += shift / bigit_bits;
|
||||
shift %= bigit_bits;
|
||||
if (shift == 0) return *this;
|
||||
bigit carry = 0;
|
||||
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
|
||||
bigit c = bigits_[i] >> (bigit_bits - shift);
|
||||
bigits_[i] = (bigits_[i] << shift) + carry;
|
||||
carry = c;
|
||||
}
|
||||
if (carry != 0) bigits_.push_back(carry);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Int> FMT_CONSTEXPR20 bigint& operator*=(Int value) {
|
||||
FMT_ASSERT(value > 0, "");
|
||||
multiply(uint32_or_64_or_128_t<Int>(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) {
|
||||
int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
|
||||
if (num_lhs_bigits != num_rhs_bigits)
|
||||
return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
|
||||
int i = static_cast<int>(lhs.bigits_.size()) - 1;
|
||||
int j = static_cast<int>(rhs.bigits_.size()) - 1;
|
||||
int end = i - j;
|
||||
if (end < 0) end = 0;
|
||||
for (; i >= end; --i, --j) {
|
||||
bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
|
||||
if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
|
||||
}
|
||||
if (i != j) return i > j ? 1 : -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns compare(lhs1 + lhs2, rhs).
|
||||
friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2,
|
||||
const bigint& rhs) {
|
||||
auto minimum = [](int a, int b) { return a < b ? a : b; };
|
||||
auto maximum = [](int a, int b) { return a > b ? a : b; };
|
||||
int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits());
|
||||
int num_rhs_bigits = rhs.num_bigits();
|
||||
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
|
||||
if (max_lhs_bigits > num_rhs_bigits) return 1;
|
||||
auto get_bigit = [](const bigint& n, int i) -> bigit {
|
||||
return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
|
||||
};
|
||||
double_bigit borrow = 0;
|
||||
int min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_);
|
||||
for (int i = num_rhs_bigits - 1; i >= min_exp; --i) {
|
||||
double_bigit sum =
|
||||
static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
|
||||
bigit rhs_bigit = get_bigit(rhs, i);
|
||||
if (sum > rhs_bigit + borrow) return 1;
|
||||
borrow = rhs_bigit + borrow - sum;
|
||||
if (borrow > 1) return -1;
|
||||
borrow <<= bigit_bits;
|
||||
}
|
||||
return borrow != 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
// Assigns pow(10, exp) to this bigint.
|
||||
FMT_CONSTEXPR20 void assign_pow10(int exp) {
|
||||
FMT_ASSERT(exp >= 0, "");
|
||||
if (exp == 0) return *this = 1;
|
||||
// Find the top bit.
|
||||
int bitmask = 1;
|
||||
while (exp >= bitmask) bitmask <<= 1;
|
||||
bitmask >>= 1;
|
||||
// pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
|
||||
// repeated squaring and multiplication.
|
||||
*this = 5;
|
||||
bitmask >>= 1;
|
||||
while (bitmask != 0) {
|
||||
square();
|
||||
if ((exp & bitmask) != 0) *this *= 5;
|
||||
bitmask >>= 1;
|
||||
}
|
||||
*this <<= exp; // Multiply by pow(2, exp) by shifting.
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 void square() {
|
||||
int num_bigits = static_cast<int>(bigits_.size());
|
||||
int num_result_bigits = 2 * num_bigits;
|
||||
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
|
||||
bigits_.resize(to_unsigned(num_result_bigits));
|
||||
auto sum = uint128_t();
|
||||
for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
|
||||
// Compute bigit at position bigit_index of the result by adding
|
||||
// cross-product terms n[i] * n[j] such that i + j == bigit_index.
|
||||
for (int i = 0, j = bigit_index; j >= 0; ++i, --j) {
|
||||
// Most terms are multiplied twice which can be optimized in the future.
|
||||
sum += static_cast<double_bigit>(n[i]) * n[j];
|
||||
}
|
||||
(*this)[bigit_index] = static_cast<bigit>(sum);
|
||||
sum >>= num_bits<bigit>(); // Compute the carry.
|
||||
}
|
||||
// Do the same for the top half.
|
||||
for (int bigit_index = num_bigits; bigit_index < num_result_bigits;
|
||||
++bigit_index) {
|
||||
for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
|
||||
sum += static_cast<double_bigit>(n[i++]) * n[j--];
|
||||
(*this)[bigit_index] = static_cast<bigit>(sum);
|
||||
sum >>= num_bits<bigit>();
|
||||
}
|
||||
remove_leading_zeros();
|
||||
exp_ *= 2;
|
||||
}
|
||||
|
||||
// If this bigint has a bigger exponent than other, adds trailing zero to make
|
||||
// exponents equal. This simplifies some operations such as subtraction.
|
||||
FMT_CONSTEXPR20 void align(const bigint& other) {
|
||||
int exp_difference = exp_ - other.exp_;
|
||||
if (exp_difference <= 0) return;
|
||||
int num_bigits = static_cast<int>(bigits_.size());
|
||||
bigits_.resize(to_unsigned(num_bigits + exp_difference));
|
||||
for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
|
||||
bigits_[j] = bigits_[i];
|
||||
std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
|
||||
exp_ -= exp_difference;
|
||||
}
|
||||
|
||||
// Divides this bignum by divisor, assigning the remainder to this and
|
||||
// returning the quotient.
|
||||
FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) {
|
||||
FMT_ASSERT(this != &divisor, "");
|
||||
if (compare(*this, divisor) < 0) return 0;
|
||||
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
|
||||
align(divisor);
|
||||
int quotient = 0;
|
||||
do {
|
||||
subtract_aligned(divisor);
|
||||
++quotient;
|
||||
} while (compare(*this, divisor) >= 0);
|
||||
return quotient;
|
||||
}
|
||||
};
|
||||
|
||||
// format_dragon flags.
|
||||
enum dragon {
|
||||
predecessor_closer = 1,
|
||||
fixup = 2, // Run fixup to correct exp10 which can be off by one.
|
||||
fixed = 4,
|
||||
};
|
||||
|
||||
// Formats a floating-point number using a variation of the Fixed-Precision
|
||||
// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
|
||||
// https://fmt.dev/papers/p372-steele.pdf.
|
||||
FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
|
||||
unsigned flags, int num_digits,
|
||||
buffer<char>& buf, int& exp10) {
|
||||
bigint numerator; // 2 * R in (FPP)^2.
|
||||
bigint denominator; // 2 * S in (FPP)^2.
|
||||
// lower and upper are differences between value and corresponding boundaries.
|
||||
bigint lower; // (M^- in (FPP)^2).
|
||||
bigint upper_store; // upper's value if different from lower.
|
||||
bigint* upper = nullptr; // (M^+ in (FPP)^2).
|
||||
// Shift numerator and denominator by an extra bit or two (if lower boundary
|
||||
// is closer) to make lower and upper integers. This eliminates multiplication
|
||||
// by 2 during later computations.
|
||||
bool is_predecessor_closer = (flags & dragon::predecessor_closer) != 0;
|
||||
int shift = is_predecessor_closer ? 2 : 1;
|
||||
if (value.e >= 0) {
|
||||
numerator = value.f;
|
||||
numerator <<= value.e + shift;
|
||||
lower = 1;
|
||||
lower <<= value.e;
|
||||
if (is_predecessor_closer) {
|
||||
upper_store = 1;
|
||||
upper_store <<= value.e + 1;
|
||||
upper = &upper_store;
|
||||
}
|
||||
denominator.assign_pow10(exp10);
|
||||
denominator <<= shift;
|
||||
} else if (exp10 < 0) {
|
||||
numerator.assign_pow10(-exp10);
|
||||
lower.assign(numerator);
|
||||
if (is_predecessor_closer) {
|
||||
upper_store.assign(numerator);
|
||||
upper_store <<= 1;
|
||||
upper = &upper_store;
|
||||
}
|
||||
numerator *= value.f;
|
||||
numerator <<= shift;
|
||||
denominator = 1;
|
||||
denominator <<= shift - value.e;
|
||||
} else {
|
||||
numerator = value.f;
|
||||
numerator <<= shift;
|
||||
denominator.assign_pow10(exp10);
|
||||
denominator <<= shift - value.e;
|
||||
lower = 1;
|
||||
if (is_predecessor_closer) {
|
||||
upper_store = 1ULL << 1;
|
||||
upper = &upper_store;
|
||||
}
|
||||
}
|
||||
int even = static_cast<int>((value.f & 1) == 0);
|
||||
if (!upper) upper = &lower;
|
||||
if ((flags & dragon::fixup) != 0) {
|
||||
if (add_compare(numerator, *upper, denominator) + even <= 0) {
|
||||
--exp10;
|
||||
numerator *= 10;
|
||||
if (num_digits < 0) {
|
||||
lower *= 10;
|
||||
if (upper != &lower) *upper *= 10;
|
||||
}
|
||||
}
|
||||
if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1);
|
||||
}
|
||||
// Invariant: value == (numerator / denominator) * pow(10, exp10).
|
||||
if (num_digits < 0) {
|
||||
// Generate the shortest representation.
|
||||
num_digits = 0;
|
||||
char* data = buf.data();
|
||||
for (;;) {
|
||||
int digit = numerator.divmod_assign(denominator);
|
||||
bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower.
|
||||
// numerator + upper >[=] pow10:
|
||||
bool high = add_compare(numerator, *upper, denominator) + even > 0;
|
||||
data[num_digits++] = static_cast<char>('0' + digit);
|
||||
if (low || high) {
|
||||
if (!low) {
|
||||
++data[num_digits - 1];
|
||||
} else if (high) {
|
||||
int result = add_compare(numerator, numerator, denominator);
|
||||
// Round half to even.
|
||||
if (result > 0 || (result == 0 && (digit % 2) != 0))
|
||||
++data[num_digits - 1];
|
||||
}
|
||||
buf.try_resize(to_unsigned(num_digits));
|
||||
exp10 -= num_digits - 1;
|
||||
return;
|
||||
}
|
||||
numerator *= 10;
|
||||
lower *= 10;
|
||||
if (upper != &lower) *upper *= 10;
|
||||
}
|
||||
}
|
||||
// Generate the given number of digits.
|
||||
exp10 -= num_digits - 1;
|
||||
if (num_digits == 0) {
|
||||
denominator *= 10;
|
||||
auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
|
||||
buf.push_back(digit);
|
||||
return;
|
||||
}
|
||||
buf.try_resize(to_unsigned(num_digits));
|
||||
for (int i = 0; i < num_digits - 1; ++i) {
|
||||
int digit = numerator.divmod_assign(denominator);
|
||||
buf[i] = static_cast<char>('0' + digit);
|
||||
numerator *= 10;
|
||||
}
|
||||
int digit = numerator.divmod_assign(denominator);
|
||||
auto result = add_compare(numerator, numerator, denominator);
|
||||
if (result > 0 || (result == 0 && (digit % 2) != 0)) {
|
||||
if (digit == 9) {
|
||||
const auto overflow = '0' + 10;
|
||||
buf[num_digits - 1] = overflow;
|
||||
// Propagate the carry.
|
||||
for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) {
|
||||
buf[i] = '0';
|
||||
++buf[i - 1];
|
||||
}
|
||||
if (buf[0] == overflow) {
|
||||
buf[0] = '1';
|
||||
++exp10;
|
||||
}
|
||||
return;
|
||||
}
|
||||
++digit;
|
||||
}
|
||||
buf[num_digits - 1] = static_cast<char>('0' + digit);
|
||||
}
|
||||
|
||||
// Formats a floating-point number using the hexfloat format.
|
||||
template <typename Float, FMT_ENABLE_IF(!is_double_double<Float>::value)>
|
||||
FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
|
||||
float_specs specs, buffer<char>& buf) {
|
||||
// float is passed as double to reduce the number of instantiations and to
|
||||
// simplify implementation.
|
||||
static_assert(!std::is_same<Float, float>::value, "");
|
||||
|
||||
using info = dragonbox::float_info<Float>;
|
||||
|
||||
// Assume Float is in the format [sign][exponent][significand].
|
||||
using carrier_uint = typename info::carrier_uint;
|
||||
|
||||
constexpr auto num_float_significand_bits =
|
||||
detail::num_significand_bits<Float>();
|
||||
|
||||
basic_fp<carrier_uint> f(value);
|
||||
f.e += num_float_significand_bits;
|
||||
if (!has_implicit_bit<Float>()) --f.e;
|
||||
|
||||
constexpr auto num_fraction_bits =
|
||||
num_float_significand_bits + (has_implicit_bit<Float>() ? 1 : 0);
|
||||
constexpr auto num_xdigits = (num_fraction_bits + 3) / 4;
|
||||
|
||||
constexpr auto leading_shift = ((num_xdigits - 1) * 4);
|
||||
const auto leading_mask = carrier_uint(0xF) << leading_shift;
|
||||
const auto leading_xdigit =
|
||||
static_cast<uint32_t>((f.f & leading_mask) >> leading_shift);
|
||||
if (leading_xdigit > 1) f.e -= (32 - countl_zero(leading_xdigit) - 1);
|
||||
|
||||
int print_xdigits = num_xdigits - 1;
|
||||
if (precision >= 0 && print_xdigits > precision) {
|
||||
const int shift = ((print_xdigits - precision - 1) * 4);
|
||||
const auto mask = carrier_uint(0xF) << shift;
|
||||
const auto v = static_cast<uint32_t>((f.f & mask) >> shift);
|
||||
|
||||
if (v >= 8) {
|
||||
const auto inc = carrier_uint(1) << (shift + 4);
|
||||
f.f += inc;
|
||||
f.f &= ~(inc - 1);
|
||||
}
|
||||
|
||||
// Check long double overflow
|
||||
if (!has_implicit_bit<Float>()) {
|
||||
const auto implicit_bit = carrier_uint(1) << num_float_significand_bits;
|
||||
if ((f.f & implicit_bit) == implicit_bit) {
|
||||
f.f >>= 4;
|
||||
f.e += 4;
|
||||
}
|
||||
}
|
||||
|
||||
print_xdigits = precision;
|
||||
}
|
||||
|
||||
char xdigits[num_bits<carrier_uint>() / 4];
|
||||
detail::fill_n(xdigits, sizeof(xdigits), '0');
|
||||
format_uint<4>(xdigits, f.f, num_xdigits, specs.upper);
|
||||
|
||||
// Remove zero tail
|
||||
while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits;
|
||||
|
||||
buf.push_back('0');
|
||||
buf.push_back(specs.upper ? 'X' : 'x');
|
||||
buf.push_back(xdigits[0]);
|
||||
if (specs.showpoint || print_xdigits > 0 || print_xdigits < precision)
|
||||
buf.push_back('.');
|
||||
buf.append(xdigits + 1, xdigits + 1 + print_xdigits);
|
||||
for (; print_xdigits < precision; ++print_xdigits) buf.push_back('0');
|
||||
|
||||
buf.push_back(specs.upper ? 'P' : 'p');
|
||||
|
||||
uint32_t abs_e;
|
||||
if (f.e < 0) {
|
||||
buf.push_back('-');
|
||||
abs_e = static_cast<uint32_t>(-f.e);
|
||||
} else {
|
||||
buf.push_back('+');
|
||||
abs_e = static_cast<uint32_t>(f.e);
|
||||
}
|
||||
format_decimal<char>(appender(buf), abs_e, detail::count_digits(abs_e));
|
||||
}
|
||||
|
||||
template <typename Float, FMT_ENABLE_IF(is_double_double<Float>::value)>
|
||||
FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
|
||||
float_specs specs, buffer<char>& buf) {
|
||||
format_hexfloat(static_cast<double>(value), precision, specs, buf);
|
||||
}
|
||||
|
||||
template <typename Float>
|
||||
FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
|
||||
buffer<char>& buf) -> int {
|
||||
// float is passed as double to reduce the number of instantiations.
|
||||
static_assert(!std::is_same<Float, float>::value, "");
|
||||
FMT_ASSERT(value >= 0, "value is negative");
|
||||
auto converted_value = convert_float(value);
|
||||
|
||||
const bool fixed = specs.format == float_format::fixed;
|
||||
if (value <= 0) { // <= instead of == to silence a warning.
|
||||
if (precision <= 0 || !fixed) {
|
||||
buf.push_back('0');
|
||||
return 0;
|
||||
}
|
||||
buf.try_resize(to_unsigned(precision));
|
||||
fill_n(buf.data(), precision, '0');
|
||||
return -precision;
|
||||
}
|
||||
|
||||
int exp = 0;
|
||||
bool use_dragon = true;
|
||||
unsigned dragon_flags = 0;
|
||||
if (!is_fast_float<Float>()) {
|
||||
const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10)
|
||||
using info = dragonbox::float_info<decltype(converted_value)>;
|
||||
const auto f = basic_fp<typename info::carrier_uint>(converted_value);
|
||||
// Compute exp, an approximate power of 10, such that
|
||||
// 10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1).
|
||||
// This is based on log10(value) == log2(value) / log2(10) and approximation
|
||||
// of log2(value) by e + num_fraction_bits idea from double-conversion.
|
||||
exp = static_cast<int>(
|
||||
std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10));
|
||||
dragon_flags = dragon::fixup;
|
||||
} else if (!is_constant_evaluated() && precision < 0) {
|
||||
// Use Dragonbox for the shortest format.
|
||||
if (specs.binary32) {
|
||||
auto dec = dragonbox::to_decimal(static_cast<float>(value));
|
||||
write<char>(buffer_appender<char>(buf), dec.significand);
|
||||
return dec.exponent;
|
||||
}
|
||||
auto dec = dragonbox::to_decimal(static_cast<double>(value));
|
||||
write<char>(buffer_appender<char>(buf), dec.significand);
|
||||
return dec.exponent;
|
||||
} else if (is_constant_evaluated()) {
|
||||
// Use Grisu + Dragon4 for the given precision:
|
||||
// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
|
||||
const int min_exp = -60; // alpha in Grisu.
|
||||
int cached_exp10 = 0; // K in Grisu.
|
||||
fp normalized = normalize(fp(converted_value));
|
||||
const auto cached_pow = get_cached_power(
|
||||
min_exp - (normalized.e + fp::num_significand_bits), cached_exp10);
|
||||
normalized = normalized * cached_pow;
|
||||
gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
|
||||
if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error &&
|
||||
!is_constant_evaluated()) {
|
||||
exp += handler.exp10;
|
||||
buf.try_resize(to_unsigned(handler.size));
|
||||
use_dragon = false;
|
||||
} else {
|
||||
exp += handler.size - cached_exp10 - 1;
|
||||
precision = handler.precision;
|
||||
}
|
||||
} else {
|
||||
// Extract significand bits and exponent bits.
|
||||
using info = dragonbox::float_info<double>;
|
||||
auto br = bit_cast<uint64_t>(static_cast<double>(value));
|
||||
|
||||
const uint64_t significand_mask =
|
||||
(static_cast<uint64_t>(1) << num_significand_bits<double>()) - 1;
|
||||
uint64_t significand = (br & significand_mask);
|
||||
int exponent = static_cast<int>((br & exponent_mask<double>()) >>
|
||||
num_significand_bits<double>());
|
||||
|
||||
if (exponent != 0) { // Check if normal.
|
||||
exponent -= exponent_bias<double>() + num_significand_bits<double>();
|
||||
significand |=
|
||||
(static_cast<uint64_t>(1) << num_significand_bits<double>());
|
||||
significand <<= 1;
|
||||
} else {
|
||||
// Normalize subnormal inputs.
|
||||
FMT_ASSERT(significand != 0, "zeros should not appear hear");
|
||||
int shift = countl_zero(significand);
|
||||
FMT_ASSERT(shift >= num_bits<uint64_t>() - num_significand_bits<double>(),
|
||||
"");
|
||||
shift -= (num_bits<uint64_t>() - num_significand_bits<double>() - 2);
|
||||
exponent = (std::numeric_limits<double>::min_exponent -
|
||||
num_significand_bits<double>()) -
|
||||
shift;
|
||||
significand <<= shift;
|
||||
}
|
||||
|
||||
// Compute the first several nonzero decimal significand digits.
|
||||
// We call the number we get the first segment.
|
||||
const int k = info::kappa - dragonbox::floor_log10_pow2(exponent);
|
||||
exp = -k;
|
||||
const int beta = exponent + dragonbox::floor_log2_pow10(k);
|
||||
uint64_t first_segment;
|
||||
bool has_more_segments;
|
||||
int digits_in_the_first_segment;
|
||||
{
|
||||
const auto r = dragonbox::umul192_upper128(
|
||||
significand << beta, dragonbox::get_cached_power(k));
|
||||
first_segment = r.high();
|
||||
has_more_segments = r.low() != 0;
|
||||
|
||||
// The first segment can have 18 ~ 19 digits.
|
||||
if (first_segment >= 1000000000000000000ULL) {
|
||||
digits_in_the_first_segment = 19;
|
||||
} else {
|
||||
// When it is of 18-digits, we align it to 19-digits by adding a bogus
|
||||
// zero at the end.
|
||||
digits_in_the_first_segment = 18;
|
||||
first_segment *= 10;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the actual number of decimal digits to print.
|
||||
if (fixed) {
|
||||
adjust_precision(precision, exp + digits_in_the_first_segment);
|
||||
}
|
||||
|
||||
// Use Dragon4 only when there might be not enough digits in the first
|
||||
// segment.
|
||||
if (digits_in_the_first_segment > precision) {
|
||||
use_dragon = false;
|
||||
|
||||
if (precision <= 0) {
|
||||
exp += digits_in_the_first_segment;
|
||||
|
||||
if (precision < 0) {
|
||||
// Nothing to do, since all we have are just leading zeros.
|
||||
buf.try_resize(0);
|
||||
} else {
|
||||
// We may need to round-up.
|
||||
buf.try_resize(1);
|
||||
if ((first_segment | static_cast<uint64_t>(has_more_segments)) >
|
||||
5000000000000000000ULL) {
|
||||
buf[0] = '1';
|
||||
} else {
|
||||
buf[0] = '0';
|
||||
}
|
||||
}
|
||||
} // precision <= 0
|
||||
else {
|
||||
exp += digits_in_the_first_segment - precision;
|
||||
|
||||
// When precision > 0, we divide the first segment into three
|
||||
// subsegments, each with 9, 9, and 0 ~ 1 digits so that each fits
|
||||
// in 32-bits which usually allows faster calculation than in
|
||||
// 64-bits. Since some compiler (e.g. MSVC) doesn't know how to optimize
|
||||
// division-by-constant for large 64-bit divisors, we do it here
|
||||
// manually. The magic number 7922816251426433760 below is equal to
|
||||
// ceil(2^(64+32) / 10^10).
|
||||
const uint32_t first_subsegment = static_cast<uint32_t>(
|
||||
dragonbox::umul128_upper64(first_segment, 7922816251426433760ULL) >>
|
||||
32);
|
||||
const uint64_t second_third_subsegments =
|
||||
first_segment - first_subsegment * 10000000000ULL;
|
||||
|
||||
uint64_t prod;
|
||||
uint32_t digits;
|
||||
bool should_round_up;
|
||||
int number_of_digits_to_print = precision > 9 ? 9 : precision;
|
||||
|
||||
// Print a 9-digits subsegment, either the first or the second.
|
||||
auto print_subsegment = [&](uint32_t subsegment, char* buffer) {
|
||||
int number_of_digits_printed = 0;
|
||||
|
||||
// If we want to print an odd number of digits from the subsegment,
|
||||
if ((number_of_digits_to_print & 1) != 0) {
|
||||
// Convert to 64-bit fixed-point fractional form with 1-digit
|
||||
// integer part. The magic number 720575941 is a good enough
|
||||
// approximation of 2^(32 + 24) / 10^8; see
|
||||
// https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case
|
||||
// for details.
|
||||
prod = ((subsegment * static_cast<uint64_t>(720575941)) >> 24) + 1;
|
||||
digits = static_cast<uint32_t>(prod >> 32);
|
||||
*buffer = static_cast<char>('0' + digits);
|
||||
number_of_digits_printed++;
|
||||
}
|
||||
// If we want to print an even number of digits from the
|
||||
// first_subsegment,
|
||||
else {
|
||||
// Convert to 64-bit fixed-point fractional form with 2-digits
|
||||
// integer part. The magic number 450359963 is a good enough
|
||||
// approximation of 2^(32 + 20) / 10^7; see
|
||||
// https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case
|
||||
// for details.
|
||||
prod = ((subsegment * static_cast<uint64_t>(450359963)) >> 20) + 1;
|
||||
digits = static_cast<uint32_t>(prod >> 32);
|
||||
copy2(buffer, digits2(digits));
|
||||
number_of_digits_printed += 2;
|
||||
}
|
||||
|
||||
// Print all digit pairs.
|
||||
while (number_of_digits_printed < number_of_digits_to_print) {
|
||||
prod = static_cast<uint32_t>(prod) * static_cast<uint64_t>(100);
|
||||
digits = static_cast<uint32_t>(prod >> 32);
|
||||
copy2(buffer + number_of_digits_printed, digits2(digits));
|
||||
number_of_digits_printed += 2;
|
||||
}
|
||||
};
|
||||
|
||||
// Print first subsegment.
|
||||
print_subsegment(first_subsegment, buf.data());
|
||||
|
||||
// Perform rounding if the first subsegment is the last subsegment to
|
||||
// print.
|
||||
if (precision <= 9) {
|
||||
// Rounding inside the subsegment.
|
||||
// We round-up if:
|
||||
// - either the fractional part is strictly larger than 1/2, or
|
||||
// - the fractional part is exactly 1/2 and the last digit is odd.
|
||||
// We rely on the following observations:
|
||||
// - If fractional_part >= threshold, then the fractional part is
|
||||
// strictly larger than 1/2.
|
||||
// - If the MSB of fractional_part is set, then the fractional part
|
||||
// must be at least 1/2.
|
||||
// - When the MSB of fractional_part is set, either
|
||||
// second_third_subsegments being nonzero or has_more_segments
|
||||
// being true means there are further digits not printed, so the
|
||||
// fractional part is strictly larger than 1/2.
|
||||
if (precision < 9) {
|
||||
uint32_t fractional_part = static_cast<uint32_t>(prod);
|
||||
should_round_up = fractional_part >=
|
||||
data::fractional_part_rounding_thresholds
|
||||
[8 - number_of_digits_to_print] ||
|
||||
((fractional_part >> 31) &
|
||||
((digits & 1) | (second_third_subsegments != 0) |
|
||||
has_more_segments)) != 0;
|
||||
}
|
||||
// Rounding at the subsegment boundary.
|
||||
// In this case, the fractional part is at least 1/2 if and only if
|
||||
// second_third_subsegments >= 5000000000ULL, and is strictly larger
|
||||
// than 1/2 if we further have either second_third_subsegments >
|
||||
// 5000000000ULL or has_more_segments == true.
|
||||
else {
|
||||
should_round_up = second_third_subsegments > 5000000000ULL ||
|
||||
(second_third_subsegments == 5000000000ULL &&
|
||||
((digits & 1) != 0 || has_more_segments));
|
||||
}
|
||||
}
|
||||
// Otherwise, print the second subsegment.
|
||||
else {
|
||||
// Compilers are not aware of how to leverage the maximum value of
|
||||
// second_third_subsegments to find out a better magic number which
|
||||
// allows us to eliminate an additional shift. 1844674407370955162 =
|
||||
// ceil(2^64/10) < ceil(2^64*(10^9/(10^10 - 1))).
|
||||
const uint32_t second_subsegment =
|
||||
static_cast<uint32_t>(dragonbox::umul128_upper64(
|
||||
second_third_subsegments, 1844674407370955162ULL));
|
||||
const uint32_t third_subsegment =
|
||||
static_cast<uint32_t>(second_third_subsegments) -
|
||||
second_subsegment * 10;
|
||||
|
||||
number_of_digits_to_print = precision - 9;
|
||||
print_subsegment(second_subsegment, buf.data() + 9);
|
||||
|
||||
// Rounding inside the subsegment.
|
||||
if (precision < 18) {
|
||||
// The condition third_subsegment != 0 implies that the segment was
|
||||
// of 19 digits, so in this case the third segment should be
|
||||
// consisting of a genuine digit from the input.
|
||||
uint32_t fractional_part = static_cast<uint32_t>(prod);
|
||||
should_round_up = fractional_part >=
|
||||
data::fractional_part_rounding_thresholds
|
||||
[8 - number_of_digits_to_print] ||
|
||||
((fractional_part >> 31) &
|
||||
((digits & 1) | (third_subsegment != 0) |
|
||||
has_more_segments)) != 0;
|
||||
}
|
||||
// Rounding at the subsegment boundary.
|
||||
else {
|
||||
// In this case, the segment must be of 19 digits, thus
|
||||
// the third subsegment should be consisting of a genuine digit from
|
||||
// the input.
|
||||
should_round_up = third_subsegment > 5 ||
|
||||
(third_subsegment == 5 &&
|
||||
((digits & 1) != 0 || has_more_segments));
|
||||
}
|
||||
}
|
||||
|
||||
// Round-up if necessary.
|
||||
if (should_round_up) {
|
||||
++buf[precision - 1];
|
||||
for (int i = precision - 1; i > 0 && buf[i] > '9'; --i) {
|
||||
buf[i] = '0';
|
||||
++buf[i - 1];
|
||||
}
|
||||
if (buf[0] > '9') {
|
||||
buf[0] = '1';
|
||||
if (fixed)
|
||||
buf[precision++] = '0';
|
||||
else
|
||||
++exp;
|
||||
}
|
||||
}
|
||||
buf.try_resize(to_unsigned(precision));
|
||||
}
|
||||
} // if (digits_in_the_first_segment > precision)
|
||||
else {
|
||||
// Adjust the exponent for its use in Dragon4.
|
||||
exp += digits_in_the_first_segment - 1;
|
||||
}
|
||||
}
|
||||
if (use_dragon) {
|
||||
auto f = basic_fp<uint128_t>();
|
||||
bool is_predecessor_closer = specs.binary32
|
||||
? f.assign(static_cast<float>(value))
|
||||
: f.assign(converted_value);
|
||||
if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer;
|
||||
if (fixed) dragon_flags |= dragon::fixed;
|
||||
// Limit precision to the maximum possible number of significant digits in
|
||||
// an IEEE754 double because we don't need to generate zeros.
|
||||
const int max_double_digits = 767;
|
||||
if (precision > max_double_digits) precision = max_double_digits;
|
||||
format_dragon(f, dragon_flags, precision, buf, exp);
|
||||
}
|
||||
if (!fixed && !specs.showpoint) {
|
||||
// Remove trailing zeros.
|
||||
auto num_digits = buf.size();
|
||||
while (num_digits > 0 && buf[num_digits - 1] == '0') {
|
||||
--num_digits;
|
||||
++exp;
|
||||
}
|
||||
buf.try_resize(num_digits);
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
template <typename Char, typename OutputIt, typename T>
|
||||
FMT_CONSTEXPR20 auto write_float(OutputIt out, T value,
|
||||
format_specs<Char> specs, locale_ref loc)
|
||||
-> OutputIt {
|
||||
if (const_check(!is_supported_floating_point(value))) return out;
|
||||
float_specs fspecs = parse_float_type_spec(specs);
|
||||
fspecs.sign = specs.sign;
|
||||
if (detail::signbit(value)) { // value < 0 is false for NaN so use signbit.
|
||||
@@ -1995,7 +3829,7 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value,
|
||||
}
|
||||
|
||||
if (!detail::isfinite(value))
|
||||
return write_nonfinite(out, detail::isinf(value), specs, fspecs);
|
||||
return write_nonfinite(out, detail::isnan(value), specs, fspecs);
|
||||
|
||||
if (specs.align == align::numeric && fspecs.sign) {
|
||||
auto it = reserve(out, 1);
|
||||
@@ -2008,7 +3842,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<char>(fspecs.sign));
|
||||
snprintf_float(promote_float(value), specs.precision, fspecs, buffer);
|
||||
format_hexfloat(convert_float(value), specs.precision, fspecs, buffer);
|
||||
return write_bytes<align::right>(out, {buffer.data(), buffer.size()},
|
||||
specs);
|
||||
}
|
||||
@@ -2020,53 +3854,59 @@ 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<T, float>())) fspecs.binary32 = true;
|
||||
if (!is_fast_float<T>()) fspecs.fallback = true;
|
||||
int exp = format_float(promote_float(value), precision, fspecs, buffer);
|
||||
int exp = format_float(convert_float(value), precision, fspecs, buffer);
|
||||
fspecs.precision = precision;
|
||||
auto fp = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
|
||||
return write_float(out, fp, specs, fspecs, loc);
|
||||
auto f = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
|
||||
return write_float(out, f, specs, fspecs, loc);
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(is_floating_point<T>::value)>
|
||||
FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs<Char> specs,
|
||||
locale_ref loc = {}) -> OutputIt {
|
||||
if (const_check(!is_supported_floating_point(value))) return out;
|
||||
return specs.localized && write_loc(out, value, specs, loc)
|
||||
? out
|
||||
: write_float(out, value, specs, loc);
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(is_fast_float<T>::value)>
|
||||
FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
|
||||
if (is_constant_evaluated()) {
|
||||
return write(out, value, basic_format_specs<Char>());
|
||||
}
|
||||
|
||||
if (is_constant_evaluated()) return write(out, value, format_specs<Char>());
|
||||
if (const_check(!is_supported_floating_point(value))) return out;
|
||||
|
||||
using floaty = conditional_t<std::is_same<T, long double>::value, double, T>;
|
||||
using uint = typename dragonbox::float_info<floaty>::carrier_uint;
|
||||
auto bits = bit_cast<uint>(value);
|
||||
|
||||
auto fspecs = float_specs();
|
||||
if (detail::signbit(value)) {
|
||||
fspecs.sign = sign::minus;
|
||||
value = -value;
|
||||
}
|
||||
|
||||
constexpr auto specs = basic_format_specs<Char>();
|
||||
uint mask = exponent_mask<floaty>();
|
||||
if ((bits & mask) == mask)
|
||||
return write_nonfinite(out, std::isinf(value), specs, fspecs);
|
||||
constexpr auto specs = format_specs<Char>();
|
||||
using floaty = conditional_t<std::is_same<T, long double>::value, double, T>;
|
||||
using floaty_uint = typename dragonbox::float_info<floaty>::carrier_uint;
|
||||
floaty_uint mask = exponent_mask<floaty>();
|
||||
if ((bit_cast<floaty_uint>(value) & mask) == mask)
|
||||
return write_nonfinite(out, std::isnan(value), specs, fspecs);
|
||||
|
||||
auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
|
||||
return write_float(out, dec, specs, fspecs, {});
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(std::is_floating_point<T>::value &&
|
||||
FMT_ENABLE_IF(is_floating_point<T>::value &&
|
||||
!is_fast_float<T>::value)>
|
||||
inline auto write(OutputIt out, T value) -> OutputIt {
|
||||
return write(out, value, basic_format_specs<Char>());
|
||||
return write(out, value, format_specs<Char>());
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
auto write(OutputIt out, monostate, basic_format_specs<Char> = {},
|
||||
locale_ref = {}) -> OutputIt {
|
||||
auto write(OutputIt out, monostate, format_specs<Char> = {}, locale_ref = {})
|
||||
-> OutputIt {
|
||||
FMT_ASSERT(false, "");
|
||||
return out;
|
||||
}
|
||||
@@ -2085,28 +3925,6 @@ constexpr auto write(OutputIt out, const T& value) -> OutputIt {
|
||||
return write<Char>(out, to_string_view(value));
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(is_integral<T>::value &&
|
||||
!std::is_same<T, bool>::value &&
|
||||
!std::is_same<T, Char>::value)>
|
||||
FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
|
||||
auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
|
||||
bool negative = is_negative(value);
|
||||
// Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
|
||||
if (negative) abs_value = ~abs_value + 1;
|
||||
int num_digits = count_digits(abs_value);
|
||||
auto size = (negative ? 1 : 0) + static_cast<size_t>(num_digits);
|
||||
auto it = reserve(out, size);
|
||||
if (auto ptr = to_pointer<Char>(it, size)) {
|
||||
if (negative) *ptr++ = static_cast<Char>('-');
|
||||
format_decimal<Char>(ptr, abs_value, num_digits);
|
||||
return out;
|
||||
}
|
||||
if (negative) *it++ = static_cast<Char>('-');
|
||||
it = format_decimal<Char>(it, abs_value, num_digits).end;
|
||||
return base_iterator(out, it);
|
||||
}
|
||||
|
||||
// FMT_ENABLE_IF() condition separated to workaround an MSVC bug.
|
||||
template <
|
||||
typename Char, typename OutputIt, typename T,
|
||||
@@ -2116,15 +3934,14 @@ template <
|
||||
type::custom_type,
|
||||
FMT_ENABLE_IF(check)>
|
||||
FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
|
||||
return write<Char>(
|
||||
out, static_cast<typename std::underlying_type<T>::type>(value));
|
||||
return write<Char>(out, static_cast<underlying_t<T>>(value));
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(std::is_same<T, bool>::value)>
|
||||
FMT_CONSTEXPR auto write(OutputIt out, T value,
|
||||
const basic_format_specs<Char>& specs = {},
|
||||
locale_ref = {}) -> OutputIt {
|
||||
const format_specs<Char>& specs = {}, locale_ref = {})
|
||||
-> OutputIt {
|
||||
return specs.type != presentation_type::none &&
|
||||
specs.type != presentation_type::string
|
||||
? write(out, value ? 1 : 0, specs, {})
|
||||
@@ -2141,21 +3958,16 @@ FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt {
|
||||
template <typename Char, typename OutputIt>
|
||||
FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value)
|
||||
-> OutputIt {
|
||||
if (!value) {
|
||||
throw_format_error("string pointer is null");
|
||||
} else {
|
||||
out = write(out, basic_string_view<Char>(value));
|
||||
}
|
||||
if (value) return write(out, basic_string_view<Char>(value));
|
||||
throw_format_error("string pointer is null");
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(std::is_same<T, void>::value)>
|
||||
auto write(OutputIt out, const T* value,
|
||||
const basic_format_specs<Char>& specs = {}, locale_ref = {})
|
||||
-> OutputIt {
|
||||
check_pointer_type_spec(specs.type, error_handler());
|
||||
return write_ptr<Char>(out, to_uintptr(value), &specs);
|
||||
auto write(OutputIt out, const T* value, const format_specs<Char>& specs = {},
|
||||
locale_ref = {}) -> OutputIt {
|
||||
return write_ptr<Char>(out, bit_cast<uintptr_t>(value), &specs);
|
||||
}
|
||||
|
||||
// A write overload that handles implicit conversions.
|
||||
@@ -2163,9 +3975,9 @@ template <typename Char, typename OutputIt, typename T,
|
||||
typename Context = basic_format_context<OutputIt, Char>>
|
||||
FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t<
|
||||
std::is_class<T>::value && !is_string<T>::value &&
|
||||
!std::is_same<T, Char>::value &&
|
||||
!std::is_same<const T&,
|
||||
decltype(arg_mapper<Context>().map(value))>::value,
|
||||
!is_floating_point<T>::value && !std::is_same<T, Char>::value &&
|
||||
!std::is_same<T, remove_cvref_t<decltype(arg_mapper<Context>().map(
|
||||
value))>>::value,
|
||||
OutputIt> {
|
||||
return write<Char>(out, arg_mapper<Context>().map(value));
|
||||
}
|
||||
@@ -2175,12 +3987,8 @@ template <typename Char, typename OutputIt, typename T,
|
||||
FMT_CONSTEXPR auto write(OutputIt out, const T& value)
|
||||
-> enable_if_t<mapped_type_constant<T, Context>::value == type::custom_type,
|
||||
OutputIt> {
|
||||
using formatter_type =
|
||||
conditional_t<has_formatter<T, Context>::value,
|
||||
typename Context::template formatter_type<T>,
|
||||
fallback_formatter<T, Char>>;
|
||||
auto ctx = Context(out, {}, {});
|
||||
return formatter_type().format(value, ctx);
|
||||
return typename Context::template formatter_type<T>().format(value, ctx);
|
||||
}
|
||||
|
||||
// An argument visitor that formats the argument and writes it via the output
|
||||
@@ -2209,7 +4017,7 @@ template <typename Char> struct arg_formatter {
|
||||
using context = buffer_context<Char>;
|
||||
|
||||
iterator out;
|
||||
const basic_format_specs<Char>& specs;
|
||||
const format_specs<Char>& specs;
|
||||
locale_ref locale;
|
||||
|
||||
template <typename T>
|
||||
@@ -2234,12 +4042,6 @@ template <typename Char> struct custom_formatter {
|
||||
template <typename T> void operator()(T) const {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using is_integer =
|
||||
bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
|
||||
!std::is_same<T, char>::value &&
|
||||
!std::is_same<T, wchar_t>::value>;
|
||||
|
||||
template <typename ErrorHandler> class width_checker {
|
||||
public:
|
||||
explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
|
||||
@@ -2296,48 +4098,6 @@ FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) ->
|
||||
return arg;
|
||||
}
|
||||
|
||||
// The standard format specifier handler with checking.
|
||||
template <typename Char> class specs_handler : public specs_setter<Char> {
|
||||
private:
|
||||
basic_format_parse_context<Char>& parse_context_;
|
||||
buffer_context<Char>& context_;
|
||||
|
||||
// This is only needed for compatibility with gcc 4.4.
|
||||
using format_arg = basic_format_arg<buffer_context<Char>>;
|
||||
|
||||
FMT_CONSTEXPR auto get_arg(auto_id) -> format_arg {
|
||||
return detail::get_arg(context_, parse_context_.next_arg_id());
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR auto get_arg(int arg_id) -> format_arg {
|
||||
parse_context_.check_arg_id(arg_id);
|
||||
return detail::get_arg(context_, arg_id);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR auto get_arg(basic_string_view<Char> arg_id) -> format_arg {
|
||||
parse_context_.check_arg_id(arg_id);
|
||||
return detail::get_arg(context_, arg_id);
|
||||
}
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR specs_handler(basic_format_specs<Char>& specs,
|
||||
basic_format_parse_context<Char>& parse_ctx,
|
||||
buffer_context<Char>& ctx)
|
||||
: specs_setter<Char>(specs), parse_context_(parse_ctx), context_(ctx) {}
|
||||
|
||||
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
|
||||
this->specs_.width = get_dynamic_spec<width_checker>(
|
||||
get_arg(arg_id), context_.error_handler());
|
||||
}
|
||||
|
||||
template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
|
||||
this->specs_.precision = get_dynamic_spec<precision_checker>(
|
||||
get_arg(arg_id), context_.error_handler());
|
||||
}
|
||||
|
||||
void on_error(const char* message) { context_.on_error(message); }
|
||||
};
|
||||
|
||||
template <template <typename> class Handler, typename Context>
|
||||
FMT_CONSTEXPR void handle_dynamic_spec(int& value,
|
||||
arg_ref<typename Context::char_type> ref,
|
||||
@@ -2346,53 +4106,27 @@ FMT_CONSTEXPR void handle_dynamic_spec(int& value,
|
||||
case arg_id_kind::none:
|
||||
break;
|
||||
case arg_id_kind::index:
|
||||
value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
|
||||
value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.index),
|
||||
ctx.error_handler());
|
||||
break;
|
||||
case arg_id_kind::name:
|
||||
value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
|
||||
value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.name),
|
||||
ctx.error_handler());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#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<decltype(s[0])>; \
|
||||
FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \
|
||||
operator fmt::basic_string_view<char_type>() const { \
|
||||
return fmt::detail_exported::compile_string_to_view<char_type>(s); \
|
||||
} \
|
||||
}; \
|
||||
return FMT_COMPILE_STRING(); \
|
||||
}()
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a compile-time format string from a string literal *s*.
|
||||
|
||||
**Example**::
|
||||
|
||||
// A compile-time error because 'd' is an invalid specifier for strings.
|
||||
std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
|
||||
\endrst
|
||||
*/
|
||||
#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string, )
|
||||
|
||||
#if FMT_USE_USER_DEFINED_LITERALS
|
||||
template <typename Char> struct udl_formatter {
|
||||
basic_string_view<Char> str;
|
||||
|
||||
template <typename... T>
|
||||
auto operator()(T&&... args) const -> std::basic_string<Char> {
|
||||
return vformat(str, fmt::make_args_checked<T...>(str, args...));
|
||||
return vformat(str, fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
};
|
||||
|
||||
# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
template <typename T, typename Char, size_t N,
|
||||
fmt::detail_exported::fixed_string<Char, N> Str>
|
||||
struct statically_named_arg : view {
|
||||
@@ -2430,21 +4164,21 @@ template <typename Char> struct udl_arg {
|
||||
#endif // FMT_USE_USER_DEFINED_LITERALS
|
||||
|
||||
template <typename Locale, typename Char>
|
||||
auto vformat(const Locale& loc, basic_string_view<Char> format_str,
|
||||
auto vformat(const Locale& loc, basic_string_view<Char> fmt,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> std::basic_string<Char> {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
|
||||
return {buffer.data(), buffer.size()};
|
||||
auto buf = basic_memory_buffer<Char>();
|
||||
detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
|
||||
return {buf.data(), buf.size()};
|
||||
}
|
||||
|
||||
using format_func = void (*)(detail::buffer<char>&, int, const char*);
|
||||
|
||||
FMT_API void format_error_code(buffer<char>& out, int error_code,
|
||||
string_view message) FMT_NOEXCEPT;
|
||||
string_view message) noexcept;
|
||||
|
||||
FMT_API void report_error(format_func func, int error_code,
|
||||
const char* message) FMT_NOEXCEPT;
|
||||
const char* message) noexcept;
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
FMT_API auto vsystem_error(int error_code, string_view format_str,
|
||||
@@ -2490,12 +4224,11 @@ auto system_error(int error_code, format_string<T...> fmt, T&&... args)
|
||||
\endrst
|
||||
*/
|
||||
FMT_API void format_system_error(detail::buffer<char>& out, int error_code,
|
||||
const char* message) FMT_NOEXCEPT;
|
||||
const char* message) noexcept;
|
||||
|
||||
// Reports a system error without throwing an exception.
|
||||
// Can be used to report errors from destructors.
|
||||
FMT_API void report_system_error(int error_code,
|
||||
const char* message) FMT_NOEXCEPT;
|
||||
FMT_API void report_system_error(int error_code, const char* message) noexcept;
|
||||
|
||||
/** Fast integer formatter. */
|
||||
class format_int {
|
||||
@@ -2558,46 +4291,16 @@ class format_int {
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
template <typename FormatContext>
|
||||
FMT_CONSTEXPR FMT_INLINE auto
|
||||
formatter<T, Char,
|
||||
enable_if_t<detail::type_constant<T, Char>::value !=
|
||||
detail::type::custom_type>>::format(const T& val,
|
||||
FormatContext& ctx)
|
||||
const -> decltype(ctx.out()) {
|
||||
if (specs_.width_ref.kind != detail::arg_id_kind::none ||
|
||||
specs_.precision_ref.kind != detail::arg_id_kind::none) {
|
||||
auto specs = specs_;
|
||||
detail::handle_dynamic_spec<detail::width_checker>(specs.width,
|
||||
specs.width_ref, ctx);
|
||||
detail::handle_dynamic_spec<detail::precision_checker>(
|
||||
specs.precision, specs.precision_ref, ctx);
|
||||
return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
|
||||
}
|
||||
return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
|
||||
}
|
||||
struct formatter<T, Char, enable_if_t<detail::has_format_as<T>::value>>
|
||||
: private formatter<detail::format_as_t<T>> {
|
||||
using base = formatter<detail::format_as_t<T>>;
|
||||
using base::parse;
|
||||
|
||||
#define FMT_FORMAT_AS(Type, Base) \
|
||||
template <typename Char> \
|
||||
struct formatter<Type, Char> : formatter<Base, Char> { \
|
||||
template <typename FormatContext> \
|
||||
auto format(Type const& val, FormatContext& ctx) const \
|
||||
-> decltype(ctx.out()) { \
|
||||
return formatter<Base, Char>::format(static_cast<Base>(val), ctx); \
|
||||
} \
|
||||
template <typename FormatContext>
|
||||
auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||
return base::format(format_as(value), ctx);
|
||||
}
|
||||
|
||||
FMT_FORMAT_AS(signed char, int);
|
||||
FMT_FORMAT_AS(unsigned char, unsigned);
|
||||
FMT_FORMAT_AS(short, int);
|
||||
FMT_FORMAT_AS(unsigned short, unsigned);
|
||||
FMT_FORMAT_AS(long, long long);
|
||||
FMT_FORMAT_AS(unsigned long, unsigned long long);
|
||||
FMT_FORMAT_AS(Char*, const Char*);
|
||||
FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
|
||||
FMT_FORMAT_AS(std::nullptr_t, const void*);
|
||||
FMT_FORMAT_AS(detail::byte, unsigned char);
|
||||
FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct formatter<void*, Char> : formatter<const void*, Char> {
|
||||
@@ -2616,58 +4319,6 @@ struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {
|
||||
}
|
||||
};
|
||||
|
||||
// A formatter for types known only at run time such as variant alternatives.
|
||||
//
|
||||
// Usage:
|
||||
// using variant = std::variant<int, std::string>;
|
||||
// template <>
|
||||
// struct formatter<variant>: dynamic_formatter<> {
|
||||
// auto format(const variant& v, format_context& ctx) {
|
||||
// return visit([&](const auto& val) {
|
||||
// return dynamic_formatter<>::format(val, ctx);
|
||||
// }, v);
|
||||
// }
|
||||
// };
|
||||
template <typename Char = char> class dynamic_formatter {
|
||||
private:
|
||||
detail::dynamic_format_specs<Char> specs_;
|
||||
const Char* format_str_;
|
||||
|
||||
struct null_handler : detail::error_handler {
|
||||
void on_align(align_t) {}
|
||||
void on_sign(sign_t) {}
|
||||
void on_hash() {}
|
||||
};
|
||||
|
||||
template <typename Context> void handle_specs(Context& ctx) {
|
||||
detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
|
||||
specs_.width_ref, ctx);
|
||||
detail::handle_dynamic_spec<detail::precision_checker>(
|
||||
specs_.precision, specs_.precision_ref, ctx);
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
format_str_ = ctx.begin();
|
||||
// Checks are deferred to formatting time when the argument type is known.
|
||||
detail::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
|
||||
return detail::parse_format_specs(ctx.begin(), ctx.end(), handler);
|
||||
}
|
||||
|
||||
template <typename T, typename FormatContext>
|
||||
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
handle_specs(ctx);
|
||||
detail::specs_checker<null_handler> checker(
|
||||
null_handler(), detail::mapped_type_constant<T, FormatContext>::value);
|
||||
checker.on_align(specs_.align);
|
||||
if (specs_.sign != sign::none) checker.on_sign(specs_.sign);
|
||||
if (specs_.alt) checker.on_hash();
|
||||
if (specs_.precision >= 0) checker.end_precision();
|
||||
return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Converts ``p`` to ``const void*`` for pointer formatting.
|
||||
@@ -2681,13 +4332,36 @@ template <typename T> auto ptr(T p) -> const void* {
|
||||
static_assert(std::is_pointer<T>::value, "");
|
||||
return detail::bit_cast<const void*>(p);
|
||||
}
|
||||
template <typename T> auto ptr(const std::unique_ptr<T>& p) -> const void* {
|
||||
template <typename T, typename Deleter>
|
||||
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
|
||||
return p.get();
|
||||
}
|
||||
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
|
||||
return p.get();
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Converts ``e`` to the underlying type.
|
||||
|
||||
**Example**::
|
||||
|
||||
enum class color { red, green, blue };
|
||||
auto s = fmt::format("{}", fmt::underlying(color::red));
|
||||
\endrst
|
||||
*/
|
||||
template <typename Enum>
|
||||
constexpr auto underlying(Enum e) noexcept -> underlying_t<Enum> {
|
||||
return static_cast<underlying_t<Enum>>(e);
|
||||
}
|
||||
|
||||
namespace enums {
|
||||
template <typename Enum, FMT_ENABLE_IF(std::is_enum<Enum>::value)>
|
||||
constexpr auto format_as(Enum e) noexcept -> underlying_t<Enum> {
|
||||
return static_cast<underlying_t<Enum>>(e);
|
||||
}
|
||||
} // namespace enums
|
||||
|
||||
class bytes {
|
||||
private:
|
||||
string_view data_;
|
||||
@@ -2699,17 +4373,13 @@ class bytes {
|
||||
|
||||
template <> struct formatter<bytes> {
|
||||
private:
|
||||
detail::dynamic_format_specs<char> specs_;
|
||||
detail::dynamic_format_specs<> specs_;
|
||||
|
||||
public:
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
using handler_type = detail::dynamic_specs_handler<ParseContext>;
|
||||
detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
|
||||
detail::type::string_type);
|
||||
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
|
||||
detail::check_string_type_spec(specs_.type, ctx.error_handler());
|
||||
return it;
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* {
|
||||
return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
|
||||
detail::type::string_type);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
@@ -2742,17 +4412,13 @@ template <typename T> auto group_digits(T value) -> group_digits_view<T> {
|
||||
|
||||
template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
|
||||
private:
|
||||
detail::dynamic_format_specs<char> specs_;
|
||||
detail::dynamic_format_specs<> specs_;
|
||||
|
||||
public:
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
using handler_type = detail::dynamic_specs_handler<ParseContext>;
|
||||
detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
|
||||
detail::type::int_type);
|
||||
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
|
||||
detail::check_string_type_spec(specs_.type, ctx.error_handler());
|
||||
return it;
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* {
|
||||
return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
|
||||
detail::type::int_type);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
@@ -2762,12 +4428,13 @@ template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
|
||||
specs_.width_ref, ctx);
|
||||
detail::handle_dynamic_spec<detail::precision_checker>(
|
||||
specs_.precision, specs_.precision_ref, ctx);
|
||||
return detail::write_int_localized(
|
||||
return detail::write_int(
|
||||
ctx.out(), static_cast<detail::uint64_or_128_t<T>>(t.value), 0, specs_,
|
||||
detail::digit_grouping<char>({"\3", ','}));
|
||||
detail::digit_grouping<char>("\3", ","));
|
||||
}
|
||||
};
|
||||
|
||||
// DEPRECATED! join_view will be moved to ranges.h.
|
||||
template <typename It, typename Sentinel, typename Char = char>
|
||||
struct join_view : detail::view {
|
||||
It begin;
|
||||
@@ -2778,9 +4445,6 @@ struct join_view : detail::view {
|
||||
: begin(b), end(e), sep(s) {}
|
||||
};
|
||||
|
||||
template <typename It, typename Sentinel, typename Char>
|
||||
using arg_join FMT_DEPRECATED_ALIAS = join_view<It, Sentinel, Char>;
|
||||
|
||||
template <typename It, typename Sentinel, typename Char>
|
||||
struct formatter<join_view<It, Sentinel, Char>, Char> {
|
||||
private:
|
||||
@@ -2790,45 +4454,26 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
|
||||
#else
|
||||
typename std::iterator_traits<It>::value_type;
|
||||
#endif
|
||||
using context = buffer_context<Char>;
|
||||
using mapper = detail::arg_mapper<context>;
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(has_formatter<T, context>::value)>
|
||||
static auto map(const T& value) -> const T& {
|
||||
return value;
|
||||
}
|
||||
template <typename T, FMT_ENABLE_IF(!has_formatter<T, context>::value)>
|
||||
static auto map(const T& value) -> decltype(mapper().map(value)) {
|
||||
return mapper().map(value);
|
||||
}
|
||||
|
||||
using formatter_type =
|
||||
conditional_t<is_formattable<value_type, Char>::value,
|
||||
formatter<remove_cvref_t<decltype(map(
|
||||
std::declval<const value_type&>()))>,
|
||||
Char>,
|
||||
detail::fallback_formatter<value_type, Char>>;
|
||||
|
||||
formatter_type value_formatter_;
|
||||
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
|
||||
|
||||
public:
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
|
||||
return value_formatter_.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const join_view<It, Sentinel, Char>& value, FormatContext& ctx)
|
||||
-> decltype(ctx.out()) {
|
||||
auto format(const join_view<It, Sentinel, Char>& value,
|
||||
FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||
auto it = value.begin;
|
||||
auto out = ctx.out();
|
||||
if (it != value.end) {
|
||||
out = value_formatter_.format(map(*it), ctx);
|
||||
out = value_formatter_.format(*it, ctx);
|
||||
++it;
|
||||
while (it != value.end) {
|
||||
out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
|
||||
ctx.advance_to(out);
|
||||
out = value_formatter_.format(map(*it), ctx);
|
||||
out = value_formatter_.format(*it, ctx);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
@@ -2880,9 +4525,9 @@ auto join(Range&& range, string_view sep)
|
||||
*/
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
inline auto to_string(const T& value) -> std::string {
|
||||
auto result = std::string();
|
||||
detail::write<char>(std::back_inserter(result), value);
|
||||
return result;
|
||||
auto buffer = memory_buffer();
|
||||
detail::write<char>(appender(buffer), value);
|
||||
return {buffer.data(), buffer.size()};
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
@@ -2906,24 +4551,8 @@ FMT_NODISCARD auto to_string(const basic_memory_buffer<Char, SIZE>& buf)
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
template <typename Char>
|
||||
void vformat_to(
|
||||
buffer<Char>& buf, basic_string_view<Char> fmt,
|
||||
basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
|
||||
locale_ref loc) {
|
||||
// workaround for msvc bug regarding name-lookup in module
|
||||
// link names into function scope
|
||||
using detail::arg_formatter;
|
||||
using detail::buffer_appender;
|
||||
using detail::custom_formatter;
|
||||
using detail::default_arg_formatter;
|
||||
using detail::get_arg;
|
||||
using detail::locale_ref;
|
||||
using detail::parse_format_specs;
|
||||
using detail::specs_checker;
|
||||
using detail::specs_handler;
|
||||
using detail::to_unsigned;
|
||||
using detail::type;
|
||||
using detail::write;
|
||||
void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
|
||||
typename vformat_args<Char>::type args, locale_ref loc) {
|
||||
auto out = buffer_appender<Char>(buf);
|
||||
if (fmt.size() == 2 && equal2(fmt.data(), "{}")) {
|
||||
auto arg = args.get(0);
|
||||
@@ -2936,9 +4565,10 @@ void vformat_to(
|
||||
basic_format_parse_context<Char> parse_context;
|
||||
buffer_context<Char> context;
|
||||
|
||||
format_handler(buffer_appender<Char> out, basic_string_view<Char> str,
|
||||
basic_format_args<buffer_context<Char>> args, locale_ref loc)
|
||||
: parse_context(str), context(out, args, loc) {}
|
||||
format_handler(buffer_appender<Char> p_out, basic_string_view<Char> str,
|
||||
basic_format_args<buffer_context<Char>> p_args,
|
||||
locale_ref p_loc)
|
||||
: parse_context(str), context(p_out, p_args, p_loc) {}
|
||||
|
||||
void on_text(const Char* begin, const Char* end) {
|
||||
auto text = basic_string_view<Char>(begin, to_unsigned(end - begin));
|
||||
@@ -2969,15 +4599,16 @@ void vformat_to(
|
||||
-> const Char* {
|
||||
auto arg = get_arg(context, id);
|
||||
if (arg.type() == type::custom_type) {
|
||||
parse_context.advance_to(parse_context.begin() +
|
||||
(begin - &*parse_context.begin()));
|
||||
parse_context.advance_to(begin);
|
||||
visit_format_arg(custom_formatter<Char>{parse_context, context}, arg);
|
||||
return parse_context.begin();
|
||||
}
|
||||
auto specs = basic_format_specs<Char>();
|
||||
specs_checker<specs_handler<Char>> handler(
|
||||
specs_handler<Char>(specs, parse_context, context), arg.type());
|
||||
begin = parse_format_specs(begin, end, handler);
|
||||
auto specs = detail::dynamic_format_specs<Char>();
|
||||
begin = parse_format_specs(begin, end, specs, parse_context, arg.type());
|
||||
detail::handle_dynamic_spec<detail::width_checker>(
|
||||
specs.width, specs.width_ref, context);
|
||||
detail::handle_dynamic_spec<detail::precision_checker>(
|
||||
specs.precision, specs.precision_ref, context);
|
||||
if (begin == end || *begin != '}')
|
||||
on_error("missing '}' in format string");
|
||||
auto f = arg_formatter<Char>{context.out(), specs, context.locale()};
|
||||
@@ -2989,26 +4620,15 @@ void vformat_to(
|
||||
}
|
||||
|
||||
#ifndef FMT_HEADER_ONLY
|
||||
extern template FMT_API void vformat_to(buffer<char>&, string_view,
|
||||
typename vformat_args<>::type,
|
||||
locale_ref);
|
||||
extern template FMT_API auto thousands_sep_impl<char>(locale_ref)
|
||||
-> thousands_sep_result<char>;
|
||||
extern template FMT_API auto thousands_sep_impl<wchar_t>(locale_ref)
|
||||
-> thousands_sep_result<wchar_t>;
|
||||
extern template FMT_API auto decimal_point_impl(locale_ref) -> char;
|
||||
extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
|
||||
extern template auto format_float<double>(double value, int precision,
|
||||
float_specs specs, buffer<char>& buf)
|
||||
-> int;
|
||||
extern template auto format_float<long double>(long double value, int precision,
|
||||
float_specs specs,
|
||||
buffer<char>& buf) -> int;
|
||||
void snprintf_float(float, int, float_specs, buffer<char>&) = delete;
|
||||
extern template auto snprintf_float<double>(double value, int precision,
|
||||
float_specs specs,
|
||||
buffer<char>& buf) -> int;
|
||||
extern template auto snprintf_float<long double>(long double value,
|
||||
int precision,
|
||||
float_specs specs,
|
||||
buffer<char>& buf) -> int;
|
||||
#endif // FMT_HEADER_ONLY
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
@@ -3025,25 +4645,16 @@ inline namespace literals {
|
||||
fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
|
||||
\endrst
|
||||
*/
|
||||
# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
template <detail_exported::fixed_string Str>
|
||||
constexpr auto operator""_a()
|
||||
-> detail::udl_arg<remove_cvref_t<decltype(Str.data[0])>,
|
||||
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> {
|
||||
return {};
|
||||
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
template <detail_exported::fixed_string Str> constexpr auto operator""_a() {
|
||||
using char_t = remove_cvref_t<decltype(Str.data[0])>;
|
||||
return detail::udl_arg<char_t, sizeof(Str.data) / sizeof(char_t), Str>();
|
||||
}
|
||||
# else
|
||||
constexpr auto operator"" _a(const char* s, size_t) -> detail::udl_arg<char> {
|
||||
return {s};
|
||||
}
|
||||
# endif
|
||||
|
||||
// DEPRECATED!
|
||||
// User-defined literal equivalent of fmt::format.
|
||||
FMT_DEPRECATED constexpr auto operator"" _format(const char* s, size_t n)
|
||||
-> detail::udl_formatter<char> {
|
||||
return {{s, n}};
|
||||
}
|
||||
} // namespace literals
|
||||
#endif // FMT_USE_USER_DEFINED_LITERALS
|
||||
|
||||
@@ -3057,15 +4668,7 @@ template <typename Locale, typename... T,
|
||||
FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
|
||||
inline auto format(const Locale& loc, format_string<T...> fmt, T&&... args)
|
||||
-> std::string {
|
||||
return vformat(loc, string_view(fmt), fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
template <typename... T, size_t SIZE, typename Allocator>
|
||||
FMT_DEPRECATED auto format_to(basic_memory_buffer<char, SIZE, Allocator>& buf,
|
||||
format_string<T...> fmt, T&&... args)
|
||||
-> appender {
|
||||
detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...));
|
||||
return appender(buf);
|
||||
return fmt::vformat(loc, string_view(fmt), fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Locale,
|
||||
@@ -3076,7 +4679,7 @@ auto vformat_to(OutputIt out, const Locale& loc, string_view fmt,
|
||||
using detail::get_buffer;
|
||||
auto&& buf = get_buffer<char>(out);
|
||||
detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
|
||||
return detail::get_iterator(buf);
|
||||
return detail::get_iterator(buf, out);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Locale, typename... T,
|
||||
@@ -3087,12 +4690,40 @@ FMT_INLINE auto format_to(OutputIt out, const Locale& loc,
|
||||
return vformat_to(out, loc, fmt, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
template <typename Locale, typename... T,
|
||||
FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
|
||||
FMT_NODISCARD FMT_INLINE auto formatted_size(const Locale& loc,
|
||||
format_string<T...> fmt,
|
||||
T&&... args) -> size_t {
|
||||
auto buf = detail::counting_buffer<>();
|
||||
detail::vformat_to<char>(buf, fmt, fmt::make_format_args(args...),
|
||||
detail::locale_ref(loc));
|
||||
return buf.count();
|
||||
}
|
||||
|
||||
#ifdef FMT_DEPRECATED_INCLUDE_XCHAR
|
||||
# include "xchar.h"
|
||||
#endif
|
||||
FMT_END_EXPORT
|
||||
|
||||
template <typename T, typename Char>
|
||||
template <typename FormatContext>
|
||||
FMT_CONSTEXPR FMT_INLINE auto
|
||||
formatter<T, Char,
|
||||
enable_if_t<detail::type_constant<T, Char>::value !=
|
||||
detail::type::custom_type>>::format(const T& val,
|
||||
FormatContext& ctx)
|
||||
const -> decltype(ctx.out()) {
|
||||
if (specs_.width_ref.kind != detail::arg_id_kind::none ||
|
||||
specs_.precision_ref.kind != detail::arg_id_kind::none) {
|
||||
auto specs = specs_;
|
||||
detail::handle_dynamic_spec<detail::width_checker>(specs.width,
|
||||
specs.width_ref, ctx);
|
||||
detail::handle_dynamic_spec<detail::precision_checker>(
|
||||
specs.precision, specs.precision_ref, ctx);
|
||||
return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
|
||||
}
|
||||
return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#ifdef FMT_HEADER_ONLY
|
||||
# define FMT_FUNC inline
|
||||
|
||||
@@ -404,8 +404,9 @@ std::string AssetCatalogDropTarget::drop_tooltip_asset_catalog(const wmDrag &dra
|
||||
BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG);
|
||||
const AssetCatalog *src_catalog = get_drag_catalog(drag, get_asset_library());
|
||||
|
||||
return fmt::format(
|
||||
TIP_("Move catalog {} into {}"), src_catalog->path.name(), catalog_item_.get_name());
|
||||
return fmt::format(TIP_("Move catalog {} into {}"),
|
||||
(std::string_view)src_catalog->path.name(),
|
||||
(std::string_view)catalog_item_.get_name());
|
||||
}
|
||||
|
||||
std::string AssetCatalogDropTarget::drop_tooltip_asset_list(const wmDrag &drag) const
|
||||
@@ -619,7 +620,7 @@ std::string AssetCatalogTreeViewAllItem::DropTarget::drop_tooltip(const wmDrag &
|
||||
drag, *get_view<AssetCatalogTreeView>().asset_library_);
|
||||
|
||||
return fmt::format(TIP_("Move catalog {} to the top level of the tree"),
|
||||
drag_catalog->path.name());
|
||||
(std::string_view)drag_catalog->path.name());
|
||||
}
|
||||
|
||||
bool AssetCatalogTreeViewAllItem::DropTarget::on_drop(struct bContext * /*C*/,
|
||||
|
||||
@@ -40,23 +40,26 @@ void FileBuffer::close_file()
|
||||
|
||||
void FileBuffer::write_header_element(StringRef name, int count)
|
||||
{
|
||||
write_fstring("element {} {}\n", name, count);
|
||||
write_fstring("element {} {}\n", (std::string_view)name, count);
|
||||
}
|
||||
void FileBuffer::write_header_scalar_property(StringRef dataType, StringRef name)
|
||||
{
|
||||
write_fstring("property {} {}\n", dataType, name);
|
||||
write_fstring("property {} {}\n", (std::string_view)dataType, (std::string_view)name);
|
||||
}
|
||||
|
||||
void FileBuffer::write_header_list_property(StringRef countType,
|
||||
StringRef dataType,
|
||||
StringRef name)
|
||||
{
|
||||
write_fstring("property list {} {} {}\n", countType, dataType, name);
|
||||
write_fstring("property list {} {} {}\n",
|
||||
(std::string_view)countType,
|
||||
(std::string_view)dataType,
|
||||
(std::string_view)name);
|
||||
}
|
||||
|
||||
void FileBuffer::write_string(StringRef s)
|
||||
{
|
||||
write_fstring("{}\n", s);
|
||||
write_fstring("{}\n", (std::string_view)s);
|
||||
}
|
||||
|
||||
void FileBuffer::write_newline()
|
||||
|
||||
@@ -108,11 +108,11 @@ class FormatHandler : NonCopyable, NonMovable {
|
||||
}
|
||||
void write_obj_usemtl(StringRef s)
|
||||
{
|
||||
write_impl("usemtl {}\n", s);
|
||||
write_impl("usemtl {}\n", (std::string_view)s);
|
||||
}
|
||||
void write_obj_mtllib(StringRef s)
|
||||
{
|
||||
write_impl("mtllib {}\n", s);
|
||||
write_impl("mtllib {}\n", (std::string_view)s);
|
||||
}
|
||||
void write_obj_smooth(int s)
|
||||
{
|
||||
@@ -120,11 +120,11 @@ class FormatHandler : NonCopyable, NonMovable {
|
||||
}
|
||||
void write_obj_group(StringRef s)
|
||||
{
|
||||
write_impl("g {}\n", s);
|
||||
write_impl("g {}\n", (std::string_view)s);
|
||||
}
|
||||
void write_obj_object(StringRef s)
|
||||
{
|
||||
write_impl("o {}\n", s);
|
||||
write_impl("o {}\n", (std::string_view)s);
|
||||
}
|
||||
void write_obj_edge(int a, int b)
|
||||
{
|
||||
@@ -169,7 +169,7 @@ class FormatHandler : NonCopyable, NonMovable {
|
||||
|
||||
void write_mtl_newmtl(StringRef s)
|
||||
{
|
||||
write_impl("newmtl {}\n", s);
|
||||
write_impl("newmtl {}\n", (std::string_view)s);
|
||||
}
|
||||
void write_mtl_float(const char *type, float v)
|
||||
{
|
||||
@@ -186,12 +186,12 @@ class FormatHandler : NonCopyable, NonMovable {
|
||||
/* NOTE: options, if present, will have its own leading space. */
|
||||
void write_mtl_map(const char *type, StringRef options, StringRef value)
|
||||
{
|
||||
write_impl("{}{} {}\n", type, options, value);
|
||||
write_impl("{}{} {}\n", type, (std::string_view)options, (std::string_view)value);
|
||||
}
|
||||
|
||||
void write_string(StringRef s)
|
||||
{
|
||||
write_impl("{}\n", s);
|
||||
write_impl("{}\n", (std::string_view)s);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user