mirror of https://github.com/axmolengine/axmol.git
Update fmtlib
This commit is contained in:
parent
65ee67539d
commit
2355ae96c9
|
@ -56,7 +56,7 @@
|
||||||
|
|
||||||
## {fmt}
|
## {fmt}
|
||||||
- Upstream: https://github.com/fmtlib/fmt
|
- Upstream: https://github.com/fmtlib/fmt
|
||||||
- Version: 8.0.1-2038bf6
|
- Version: git 8.1.1-9ff91b1 (5518)
|
||||||
- License: MIT
|
- License: MIT
|
||||||
|
|
||||||
## FreeType
|
## FreeType
|
||||||
|
|
|
@ -81,12 +81,13 @@ option(FMT_FUZZ "Generate the fuzz target." OFF)
|
||||||
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
|
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
|
||||||
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
|
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
|
||||||
option(FMT_MODULE "Build a module instead of a traditional library." OFF)
|
option(FMT_MODULE "Build a module instead of a traditional library." OFF)
|
||||||
|
option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF)
|
||||||
|
|
||||||
set(FMT_CAN_MODULE OFF)
|
set(FMT_CAN_MODULE OFF)
|
||||||
if (CMAKE_CXX_STANDARD GREATER 17 AND
|
if (CMAKE_CXX_STANDARD GREATER 17 AND
|
||||||
# msvc 16.10-pre4
|
# msvc 16.10-pre4
|
||||||
MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.29.30035)
|
MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.29.30035)
|
||||||
set(FMT_CAN_MODULE ON)
|
set(FMT_CAN_MODULE OFF)
|
||||||
endif ()
|
endif ()
|
||||||
if (NOT FMT_CAN_MODULE)
|
if (NOT FMT_CAN_MODULE)
|
||||||
set(FMT_MODULE OFF)
|
set(FMT_MODULE OFF)
|
||||||
|
@ -96,6 +97,10 @@ if (FMT_TEST AND FMT_MODULE)
|
||||||
# The tests require {fmt} to be compiled as traditional library
|
# The tests require {fmt} to be compiled as traditional library
|
||||||
message(STATUS "Testing is incompatible with build mode 'module'.")
|
message(STATUS "Testing is incompatible with build mode 'module'.")
|
||||||
endif ()
|
endif ()
|
||||||
|
set(FMT_SYSTEM_HEADERS_ATTRIBUTE "")
|
||||||
|
if (FMT_SYSTEM_HEADERS)
|
||||||
|
set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM)
|
||||||
|
endif ()
|
||||||
|
|
||||||
# Get version from core.h
|
# Get version from core.h
|
||||||
file(READ include/fmt/core.h core_h)
|
file(READ include/fmt/core.h core_h)
|
||||||
|
@ -151,7 +156,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
-Wcast-align
|
-Wcast-align
|
||||||
-Wctor-dtor-privacy -Wdisabled-optimization
|
-Wctor-dtor-privacy -Wdisabled-optimization
|
||||||
-Winvalid-pch -Woverloaded-virtual
|
-Winvalid-pch -Woverloaded-virtual
|
||||||
-Wconversion -Wswitch-enum -Wundef
|
-Wconversion -Wundef
|
||||||
-Wno-ctor-dtor-privacy -Wno-format-nonliteral)
|
-Wno-ctor-dtor-privacy -Wno-format-nonliteral)
|
||||||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
|
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
|
||||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
|
||||||
|
@ -244,7 +249,7 @@ if (HAVE_STRTOD_L)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (MINGW)
|
if (MINGW)
|
||||||
check_cxx_compiler_flag("Wa,-mbig-obj" FMT_HAS_MBIG_OBJ)
|
check_cxx_compiler_flag("-Wa,-mbig-obj" FMT_HAS_MBIG_OBJ)
|
||||||
if (${FMT_HAS_MBIG_OBJ})
|
if (${FMT_HAS_MBIG_OBJ})
|
||||||
target_compile_options(fmt PUBLIC "-Wa,-mbig-obj")
|
target_compile_options(fmt PUBLIC "-Wa,-mbig-obj")
|
||||||
endif()
|
endif()
|
||||||
|
@ -262,7 +267,7 @@ endif ()
|
||||||
|
|
||||||
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
|
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
|
||||||
|
|
||||||
target_include_directories(fmt PUBLIC
|
target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||||
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
|
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
|
||||||
|
|
||||||
|
@ -298,7 +303,7 @@ add_library(fmt::fmt-header-only ALIAS fmt-header-only)
|
||||||
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
|
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
|
||||||
target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES})
|
target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES})
|
||||||
|
|
||||||
target_include_directories(fmt-header-only INTERFACE
|
target_include_directories(fmt-header-only ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||||
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
|
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,374 @@
|
||||||
|
8.1.1 - 2022-01-06
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* Restored ABI compatibility with version 8.0.x
|
||||||
|
(`#2695 <https://github.com/fmtlib/fmt/issues/2695>`_,
|
||||||
|
`#2696 <https://github.com/fmtlib/fmt/pull/2696>`_).
|
||||||
|
Thanks `@saraedum (Julian Rüth) <https://github.com/saraedum>`_.
|
||||||
|
|
||||||
|
* Fixed chrono formatting on big endian systems
|
||||||
|
(`#2698 <https://github.com/fmtlib/fmt/issues/2698>`_,
|
||||||
|
`#2699 <https://github.com/fmtlib/fmt/pull/2699>`_).
|
||||||
|
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_ and
|
||||||
|
`@xvitaly (Vitaly Zaitsev) <https://github.com/xvitaly>`_.
|
||||||
|
|
||||||
|
* Fixed a linkage error with mingw
|
||||||
|
(`#2691 <https://github.com/fmtlib/fmt/issues/2691>`_,
|
||||||
|
`#2692 <https://github.com/fmtlib/fmt/pull/2692>`_).
|
||||||
|
Thanks `@rbberger (Richard Berger) <https://github.com/rbberger>`_.
|
||||||
|
|
||||||
|
8.1.0 - 2022-01-02
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* Optimized chrono formatting
|
||||||
|
(`#2500 <https://github.com/fmtlib/fmt/pull/2500>`_,
|
||||||
|
`#2537 <https://github.com/fmtlib/fmt/pull/2537>`_,
|
||||||
|
`#2541 <https://github.com/fmtlib/fmt/issues/2541>`_,
|
||||||
|
`#2544 <https://github.com/fmtlib/fmt/pull/2544>`_,
|
||||||
|
`#2550 <https://github.com/fmtlib/fmt/pull/2550>`_,
|
||||||
|
`#2551 <https://github.com/fmtlib/fmt/pull/2551>`_,
|
||||||
|
`#2576 <https://github.com/fmtlib/fmt/pull/2576>`_,
|
||||||
|
`#2577 <https://github.com/fmtlib/fmt/issues/2577>`_,
|
||||||
|
`#2586 <https://github.com/fmtlib/fmt/pull/2586>`_,
|
||||||
|
`#2591 <https://github.com/fmtlib/fmt/pull/2591>`_,
|
||||||
|
`#2594 <https://github.com/fmtlib/fmt/pull/2594>`_,
|
||||||
|
`#2602 <https://github.com/fmtlib/fmt/pull/2602>`_,
|
||||||
|
`#2617 <https://github.com/fmtlib/fmt/pull/2617>`_,
|
||||||
|
`#2628 <https://github.com/fmtlib/fmt/issues/2628>`_,
|
||||||
|
`#2633 <https://github.com/fmtlib/fmt/pull/2633>`_,
|
||||||
|
`#2670 <https://github.com/fmtlib/fmt/issues/2670>`_,
|
||||||
|
`#2671 <https://github.com/fmtlib/fmt/pull/2671>`_).
|
||||||
|
|
||||||
|
Processing of some specifiers such as ``%z`` and ``%Y`` is now up to 10-20
|
||||||
|
times faster, for example on GCC 11 with libstdc++::
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
Benchmark Before After
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
FMTFormatter_z 261 ns 26.3 ns
|
||||||
|
FMTFormatterCompile_z 246 ns 11.6 ns
|
||||||
|
FMTFormatter_Y 263 ns 26.1 ns
|
||||||
|
FMTFormatterCompile_Y 244 ns 10.5 ns
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_ and
|
||||||
|
`@toughengineer (Pavel Novikov) <https://github.com/toughengineer>`_.
|
||||||
|
|
||||||
|
* Implemented subsecond formatting for chrono durations
|
||||||
|
(`#2623 <https://github.com/fmtlib/fmt/pull/2623>`_).
|
||||||
|
For example (`godbolt <https://godbolt.org/z/es7vWTETe>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/chrono.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
fmt::print("{:%S}", std::chrono::milliseconds(1234));
|
||||||
|
}
|
||||||
|
|
||||||
|
prints "01.234".
|
||||||
|
|
||||||
|
Thanks `@matrackif <https://github.com/matrackif>`_.
|
||||||
|
|
||||||
|
* Fixed handling of precision 0 when formatting chrono durations
|
||||||
|
(`#2587 <https://github.com/fmtlib/fmt/issues/2587>`_,
|
||||||
|
`#2588 <https://github.com/fmtlib/fmt/pull/2588>`_).
|
||||||
|
Thanks `@lukester1975 <https://github.com/lukester1975>`_.
|
||||||
|
|
||||||
|
* Fixed an overflow on invalid inputs in the ``tm`` formatter
|
||||||
|
(`#2564 <https://github.com/fmtlib/fmt/pull/2564>`_).
|
||||||
|
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
|
||||||
|
|
||||||
|
* Added ``fmt::group_digits`` that formats integers with a non-localized digit
|
||||||
|
separator (comma) for groups of three digits.
|
||||||
|
For example (`godbolt <https://godbolt.org/z/TxGxG9Poq>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
fmt::print("{} dollars", fmt::group_digits(1000000));
|
||||||
|
}
|
||||||
|
|
||||||
|
prints "1,000,000 dollars".
|
||||||
|
|
||||||
|
* Added support for faint, conceal, reverse and blink text styles
|
||||||
|
(`#2394 <https://github.com/fmtlib/fmt/pull/2394>`_):
|
||||||
|
|
||||||
|
https://user-images.githubusercontent.com/576385/147710227-c68f5317-f8fa-42c3-9123-7c4ba3c398cb.mp4
|
||||||
|
|
||||||
|
Thanks `@benit8 (Benoît Lormeau) <https://github.com/benit8>`_ and
|
||||||
|
`@data-man (Dmitry Atamanov) <https://github.com/data-man>`_.
|
||||||
|
|
||||||
|
* Added experimental support for compile-time floating point formatting
|
||||||
|
(`#2426 <https://github.com/fmtlib/fmt/pull/2426>`_,
|
||||||
|
`#2470 <https://github.com/fmtlib/fmt/pull/2470>`_).
|
||||||
|
It is currently limited to the header-only mode.
|
||||||
|
Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_.
|
||||||
|
|
||||||
|
* Added UDL-based named argument support to compile-time format string checks
|
||||||
|
(`#2640 <https://github.com/fmtlib/fmt/issues/2640>`_,
|
||||||
|
`#2649 <https://github.com/fmtlib/fmt/pull/2649>`_).
|
||||||
|
For example (`godbolt <https://godbolt.org/z/ohGbbvonv>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
using namespace fmt::literals;
|
||||||
|
fmt::print("{answer:s}", "answer"_a=42);
|
||||||
|
}
|
||||||
|
|
||||||
|
gives a compile-time error on compilers with C++20 ``consteval`` and non-type
|
||||||
|
template parameter support (gcc 10+) because ``s`` is not a valid format
|
||||||
|
specifier for an integer.
|
||||||
|
|
||||||
|
Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_.
|
||||||
|
|
||||||
|
* Implemented escaping of string range elements.
|
||||||
|
For example (`godbolt <https://godbolt.org/z/rKvM1vKf3>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/ranges.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
fmt::print("{}", std::vector<std::string>{"\naan"});
|
||||||
|
}
|
||||||
|
|
||||||
|
is now printed as::
|
||||||
|
|
||||||
|
["\naan"]
|
||||||
|
|
||||||
|
instead of::
|
||||||
|
|
||||||
|
["
|
||||||
|
aan"]
|
||||||
|
|
||||||
|
* Switched to JSON-like representation of maps and sets for consistency with
|
||||||
|
Python's ``str.format``.
|
||||||
|
For example (`godbolt <https://godbolt.org/z/seKjoY9W5>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/ranges.h>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
fmt::print("{}", std::map<std::string, int>{{"answer", 42}});
|
||||||
|
}
|
||||||
|
|
||||||
|
is now printed as::
|
||||||
|
|
||||||
|
{"answer": 42}
|
||||||
|
|
||||||
|
* Extended ``fmt::join`` to support C++20-only ranges
|
||||||
|
(`#2549 <https://github.com/fmtlib/fmt/pull/2549>`_).
|
||||||
|
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
|
||||||
|
|
||||||
|
* Optimized handling of non-const-iterable ranges and implemented initial
|
||||||
|
support for non-const-formattable types.
|
||||||
|
|
||||||
|
* Disabled implicit conversions of scoped enums to integers that was
|
||||||
|
accidentally introduced in earlier versions
|
||||||
|
(`#1841 <https://github.com/fmtlib/fmt/pull/1841>`_).
|
||||||
|
|
||||||
|
* Deprecated implicit conversion of ``[const] signed char*`` and
|
||||||
|
``[const] unsigned char*`` to C strings.
|
||||||
|
|
||||||
|
* Deprecated ``_format``, a legacy UDL-based format API
|
||||||
|
(`#2646 <https://github.com/fmtlib/fmt/pull/2646>`_).
|
||||||
|
Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_.
|
||||||
|
|
||||||
|
* Marked ``format``, ``formatted_size`` and ``to_string`` as ``[[nodiscard]]``
|
||||||
|
(`#2612 <https://github.com/fmtlib/fmt/pull/2612>`_).
|
||||||
|
`@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_.
|
||||||
|
|
||||||
|
* Added missing diagnostic when trying to format function and member pointers
|
||||||
|
as well as objects convertible to pointers which is explicitly disallowed
|
||||||
|
(`#2598 <https://github.com/fmtlib/fmt/issues/2598>`_,
|
||||||
|
`#2609 <https://github.com/fmtlib/fmt/pull/2609>`_,
|
||||||
|
`#2610 <https://github.com/fmtlib/fmt/pull/2610>`_).
|
||||||
|
Thanks `@AlexGuteniev (Alex Guteniev) <https://github.com/AlexGuteniev>`_.
|
||||||
|
|
||||||
|
* Optimized writing to a contiguous buffer with ``format_to_n``
|
||||||
|
(`#2489 <https://github.com/fmtlib/fmt/pull/2489>`_).
|
||||||
|
Thanks `@Roman-Koshelev <https://github.com/Roman-Koshelev>`_.
|
||||||
|
|
||||||
|
* Optimized writing to non-``char`` buffers
|
||||||
|
(`#2477 <https://github.com/fmtlib/fmt/pull/2477>`_).
|
||||||
|
Thanks `@Roman-Koshelev <https://github.com/Roman-Koshelev>`_.
|
||||||
|
|
||||||
|
* Decimal point is now localized when using the ``L`` specifier.
|
||||||
|
|
||||||
|
* Improved floating point formatter implementation
|
||||||
|
(`#2498 <https://github.com/fmtlib/fmt/pull/2498>`_,
|
||||||
|
`#2499 <https://github.com/fmtlib/fmt/pull/2499>`_).
|
||||||
|
Thanks `@Roman-Koshelev <https://github.com/Roman-Koshelev>`_.
|
||||||
|
|
||||||
|
* Fixed handling of very large precision in fixed format
|
||||||
|
(`#2616 <https://github.com/fmtlib/fmt/pull/2616>`_).
|
||||||
|
|
||||||
|
* Made a table of cached powers used in FP formatting static
|
||||||
|
(`#2509 <https://github.com/fmtlib/fmt/pull/2509>`_).
|
||||||
|
Thanks `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_.
|
||||||
|
|
||||||
|
* Resolved a lookup ambiguity with C++20 format-related functions due to ADL
|
||||||
|
(`#2639 <https://github.com/fmtlib/fmt/issues/2639>`_,
|
||||||
|
`#2641 <https://github.com/fmtlib/fmt/pull/2641>`_).
|
||||||
|
Thanks `@mkurdej (Marek Kurdej) <https://github.com/mkurdej>`_.
|
||||||
|
|
||||||
|
* Removed unnecessary inline namespace qualification
|
||||||
|
(`#2642 <https://github.com/fmtlib/fmt/issues/2642>`_,
|
||||||
|
`#2643 <https://github.com/fmtlib/fmt/pull/2643>`_).
|
||||||
|
Thanks `@mkurdej (Marek Kurdej) <https://github.com/mkurdej>`_.
|
||||||
|
|
||||||
|
* Implemented argument forwarding in ``format_to_n``
|
||||||
|
(`#2462 <https://github.com/fmtlib/fmt/issues/2462>`_,
|
||||||
|
`#2463 <https://github.com/fmtlib/fmt/pull/2463>`_).
|
||||||
|
Thanks `@owent (WenTao Ou) <https://github.com/owent>`_.
|
||||||
|
|
||||||
|
* Fixed handling of implicit conversions in ``fmt::to_string`` and format string
|
||||||
|
compilation (`#2565 <https://github.com/fmtlib/fmt/issues/2565>`_).
|
||||||
|
|
||||||
|
* Changed the default access mode of files created by ``fmt::output_file`` to
|
||||||
|
``-rw-r--r--`` for consistency with ``fopen``
|
||||||
|
(`#2530 <https://github.com/fmtlib/fmt/issues/2530>`_).
|
||||||
|
|
||||||
|
* Make ``fmt::ostream::flush`` public
|
||||||
|
(`#2435 <https://github.com/fmtlib/fmt/issues/2435>`_).
|
||||||
|
|
||||||
|
* Improved C++14/17 attribute detection
|
||||||
|
(`#2615 <https://github.com/fmtlib/fmt/pull/2615>`_).
|
||||||
|
Thanks `@AlexGuteniev (Alex Guteniev) <https://github.com/AlexGuteniev>`_.
|
||||||
|
|
||||||
|
* Improved ``consteval`` detection for MSVC
|
||||||
|
(`#2559 <https://github.com/fmtlib/fmt/pull/2559>`_).
|
||||||
|
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
|
||||||
|
|
||||||
|
* Improved documentation
|
||||||
|
(`#2406 <https://github.com/fmtlib/fmt/issues/2406>`_,
|
||||||
|
`#2446 <https://github.com/fmtlib/fmt/pull/2446>`_,
|
||||||
|
`#2493 <https://github.com/fmtlib/fmt/issues/2493>`_,
|
||||||
|
`#2513 <https://github.com/fmtlib/fmt/issues/2513>`_,
|
||||||
|
`#2515 <https://github.com/fmtlib/fmt/pull/2515>`_,
|
||||||
|
`#2522 <https://github.com/fmtlib/fmt/issues/2522>`_,
|
||||||
|
`#2562 <https://github.com/fmtlib/fmt/pull/2562>`_,
|
||||||
|
`#2575 <https://github.com/fmtlib/fmt/pull/2575>`_,
|
||||||
|
`#2606 <https://github.com/fmtlib/fmt/pull/2606>`_,
|
||||||
|
`#2620 <https://github.com/fmtlib/fmt/pull/2620>`_,
|
||||||
|
`#2676 <https://github.com/fmtlib/fmt/issues/2676>`_).
|
||||||
|
Thanks `@sobolevn (Nikita Sobolev) <https://github.com/sobolevn>`_,
|
||||||
|
`@UnePierre (Max FERGER) <https://github.com/UnePierre>`_,
|
||||||
|
`@zhsj <https://github.com/zhsj>`_,
|
||||||
|
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_,
|
||||||
|
`@ericcurtin (Eric Curtin) <https://github.com/ericcurtin>`_,
|
||||||
|
`@Lounarok <https://github.com/Lounarok>`_.
|
||||||
|
|
||||||
|
* Improved fuzzers and added a fuzzer for chrono timepoint formatting
|
||||||
|
(`#2461 <https://github.com/fmtlib/fmt/pull/2461>`_,
|
||||||
|
`#2469 <https://github.com/fmtlib/fmt/pull/2469>`_).
|
||||||
|
`@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_,
|
||||||
|
|
||||||
|
* Added the ``FMT_SYSTEM_HEADERS`` CMake option setting which marks {fmt}'s
|
||||||
|
headers as system. It can be used to suppress warnings
|
||||||
|
(`#2644 <https://github.com/fmtlib/fmt/issues/2644>`_,
|
||||||
|
`#2651 <https://github.com/fmtlib/fmt/pull/2651>`_).
|
||||||
|
Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_.
|
||||||
|
|
||||||
|
* Added the Bazel build system support
|
||||||
|
(`#2505 <https://github.com/fmtlib/fmt/pull/2505>`_,
|
||||||
|
`#2516 <https://github.com/fmtlib/fmt/pull/2516>`_).
|
||||||
|
Thanks `@Vertexwahn <https://github.com/Vertexwahn>`_.
|
||||||
|
|
||||||
|
* Improved build configuration and tests
|
||||||
|
(`#2437 <https://github.com/fmtlib/fmt/issues/2437>`_,
|
||||||
|
`#2558 <https://github.com/fmtlib/fmt/pull/2558>`_,
|
||||||
|
`#2648 <https://github.com/fmtlib/fmt/pull/2648>`_,
|
||||||
|
`#2650 <https://github.com/fmtlib/fmt/pull/2650>`_,
|
||||||
|
`#2663 <https://github.com/fmtlib/fmt/pull/2663>`_,
|
||||||
|
`#2677 <https://github.com/fmtlib/fmt/pull/2677>`_).
|
||||||
|
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_,
|
||||||
|
`@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_,
|
||||||
|
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
|
||||||
|
|
||||||
|
* Fixed various warnings and compilation issues
|
||||||
|
(`#2353 <https://github.com/fmtlib/fmt/pull/2353>`_,
|
||||||
|
`#2356 <https://github.com/fmtlib/fmt/pull/2356>`_,
|
||||||
|
`#2399 <https://github.com/fmtlib/fmt/pull/2399>`_,
|
||||||
|
`#2408 <https://github.com/fmtlib/fmt/issues/2408>`_,
|
||||||
|
`#2414 <https://github.com/fmtlib/fmt/pull/2414>`_,
|
||||||
|
`#2427 <https://github.com/fmtlib/fmt/pull/2427>`_,
|
||||||
|
`#2432 <https://github.com/fmtlib/fmt/pull/2432>`_,
|
||||||
|
`#2442 <https://github.com/fmtlib/fmt/pull/2442>`_,
|
||||||
|
`#2434 <https://github.com/fmtlib/fmt/pull/2434>`_,
|
||||||
|
`#2439 <https://github.com/fmtlib/fmt/issues/2439>`_,
|
||||||
|
`#2447 <https://github.com/fmtlib/fmt/pull/2447>`_,
|
||||||
|
`#2450 <https://github.com/fmtlib/fmt/pull/2450>`_,
|
||||||
|
`#2455 <https://github.com/fmtlib/fmt/issues/2455>`_,
|
||||||
|
`#2465 <https://github.com/fmtlib/fmt/issues/2465>`_,
|
||||||
|
`#2472 <https://github.com/fmtlib/fmt/issues/2472>`_,
|
||||||
|
`#2474 <https://github.com/fmtlib/fmt/issues/2474>`_,
|
||||||
|
`#2476 <https://github.com/fmtlib/fmt/pull/2476>`_,
|
||||||
|
`#2478 <https://github.com/fmtlib/fmt/issues/2478>`_,
|
||||||
|
`#2479 <https://github.com/fmtlib/fmt/issues/2479>`_,
|
||||||
|
`#2481 <https://github.com/fmtlib/fmt/issues/2481>`_,
|
||||||
|
`#2482 <https://github.com/fmtlib/fmt/pull/2482>`_,
|
||||||
|
`#2483 <https://github.com/fmtlib/fmt/pull/2483>`_,
|
||||||
|
`#2490 <https://github.com/fmtlib/fmt/issues/2490>`_,
|
||||||
|
`#2491 <https://github.com/fmtlib/fmt/pull/2491>`_,
|
||||||
|
`#2510 <https://github.com/fmtlib/fmt/pull/2510>`_,
|
||||||
|
`#2518 <https://github.com/fmtlib/fmt/pull/2518>`_,
|
||||||
|
`#2528 <https://github.com/fmtlib/fmt/issues/2528>`_,
|
||||||
|
`#2529 <https://github.com/fmtlib/fmt/pull/2529>`_,
|
||||||
|
`#2539 <https://github.com/fmtlib/fmt/pull/2539>`_,
|
||||||
|
`#2540 <https://github.com/fmtlib/fmt/issues/2540>`_,
|
||||||
|
`#2545 <https://github.com/fmtlib/fmt/pull/2545>`_,
|
||||||
|
`#2555 <https://github.com/fmtlib/fmt/pull/2555>`_,
|
||||||
|
`#2557 <https://github.com/fmtlib/fmt/issues/2557>`_,
|
||||||
|
`#2570 <https://github.com/fmtlib/fmt/issues/2570>`_,
|
||||||
|
`#2573 <https://github.com/fmtlib/fmt/pull/2573>`_,
|
||||||
|
`#2582 <https://github.com/fmtlib/fmt/pull/2582>`_,
|
||||||
|
`#2605 <https://github.com/fmtlib/fmt/issues/2605>`_,
|
||||||
|
`#2611 <https://github.com/fmtlib/fmt/pull/2611>`_,
|
||||||
|
`#2647 <https://github.com/fmtlib/fmt/pull/2647>`_,
|
||||||
|
`#2627 <https://github.com/fmtlib/fmt/issues/2627>`_,
|
||||||
|
`#2630 <https://github.com/fmtlib/fmt/pull/2630>`_,
|
||||||
|
`#2635 <https://github.com/fmtlib/fmt/issues/2635>`_,
|
||||||
|
`#2638 <https://github.com/fmtlib/fmt/issues/2638>`_,
|
||||||
|
`#2653 <https://github.com/fmtlib/fmt/issues/2653>`_,
|
||||||
|
`#2654 <https://github.com/fmtlib/fmt/issues/2654>`_,
|
||||||
|
`#2661 <https://github.com/fmtlib/fmt/issues/2661>`_,
|
||||||
|
`#2664 <https://github.com/fmtlib/fmt/pull/2664>`_,
|
||||||
|
`#2684 <https://github.com/fmtlib/fmt/pull/2684>`_).
|
||||||
|
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_,
|
||||||
|
`@mwinterb <https://github.com/mwinterb>`_,
|
||||||
|
`@cdacamar (Cameron DaCamara) <https://github.com/cdacamar>`_,
|
||||||
|
`@TrebledJ (Johnathan) <https://github.com/TrebledJ>`_,
|
||||||
|
`@bodomartin (brm) <https://github.com/bodomartin>`_,
|
||||||
|
`@cquammen (Cory Quammen) <https://github.com/cquammen>`_,
|
||||||
|
`@white238 (Chris White) <https://github.com/white238>`_,
|
||||||
|
`@mmarkeloff (Max) <https://github.com/mmarkeloff>`_,
|
||||||
|
`@palacaze (Pierre-Antoine Lacaze) <https://github.com/palacaze>`_,
|
||||||
|
`@jcelerier (Jean-Michaël Celerier) <https://github.com/jcelerier>`_,
|
||||||
|
`@mborn-adi (Mathias Born) <https://github.com/mborn-adi>`_,
|
||||||
|
`@BrukerJWD (Jonathan W) <https://github.com/BrukerJWD>`_,
|
||||||
|
`@spyridon97 (Spiros Tsalikis) <https://github.com/spyridon97>`_,
|
||||||
|
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_,
|
||||||
|
`@oliverlee (Oliver Lee) <https://github.com/oliverlee>`_,
|
||||||
|
`@joshessman-llnl (Josh Essman) <https://github.com/joshessman-llnl>`_,
|
||||||
|
`@akohlmey (Axel Kohlmeyer) <https://github.com/akohlmey>`_,
|
||||||
|
`@timkalu <https://github.com/timkalu>`_,
|
||||||
|
`@olupton (Olli Lupton) <https://github.com/olupton>`_,
|
||||||
|
`@Acretock <https://github.com/Acretock>`_,
|
||||||
|
`@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_,
|
||||||
|
`@andrewcorrigan (Andrew Corrigan) <https://github.com/andrewcorrigan>`_,
|
||||||
|
`@lucpelletier <https://github.com/lucpelletier>`_,
|
||||||
|
`@HazardyKnusperkeks (Björn Schäpers) <https://github.com/HazardyKnusperkeks>`_.
|
||||||
|
|
||||||
8.0.1 - 2021-07-02
|
8.0.1 - 2021-07-02
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@ -34,7 +405,7 @@
|
||||||
`#2389 <https://github.com/fmtlib/fmt/pull/2389>`_,
|
`#2389 <https://github.com/fmtlib/fmt/pull/2389>`_,
|
||||||
`#2395 <https://github.com/fmtlib/fmt/pull/2395>`_,
|
`#2395 <https://github.com/fmtlib/fmt/pull/2395>`_,
|
||||||
`#2397 <https://github.com/fmtlib/fmt/pull/2397>`_,
|
`#2397 <https://github.com/fmtlib/fmt/pull/2397>`_,
|
||||||
`#2400 <https://github.com/fmtlib/fmt/issues/2400>`_
|
`#2400 <https://github.com/fmtlib/fmt/issues/2400>`_,
|
||||||
`#2401 <https://github.com/fmtlib/fmt/issues/2401>`_,
|
`#2401 <https://github.com/fmtlib/fmt/issues/2401>`_,
|
||||||
`#2407 <https://github.com/fmtlib/fmt/pull/2407>`_).
|
`#2407 <https://github.com/fmtlib/fmt/pull/2407>`_).
|
||||||
Thanks `@zx2c4 (Jason A. Donenfeld) <https://github.com/zx2c4>`_,
|
Thanks `@zx2c4 (Jason A. Donenfeld) <https://github.com/zx2c4>`_,
|
||||||
|
@ -50,7 +421,7 @@
|
||||||
8.0.0 - 2021-06-21
|
8.0.0 - 2021-06-21
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
* Enabled compile-time format string check by default.
|
* Enabled compile-time format string checks by default.
|
||||||
For example (`godbolt <https://godbolt.org/z/sMxcohGjz>`__):
|
For example (`godbolt <https://godbolt.org/z/sMxcohGjz>`__):
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
@ -281,6 +652,9 @@
|
||||||
This doesn't introduce a dependency on ``<locale>`` so there is virtually no
|
This doesn't introduce a dependency on ``<locale>`` so there is virtually no
|
||||||
compile time effect.
|
compile time effect.
|
||||||
|
|
||||||
|
* Deprecated an undocumented ``format_to`` overload that takes
|
||||||
|
``basic_memory_buffer``.
|
||||||
|
|
||||||
* Made parameter order in ``vformat_to`` consistent with ``format_to``
|
* Made parameter order in ``vformat_to`` consistent with ``format_to``
|
||||||
(`#2327 <https://github.com/fmtlib/fmt/issues/2327>`_).
|
(`#2327 <https://github.com/fmtlib/fmt/issues/2327>`_).
|
||||||
|
|
||||||
|
@ -387,6 +761,9 @@
|
||||||
|
|
||||||
Thanks `@mikecrowe (Mike Crowe) <https://github.com/mikecrowe>`_.
|
Thanks `@mikecrowe (Mike Crowe) <https://github.com/mikecrowe>`_.
|
||||||
|
|
||||||
|
* The undocumented support for specializing ``formatter`` for pointer types
|
||||||
|
has been removed.
|
||||||
|
|
||||||
* Fixed ``fmt::formatted_size`` with format string compilation
|
* Fixed ``fmt::formatted_size`` with format string compilation
|
||||||
(`#2141 <https://github.com/fmtlib/fmt/pull/2141>`_,
|
(`#2141 <https://github.com/fmtlib/fmt/pull/2141>`_,
|
||||||
`#2161 <https://github.com/fmtlib/fmt/pull/2161>`_).
|
`#2161 <https://github.com/fmtlib/fmt/pull/2161>`_).
|
||||||
|
@ -559,13 +936,13 @@
|
||||||
`#2067 <https://github.com/fmtlib/fmt/pull/2067>`_,
|
`#2067 <https://github.com/fmtlib/fmt/pull/2067>`_,
|
||||||
`#2068 <https://github.com/fmtlib/fmt/pull/2068>`_,
|
`#2068 <https://github.com/fmtlib/fmt/pull/2068>`_,
|
||||||
`#2073 <https://github.com/fmtlib/fmt/pull/2073>`_,
|
`#2073 <https://github.com/fmtlib/fmt/pull/2073>`_,
|
||||||
`#2103 <https://github.com/fmtlib/fmt/issues/2103>`_
|
`#2103 <https://github.com/fmtlib/fmt/issues/2103>`_,
|
||||||
`#2105 <https://github.com/fmtlib/fmt/issues/2105>`_
|
`#2105 <https://github.com/fmtlib/fmt/issues/2105>`_,
|
||||||
`#2106 <https://github.com/fmtlib/fmt/pull/2106>`_,
|
`#2106 <https://github.com/fmtlib/fmt/pull/2106>`_,
|
||||||
`#2107 <https://github.com/fmtlib/fmt/pull/2107>`_,
|
`#2107 <https://github.com/fmtlib/fmt/pull/2107>`_,
|
||||||
`#2116 <https://github.com/fmtlib/fmt/issues/2116>`_
|
`#2116 <https://github.com/fmtlib/fmt/issues/2116>`_,
|
||||||
`#2117 <https://github.com/fmtlib/fmt/pull/2117>`_,
|
`#2117 <https://github.com/fmtlib/fmt/pull/2117>`_,
|
||||||
`#2118 <https://github.com/fmtlib/fmt/issues/2118>`_
|
`#2118 <https://github.com/fmtlib/fmt/issues/2118>`_,
|
||||||
`#2119 <https://github.com/fmtlib/fmt/pull/2119>`_,
|
`#2119 <https://github.com/fmtlib/fmt/pull/2119>`_,
|
||||||
`#2127 <https://github.com/fmtlib/fmt/issues/2127>`_,
|
`#2127 <https://github.com/fmtlib/fmt/issues/2127>`_,
|
||||||
`#2128 <https://github.com/fmtlib/fmt/pull/2128>`_,
|
`#2128 <https://github.com/fmtlib/fmt/pull/2128>`_,
|
||||||
|
@ -638,7 +1015,7 @@
|
||||||
`@yeswalrus (Walter Gray) <https://github.com/yeswalrus>`_,
|
`@yeswalrus (Walter Gray) <https://github.com/yeswalrus>`_,
|
||||||
`@Finkman <https://github.com/Finkman>`_,
|
`@Finkman <https://github.com/Finkman>`_,
|
||||||
`@HazardyKnusperkeks (Björn Schäpers) <https://github.com/HazardyKnusperkeks>`_,
|
`@HazardyKnusperkeks (Björn Schäpers) <https://github.com/HazardyKnusperkeks>`_,
|
||||||
`@dkavolis (Daumantas Kavolis) <https://github.com/dkavolis>`_
|
`@dkavolis (Daumantas Kavolis) <https://github.com/dkavolis>`_,
|
||||||
`@concatime (Issam Maghni) <https://github.com/concatime>`_,
|
`@concatime (Issam Maghni) <https://github.com/concatime>`_,
|
||||||
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
|
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
|
||||||
`@summivox (Yin Zhong) <https://github.com/summivox>`_,
|
`@summivox (Yin Zhong) <https://github.com/summivox>`_,
|
||||||
|
@ -1095,7 +1472,7 @@
|
||||||
`#1912 <https://github.com/fmtlib/fmt/issues/1912>`_,
|
`#1912 <https://github.com/fmtlib/fmt/issues/1912>`_,
|
||||||
`#1928 <https://github.com/fmtlib/fmt/issues/1928>`_,
|
`#1928 <https://github.com/fmtlib/fmt/issues/1928>`_,
|
||||||
`#1929 <https://github.com/fmtlib/fmt/pull/1929>`_,
|
`#1929 <https://github.com/fmtlib/fmt/pull/1929>`_,
|
||||||
`#1935 <https://github.com/fmtlib/fmt/issues/1935>`_
|
`#1935 <https://github.com/fmtlib/fmt/issues/1935>`_,
|
||||||
`#1937 <https://github.com/fmtlib/fmt/pull/1937>`_,
|
`#1937 <https://github.com/fmtlib/fmt/pull/1937>`_,
|
||||||
`#1942 <https://github.com/fmtlib/fmt/pull/1942>`_,
|
`#1942 <https://github.com/fmtlib/fmt/pull/1942>`_,
|
||||||
`#1949 <https://github.com/fmtlib/fmt/issues/1949>`_).
|
`#1949 <https://github.com/fmtlib/fmt/issues/1949>`_).
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
.. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg
|
.. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg
|
||||||
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows
|
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows
|
||||||
|
|
||||||
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
|
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v?svg=true
|
||||||
:target: https://ci.appveyor.com/project/vitaut/fmt
|
:target: https://ci.appveyor.com/project/vitaut/fmt
|
||||||
|
|
||||||
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg
|
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg
|
||||||
|
@ -143,10 +143,10 @@ Output::
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
std::string s = fmt::format(FMT_STRING("{:d}"), "I am not a number");
|
std::string s = fmt::format("{:d}", "I am not a number");
|
||||||
|
|
||||||
This gives a compile-time error because ``d`` is an invalid format specifier for
|
This gives a compile-time error in C++20 because ``d`` is an invalid format
|
||||||
a string.
|
specifier for a string.
|
||||||
|
|
||||||
**Write a file from a single thread**
|
**Write a file from a single thread**
|
||||||
|
|
||||||
|
@ -205,7 +205,7 @@ The above results were generated by building ``tinyformat_test.cpp`` on macOS
|
||||||
best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
|
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
|
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
|
||||||
further details refer to the `source
|
further details refer to the `source
|
||||||
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
|
<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
|
{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on
|
||||||
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
|
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
|
||||||
|
@ -469,7 +469,7 @@ Boost Format
|
||||||
|
|
||||||
This is a very powerful library which supports both ``printf``-like format
|
This is a very powerful library which supports both ``printf``-like format
|
||||||
strings and positional arguments. Its main drawback is performance. According to
|
strings and positional arguments. Its main drawback is performance. According to
|
||||||
various, benchmarks it is much slower than other methods considered here. Boost
|
various benchmarks, it is much slower than other methods considered here. Boost
|
||||||
Format also has excessive build times and severe code bloat issues (see
|
Format also has excessive build times and severe code bloat issues (see
|
||||||
`Benchmarks`_).
|
`Benchmarks`_).
|
||||||
|
|
||||||
|
|
|
@ -143,6 +143,8 @@ class dynamic_format_arg_store
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
constexpr dynamic_format_arg_store() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Adds an argument into the dynamic store for later passing to a formatting
|
Adds an argument into the dynamic store for later passing to a formatting
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -185,9 +185,13 @@ enum class terminal_color : uint8_t {
|
||||||
|
|
||||||
enum class emphasis : uint8_t {
|
enum class emphasis : uint8_t {
|
||||||
bold = 1,
|
bold = 1,
|
||||||
italic = 1 << 1,
|
faint = 1 << 1,
|
||||||
underline = 1 << 2,
|
italic = 1 << 2,
|
||||||
strikethrough = 1 << 3
|
underline = 1 << 3,
|
||||||
|
blink = 1 << 4,
|
||||||
|
reverse = 1 << 5,
|
||||||
|
conceal = 1 << 6,
|
||||||
|
strikethrough = 1 << 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
// rgb is a struct for red, green and blue colors.
|
// rgb is a struct for red, green and blue colors.
|
||||||
|
@ -409,16 +413,18 @@ template <typename Char> struct ansi_color_escape {
|
||||||
buffer[19] = static_cast<Char>(0);
|
buffer[19] = static_cast<Char>(0);
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
||||||
uint8_t em_codes[4] = {};
|
uint8_t em_codes[num_emphases] = {};
|
||||||
uint8_t em_bits = static_cast<uint8_t>(em);
|
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
|
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
|
if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
|
if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
|
||||||
em_codes[3] = 9;
|
if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
|
||||||
|
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
|
||||||
|
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
|
||||||
|
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (size_t i = 0; i < num_emphases; ++i) {
|
||||||
if (!em_codes[i]) continue;
|
if (!em_codes[i]) continue;
|
||||||
buffer[index++] = static_cast<Char>('\x1b');
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
buffer[index++] = static_cast<Char>('[');
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
@ -435,7 +441,8 @@ template <typename Char> struct ansi_color_escape {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Char buffer[7u + 3u * 4u + 1u];
|
static constexpr size_t num_emphases = 8;
|
||||||
|
Char buffer[7u + 3u * num_emphases + 1u];
|
||||||
|
|
||||||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
||||||
char delimiter) FMT_NOEXCEPT {
|
char delimiter) FMT_NOEXCEPT {
|
||||||
|
@ -444,6 +451,10 @@ template <typename Char> struct ansi_color_escape {
|
||||||
out[2] = static_cast<Char>('0' + c % 10);
|
out[2] = static_cast<Char>('0' + c % 10);
|
||||||
out[3] = static_cast<Char>(delimiter);
|
out[3] = static_cast<Char>(delimiter);
|
||||||
}
|
}
|
||||||
|
static FMT_CONSTEXPR bool has_emphasis(emphasis em,
|
||||||
|
emphasis mask) FMT_NOEXCEPT {
|
||||||
|
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
|
@ -463,26 +474,27 @@ FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
|
||||||
return ansi_color_escape<Char>(em);
|
return ansi_color_escape<Char>(em);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char> inline void fputs(const Char* chars, FILE* stream) {
|
||||||
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
|
int result = std::fputs(chars, stream);
|
||||||
std::fputs(chars, stream);
|
if (result < 0)
|
||||||
|
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) {
|
||||||
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
|
int result = std::fputws(chars, stream);
|
||||||
std::fputws(chars, stream);
|
if (result < 0)
|
||||||
|
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
|
template <typename Char> inline void reset_color(FILE* stream) {
|
||||||
fputs("\x1b[0m", stream);
|
fputs("\x1b[0m", stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
template <> inline void reset_color<wchar_t>(FILE* stream) {
|
||||||
fputs(L"\x1b[0m", stream);
|
fputs(L"\x1b[0m", stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char> inline void reset_color(buffer<Char>& buffer) {
|
||||||
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
|
|
||||||
auto reset_color = string_view("\x1b[0m");
|
auto reset_color = string_view("\x1b[0m");
|
||||||
buffer.append(reset_color.begin(), reset_color.end());
|
buffer.append(reset_color.begin(), reset_color.end());
|
||||||
}
|
}
|
||||||
|
@ -518,8 +530,12 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buf;
|
basic_memory_buffer<Char> buf;
|
||||||
detail::vformat_to(buf, ts, to_string_view(format), args);
|
detail::vformat_to(buf, ts, to_string_view(format), args);
|
||||||
buf.push_back(Char(0));
|
if (detail::is_utf8()) {
|
||||||
detail::fputs(buf.data(), f);
|
detail::print(f, basic_string_view<Char>(buf.begin(), buf.size()));
|
||||||
|
} else {
|
||||||
|
buf.push_back(Char(0));
|
||||||
|
detail::fputs(buf.data(), f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -156,7 +156,7 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
||||||
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
|
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
#ifdef __cpp_if_constexpr
|
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
# define FMT_COMPILE(s) \
|
# define FMT_COMPILE(s) \
|
||||||
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
|
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
|
||||||
#else
|
#else
|
||||||
|
@ -179,7 +179,7 @@ const T& first(const T& value, const Tail&...) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cpp_if_constexpr
|
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
template <typename... Args> struct type_list {};
|
template <typename... Args> struct type_list {};
|
||||||
|
|
||||||
// Returns a reference to the argument at index N from [first, rest...].
|
// Returns a reference to the argument at index N from [first, rest...].
|
||||||
|
@ -190,7 +190,7 @@ constexpr const auto& get([[maybe_unused]] const T& first,
|
||||||
if constexpr (N == 0)
|
if constexpr (N == 0)
|
||||||
return first;
|
return first;
|
||||||
else
|
else
|
||||||
return get<N - 1>(rest...);
|
return detail::get<N - 1>(rest...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename... Args>
|
template <typename Char, typename... Args>
|
||||||
|
@ -202,7 +202,8 @@ constexpr int get_arg_index_by_name(basic_string_view<Char> name,
|
||||||
template <int N, typename> struct get_type_impl;
|
template <int N, typename> struct get_type_impl;
|
||||||
|
|
||||||
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
||||||
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
|
using type =
|
||||||
|
remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <int N, typename T>
|
template <int N, typename T>
|
||||||
|
@ -242,7 +243,7 @@ template <typename Char> struct code_unit {
|
||||||
// This ensures that the argument type is convertible to `const T&`.
|
// This ensures that the argument type is convertible to `const T&`.
|
||||||
template <typename T, int N, typename... Args>
|
template <typename T, int N, typename... Args>
|
||||||
constexpr const T& get_arg_checked(const Args&... args) {
|
constexpr const T& get_arg_checked(const Args&... args) {
|
||||||
const auto& arg = get<N>(args...);
|
const auto& arg = detail::get<N>(args...);
|
||||||
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
|
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
|
||||||
return arg.value;
|
return arg.value;
|
||||||
} else {
|
} else {
|
||||||
|
@ -289,7 +290,7 @@ template <typename Char> struct runtime_named_field {
|
||||||
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
bool found = (try_format_argument(out, name, args) || ...);
|
bool found = (try_format_argument(out, name, args) || ...);
|
||||||
if (!found) {
|
if (!found) {
|
||||||
throw format_error("argument with specified name is not found");
|
FMT_THROW(format_error("argument with specified name is not found"));
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -399,7 +400,9 @@ template <typename Char> struct arg_id_handler {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void on_error(const char* message) { throw format_error(message); }
|
constexpr void on_error(const char* message) {
|
||||||
|
FMT_THROW(format_error(message));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char> struct parse_arg_id_result {
|
template <typename Char> struct parse_arg_id_result {
|
||||||
|
@ -451,7 +454,7 @@ constexpr auto compile_format_string(S format_str) {
|
||||||
constexpr auto str = basic_string_view<char_type>(format_str);
|
constexpr auto str = basic_string_view<char_type>(format_str);
|
||||||
if constexpr (str[POS] == '{') {
|
if constexpr (str[POS] == '{') {
|
||||||
if constexpr (POS + 1 == str.size())
|
if constexpr (POS + 1 == str.size())
|
||||||
throw format_error("unmatched '{' in format string");
|
FMT_THROW(format_error("unmatched '{' in format string"));
|
||||||
if constexpr (str[POS + 1] == '{') {
|
if constexpr (str[POS + 1] == '{') {
|
||||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
|
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
|
||||||
|
@ -500,7 +503,7 @@ constexpr auto compile_format_string(S format_str) {
|
||||||
}
|
}
|
||||||
} else if constexpr (str[POS] == '}') {
|
} else if constexpr (str[POS] == '}') {
|
||||||
if constexpr (POS + 1 == str.size())
|
if constexpr (POS + 1 == str.size())
|
||||||
throw format_error("unmatched '}' in format string");
|
FMT_THROW(format_error("unmatched '}' in format string"));
|
||||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
} else {
|
} else {
|
||||||
constexpr auto end = parse_text(str, POS + 1);
|
constexpr auto end = parse_text(str, POS + 1);
|
||||||
|
@ -527,12 +530,12 @@ constexpr auto compile(S format_str) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // __cpp_if_constexpr
|
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
FMT_MODULE_EXPORT_BEGIN
|
FMT_MODULE_EXPORT_BEGIN
|
||||||
|
|
||||||
#ifdef __cpp_if_constexpr
|
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args,
|
template <typename CompiledFormat, typename... Args,
|
||||||
typename Char = typename CompiledFormat::char_type,
|
typename Char = typename CompiledFormat::char_type,
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -40,6 +40,10 @@ FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
|
||||||
std::terminate();
|
std::terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void throw_format_error(const char* message) {
|
||||||
|
FMT_THROW(format_error(message));
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
# define FMT_SNPRINTF snprintf
|
# define FMT_SNPRINTF snprintf
|
||||||
#else // _MSC_VER
|
#else // _MSC_VER
|
||||||
|
@ -145,141 +149,13 @@ template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) {
|
||||||
return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1;
|
return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if __cplusplus < 201703L
|
// log10(2) = 0x0.4d104d427de7fbcc...
|
||||||
template <typename T> constexpr const char basic_data<T>::digits[][2];
|
static constexpr uint64_t log10_2_significand = 0x4d104d427de7fbcc;
|
||||||
template <typename T> constexpr const char basic_data<T>::hex_digits[];
|
|
||||||
template <typename T> constexpr const char basic_data<T>::signs[];
|
|
||||||
template <typename T> constexpr const unsigned basic_data<T>::prefixes[];
|
|
||||||
template <typename T> constexpr const char basic_data<T>::left_padding_shifts[];
|
|
||||||
template <typename T>
|
|
||||||
constexpr const char basic_data<T>::right_padding_shifts[];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <typename T> struct bits {
|
template <typename T = void> struct basic_impl_data {
|
||||||
static FMT_CONSTEXPR_DECL const int value =
|
|
||||||
static_cast<int>(sizeof(T) * std::numeric_limits<unsigned char>::digits);
|
|
||||||
};
|
|
||||||
|
|
||||||
class fp;
|
|
||||||
template <int SHIFT = 0> fp normalize(fp value);
|
|
||||||
|
|
||||||
// Lower (upper) boundary is a value half way between a floating-point value
|
|
||||||
// and its predecessor (successor). Boundaries have the same exponent as the
|
|
||||||
// value so only significands are stored.
|
|
||||||
struct boundaries {
|
|
||||||
uint64_t lower;
|
|
||||||
uint64_t upper;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A handmade floating-point number f * pow(2, e).
|
|
||||||
class fp {
|
|
||||||
private:
|
|
||||||
using significand_type = uint64_t;
|
|
||||||
|
|
||||||
template <typename Float>
|
|
||||||
using is_supported_float = bool_constant<sizeof(Float) == sizeof(uint64_t) ||
|
|
||||||
sizeof(Float) == sizeof(uint32_t)>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
significand_type f;
|
|
||||||
int e;
|
|
||||||
|
|
||||||
// All sizes are in bits.
|
|
||||||
// Subtract 1 to account for an implicit most significant bit in the
|
|
||||||
// normalized form.
|
|
||||||
static FMT_CONSTEXPR_DECL const int double_significand_size =
|
|
||||||
std::numeric_limits<double>::digits - 1;
|
|
||||||
static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
|
|
||||||
1ULL << double_significand_size;
|
|
||||||
static FMT_CONSTEXPR_DECL const int significand_size =
|
|
||||||
bits<significand_type>::value;
|
|
||||||
|
|
||||||
fp() : f(0), e(0) {}
|
|
||||||
fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
|
|
||||||
|
|
||||||
// Constructs fp from an IEEE754 double. It is a template to prevent compile
|
|
||||||
// errors on platforms where double is not IEEE754.
|
|
||||||
template <typename Double> explicit fp(Double d) { assign(d); }
|
|
||||||
|
|
||||||
// Assigns d to this and return true iff predecessor is closer than successor.
|
|
||||||
template <typename Float, FMT_ENABLE_IF(is_supported_float<Float>::value)>
|
|
||||||
bool assign(Float d) {
|
|
||||||
// Assume float is in the format [sign][exponent][significand].
|
|
||||||
using limits = std::numeric_limits<Float>;
|
|
||||||
const int float_significand_size = limits::digits - 1;
|
|
||||||
const int exponent_size =
|
|
||||||
bits<Float>::value - float_significand_size - 1; // -1 for sign
|
|
||||||
const uint64_t float_implicit_bit = 1ULL << float_significand_size;
|
|
||||||
const uint64_t significand_mask = float_implicit_bit - 1;
|
|
||||||
const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask;
|
|
||||||
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
|
|
||||||
constexpr bool is_double = sizeof(Float) == sizeof(uint64_t);
|
|
||||||
auto u = bit_cast<conditional_t<is_double, uint64_t, uint32_t>>(d);
|
|
||||||
f = u & significand_mask;
|
|
||||||
int biased_e =
|
|
||||||
static_cast<int>((u & exponent_mask) >> float_significand_size);
|
|
||||||
// Predecessor is closer if d 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 += float_implicit_bit;
|
|
||||||
else
|
|
||||||
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
|
|
||||||
e = biased_e - exponent_bias - float_significand_size;
|
|
||||||
return is_predecessor_closer;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Float, FMT_ENABLE_IF(!is_supported_float<Float>::value)>
|
|
||||||
bool assign(Float) {
|
|
||||||
*this = fp();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
|
|
||||||
template <int SHIFT> fp normalize(fp value) {
|
|
||||||
// Handle subnormals.
|
|
||||||
const auto shifted_implicit_bit = fp::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::significand_size - fp::double_significand_size - SHIFT - 1;
|
|
||||||
value.f <<= offset;
|
|
||||||
value.e -= offset;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
|
|
||||||
|
|
||||||
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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`.
|
|
||||||
inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
|
|
||||||
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
|
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
|
||||||
// These are generated by support/compute-powers.py.
|
// These are generated by support/compute-powers.py.
|
||||||
static constexpr const uint64_t pow10_significands[] = {
|
static constexpr uint64_t pow10_significands[87] = {
|
||||||
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
|
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
|
||||||
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
|
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
|
||||||
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
|
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
|
||||||
|
@ -311,9 +187,13 @@ inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
|
||||||
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
|
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
|
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
|
||||||
// to significands above.
|
// to significands above.
|
||||||
static constexpr const int16_t pow10_exponents[] = {
|
static constexpr int16_t pow10_exponents[87] = {
|
||||||
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
|
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
|
||||||
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
|
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
|
||||||
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
|
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
|
||||||
|
@ -322,11 +202,137 @@ inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
|
||||||
242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
|
242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
|
||||||
534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
|
534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
|
||||||
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066};
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 int shift = 32;
|
||||||
const auto significand = static_cast<int64_t>(data::log10_2_significand);
|
const auto significand = static_cast<int64_t>(log10_2_significand);
|
||||||
int index = static_cast<int>(
|
int index = static_cast<int>(
|
||||||
((min_exponent + fp::significand_size - 1) * (significand >> shift) +
|
((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) +
|
||||||
((int64_t(1) << shift) - 1)) // ceil
|
((int64_t(1) << shift) - 1)) // ceil
|
||||||
>> 32 // arithmetic shift
|
>> 32 // arithmetic shift
|
||||||
);
|
);
|
||||||
|
@ -336,7 +342,8 @@ inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
|
||||||
const int dec_exp_step = 8;
|
const int dec_exp_step = 8;
|
||||||
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
|
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
|
||||||
pow10_exponent = first_dec_exp + index * dec_exp_step;
|
pow10_exponent = first_dec_exp + index * dec_exp_step;
|
||||||
return {pow10_significands[index], pow10_exponents[index]};
|
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
|
// A simple accumulator to hold the sums of terms in bigint::square if uint128_t
|
||||||
|
@ -345,14 +352,16 @@ struct accumulator {
|
||||||
uint64_t lower;
|
uint64_t lower;
|
||||||
uint64_t upper;
|
uint64_t upper;
|
||||||
|
|
||||||
accumulator() : lower(0), upper(0) {}
|
constexpr accumulator() : lower(0), upper(0) {}
|
||||||
explicit operator uint32_t() const { return static_cast<uint32_t>(lower); }
|
constexpr explicit operator uint32_t() const {
|
||||||
|
return static_cast<uint32_t>(lower);
|
||||||
|
}
|
||||||
|
|
||||||
void operator+=(uint64_t n) {
|
FMT_CONSTEXPR void operator+=(uint64_t n) {
|
||||||
lower += n;
|
lower += n;
|
||||||
if (lower < n) ++upper;
|
if (lower < n) ++upper;
|
||||||
}
|
}
|
||||||
void operator>>=(int shift) {
|
FMT_CONSTEXPR void operator>>=(int shift) {
|
||||||
FMT_ASSERT(shift == 32, "");
|
FMT_ASSERT(shift == 32, "");
|
||||||
(void)shift;
|
(void)shift;
|
||||||
lower = (upper << 32) | (lower >> 32);
|
lower = (upper << 32) | (lower >> 32);
|
||||||
|
@ -370,27 +379,31 @@ class bigint {
|
||||||
basic_memory_buffer<bigit, bigits_capacity> bigits_;
|
basic_memory_buffer<bigit, bigits_capacity> bigits_;
|
||||||
int exp_;
|
int exp_;
|
||||||
|
|
||||||
bigit operator[](int index) const { return bigits_[to_unsigned(index)]; }
|
FMT_CONSTEXPR20 bigit operator[](int index) const {
|
||||||
bigit& operator[](int index) { return bigits_[to_unsigned(index)]; }
|
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;
|
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
|
||||||
|
|
||||||
friend struct formatter<bigint>;
|
friend struct formatter<bigint>;
|
||||||
|
|
||||||
void subtract_bigits(int index, bigit other, bigit& borrow) {
|
FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) {
|
||||||
auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
|
auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
|
||||||
(*this)[index] = static_cast<bigit>(result);
|
(*this)[index] = static_cast<bigit>(result);
|
||||||
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
|
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove_leading_zeros() {
|
FMT_CONSTEXPR20 void remove_leading_zeros() {
|
||||||
int num_bigits = static_cast<int>(bigits_.size()) - 1;
|
int num_bigits = static_cast<int>(bigits_.size()) - 1;
|
||||||
while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
|
while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
|
||||||
bigits_.resize(to_unsigned(num_bigits + 1));
|
bigits_.resize(to_unsigned(num_bigits + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes *this -= other assuming aligned bigints and *this >= other.
|
// Computes *this -= other assuming aligned bigints and *this >= other.
|
||||||
void subtract_aligned(const bigint& other) {
|
FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) {
|
||||||
FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
|
FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
|
||||||
FMT_ASSERT(compare(*this, other) >= 0, "");
|
FMT_ASSERT(compare(*this, other) >= 0, "");
|
||||||
bigit borrow = 0;
|
bigit borrow = 0;
|
||||||
|
@ -401,7 +414,7 @@ class bigint {
|
||||||
remove_leading_zeros();
|
remove_leading_zeros();
|
||||||
}
|
}
|
||||||
|
|
||||||
void multiply(uint32_t value) {
|
FMT_CONSTEXPR20 void multiply(uint32_t value) {
|
||||||
const double_bigit wide_value = value;
|
const double_bigit wide_value = value;
|
||||||
bigit carry = 0;
|
bigit carry = 0;
|
||||||
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
|
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
|
||||||
|
@ -412,7 +425,7 @@ class bigint {
|
||||||
if (carry != 0) bigits_.push_back(carry);
|
if (carry != 0) bigits_.push_back(carry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void multiply(uint64_t value) {
|
FMT_CONSTEXPR20 void multiply(uint64_t value) {
|
||||||
const bigit mask = ~bigit(0);
|
const bigit mask = ~bigit(0);
|
||||||
const double_bigit lower = value & mask;
|
const double_bigit lower = value & mask;
|
||||||
const double_bigit upper = value >> bigit_bits;
|
const double_bigit upper = value >> bigit_bits;
|
||||||
|
@ -430,14 +443,16 @@ class bigint {
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bigint() : exp_(0) {}
|
FMT_CONSTEXPR20 bigint() : exp_(0) {}
|
||||||
explicit bigint(uint64_t n) { assign(n); }
|
explicit bigint(uint64_t n) { assign(n); }
|
||||||
~bigint() { FMT_ASSERT(bigits_.capacity() <= bigits_capacity, ""); }
|
FMT_CONSTEXPR20 ~bigint() {
|
||||||
|
FMT_ASSERT(bigits_.capacity() <= bigits_capacity, "");
|
||||||
|
}
|
||||||
|
|
||||||
bigint(const bigint&) = delete;
|
bigint(const bigint&) = delete;
|
||||||
void operator=(const bigint&) = delete;
|
void operator=(const bigint&) = delete;
|
||||||
|
|
||||||
void assign(const bigint& other) {
|
FMT_CONSTEXPR20 void assign(const bigint& other) {
|
||||||
auto size = other.bigits_.size();
|
auto size = other.bigits_.size();
|
||||||
bigits_.resize(size);
|
bigits_.resize(size);
|
||||||
auto data = other.bigits_.data();
|
auto data = other.bigits_.data();
|
||||||
|
@ -445,7 +460,7 @@ class bigint {
|
||||||
exp_ = other.exp_;
|
exp_ = other.exp_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void assign(uint64_t n) {
|
FMT_CONSTEXPR20 void assign(uint64_t n) {
|
||||||
size_t num_bigits = 0;
|
size_t num_bigits = 0;
|
||||||
do {
|
do {
|
||||||
bigits_[num_bigits++] = n & ~bigit(0);
|
bigits_[num_bigits++] = n & ~bigit(0);
|
||||||
|
@ -455,9 +470,11 @@ class bigint {
|
||||||
exp_ = 0;
|
exp_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
|
FMT_CONSTEXPR20 int num_bigits() const {
|
||||||
|
return static_cast<int>(bigits_.size()) + exp_;
|
||||||
|
}
|
||||||
|
|
||||||
FMT_NOINLINE bigint& operator<<=(int shift) {
|
FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) {
|
||||||
FMT_ASSERT(shift >= 0, "");
|
FMT_ASSERT(shift >= 0, "");
|
||||||
exp_ += shift / bigit_bits;
|
exp_ += shift / bigit_bits;
|
||||||
shift %= bigit_bits;
|
shift %= bigit_bits;
|
||||||
|
@ -472,13 +489,13 @@ class bigint {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Int> bigint& operator*=(Int value) {
|
template <typename Int> FMT_CONSTEXPR20 bigint& operator*=(Int value) {
|
||||||
FMT_ASSERT(value > 0, "");
|
FMT_ASSERT(value > 0, "");
|
||||||
multiply(uint32_or_64_or_128_t<Int>(value));
|
multiply(uint32_or_64_or_128_t<Int>(value));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend int compare(const bigint& lhs, const bigint& rhs) {
|
friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) {
|
||||||
int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
|
int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
|
||||||
if (num_lhs_bigits != num_rhs_bigits)
|
if (num_lhs_bigits != num_rhs_bigits)
|
||||||
return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
|
return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
|
||||||
|
@ -495,8 +512,8 @@ class bigint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns compare(lhs1 + lhs2, rhs).
|
// Returns compare(lhs1 + lhs2, rhs).
|
||||||
friend int add_compare(const bigint& lhs1, const bigint& lhs2,
|
friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2,
|
||||||
const bigint& rhs) {
|
const bigint& rhs) {
|
||||||
int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits());
|
int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits());
|
||||||
int num_rhs_bigits = rhs.num_bigits();
|
int num_rhs_bigits = rhs.num_bigits();
|
||||||
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
|
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
|
||||||
|
@ -519,7 +536,7 @@ class bigint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assigns pow(10, exp) to this bigint.
|
// Assigns pow(10, exp) to this bigint.
|
||||||
void assign_pow10(int exp) {
|
FMT_CONSTEXPR20 void assign_pow10(int exp) {
|
||||||
FMT_ASSERT(exp >= 0, "");
|
FMT_ASSERT(exp >= 0, "");
|
||||||
if (exp == 0) return assign(1);
|
if (exp == 0) return assign(1);
|
||||||
// Find the top bit.
|
// Find the top bit.
|
||||||
|
@ -538,7 +555,7 @@ class bigint {
|
||||||
*this <<= exp; // Multiply by pow(2, exp) by shifting.
|
*this <<= exp; // Multiply by pow(2, exp) by shifting.
|
||||||
}
|
}
|
||||||
|
|
||||||
void square() {
|
FMT_CONSTEXPR20 void square() {
|
||||||
int num_bigits = static_cast<int>(bigits_.size());
|
int num_bigits = static_cast<int>(bigits_.size());
|
||||||
int num_result_bigits = 2 * num_bigits;
|
int num_result_bigits = 2 * num_bigits;
|
||||||
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
|
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
|
||||||
|
@ -569,7 +586,7 @@ class bigint {
|
||||||
|
|
||||||
// If this bigint has a bigger exponent than other, adds trailing zero to make
|
// If this bigint has a bigger exponent than other, adds trailing zero to make
|
||||||
// exponents equal. This simplifies some operations such as subtraction.
|
// exponents equal. This simplifies some operations such as subtraction.
|
||||||
void align(const bigint& other) {
|
FMT_CONSTEXPR20 void align(const bigint& other) {
|
||||||
int exp_difference = exp_ - other.exp_;
|
int exp_difference = exp_ - other.exp_;
|
||||||
if (exp_difference <= 0) return;
|
if (exp_difference <= 0) return;
|
||||||
int num_bigits = static_cast<int>(bigits_.size());
|
int num_bigits = static_cast<int>(bigits_.size());
|
||||||
|
@ -582,7 +599,7 @@ class bigint {
|
||||||
|
|
||||||
// Divides this bignum by divisor, assigning the remainder to this and
|
// Divides this bignum by divisor, assigning the remainder to this and
|
||||||
// returning the quotient.
|
// returning the quotient.
|
||||||
int divmod_assign(const bigint& divisor) {
|
FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) {
|
||||||
FMT_ASSERT(this != &divisor, "");
|
FMT_ASSERT(this != &divisor, "");
|
||||||
if (compare(*this, divisor) < 0) return 0;
|
if (compare(*this, divisor) < 0) return 0;
|
||||||
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
|
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
|
||||||
|
@ -602,8 +619,9 @@ enum class round_direction { unknown, up, down };
|
||||||
// some number v and the error, returns whether v should be rounded up, down, or
|
// 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.
|
// whether the rounding direction can't be determined due to error.
|
||||||
// error should be less than divisor / 2.
|
// error should be less than divisor / 2.
|
||||||
inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder,
|
FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor,
|
||||||
uint64_t error) {
|
uint64_t remainder,
|
||||||
|
uint64_t error) {
|
||||||
FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow.
|
FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow.
|
||||||
FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow.
|
FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow.
|
||||||
FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
|
FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
|
||||||
|
@ -626,19 +644,52 @@ enum result {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint64_t power_of_10_64(int exp) {
|
struct gen_digits_handler {
|
||||||
static constexpr const uint64_t data[] = {1, FMT_POWERS_OF_10(1),
|
char* buf;
|
||||||
FMT_POWERS_OF_10(1000000000ULL),
|
int size;
|
||||||
10000000000000000000ULL};
|
int precision;
|
||||||
return data[exp];
|
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.
|
// Generates output using the Grisu digit-gen algorithm.
|
||||||
// error: the size of the region (lower, upper) outside of which numbers
|
// error: the size of the region (lower, upper) outside of which numbers
|
||||||
// definitely do not round to value (Delta in Grisu3).
|
// definitely do not round to value (Delta in Grisu3).
|
||||||
template <typename Handler>
|
FMT_INLINE FMT_CONSTEXPR20 digits::result grisu_gen_digits(
|
||||||
FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
|
fp value, uint64_t error, int& exp, gen_digits_handler& handler) {
|
||||||
Handler& handler) {
|
|
||||||
const fp one(1ULL << -value.e, value.e);
|
const fp one(1ULL << -value.e, value.e);
|
||||||
// The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
|
// 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
|
// zero because it contains a product of two 64-bit numbers with MSB set (due
|
||||||
|
@ -649,10 +700,28 @@ FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
|
||||||
// The fractional part of scaled value (p2 in Grisu) c = value % one.
|
// The fractional part of scaled value (p2 in Grisu) c = value % one.
|
||||||
uint64_t fractional = value.f & (one.f - 1);
|
uint64_t fractional = value.f & (one.f - 1);
|
||||||
exp = count_digits(integral); // kappa in Grisu.
|
exp = count_digits(integral); // kappa in Grisu.
|
||||||
// Divide by 10 to prevent overflow.
|
// Non-fixed formats require at least one digit and no precision adjustment.
|
||||||
auto result = handler.on_start(power_of_10_64(exp - 1) << -one.e,
|
if (handler.fixed) {
|
||||||
value.f / 10, error * 10, exp);
|
// Adjust fixed precision by exponent because it is relative to decimal
|
||||||
if (result != digits::more) return result;
|
// 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.
|
// Generate digits for the integral part. This can produce up to 10 digits.
|
||||||
do {
|
do {
|
||||||
uint32_t digit = 0;
|
uint32_t digit = 0;
|
||||||
|
@ -699,9 +768,9 @@ FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
|
||||||
}
|
}
|
||||||
--exp;
|
--exp;
|
||||||
auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
|
auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
|
||||||
result = handler.on_digit(static_cast<char>('0' + digit),
|
auto result = handler.on_digit(static_cast<char>('0' + digit),
|
||||||
power_of_10_64(exp) << -one.e, remainder, error,
|
impl_data::power_of_10_64[exp] << -one.e,
|
||||||
exp, true);
|
remainder, error, true);
|
||||||
if (result != digits::more) return result;
|
if (result != digits::more) return result;
|
||||||
} while (exp > 0);
|
} while (exp > 0);
|
||||||
// Generate digits for the fractional part.
|
// Generate digits for the fractional part.
|
||||||
|
@ -711,69 +780,11 @@ FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
|
||||||
char digit = static_cast<char>('0' + (fractional >> -one.e));
|
char digit = static_cast<char>('0' + (fractional >> -one.e));
|
||||||
fractional &= one.f - 1;
|
fractional &= one.f - 1;
|
||||||
--exp;
|
--exp;
|
||||||
result = handler.on_digit(digit, one.f, fractional, error, exp, false);
|
auto result = handler.on_digit(digit, one.f, fractional, error, false);
|
||||||
if (result != digits::more) return result;
|
if (result != digits::more) return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The fixed precision digit handler.
|
|
||||||
struct fixed_handler {
|
|
||||||
char* buf;
|
|
||||||
int size;
|
|
||||||
int precision;
|
|
||||||
int exp10;
|
|
||||||
bool fixed;
|
|
||||||
|
|
||||||
digits::result on_start(uint64_t divisor, uint64_t remainder, uint64_t error,
|
|
||||||
int& exp) {
|
|
||||||
// Non-fixed formats require at least one digit and no precision adjustment.
|
|
||||||
if (!fixed) return digits::more;
|
|
||||||
// Adjust fixed precision by exponent because it is relative to decimal
|
|
||||||
// point.
|
|
||||||
precision += exp + exp10;
|
|
||||||
// Check if precision is satisfied just by leading zeros, e.g.
|
|
||||||
// format("{:.2f}", 0.001) gives "0.00" without generating any digits.
|
|
||||||
if (precision > 0) return digits::more;
|
|
||||||
if (precision < 0) return digits::done;
|
|
||||||
auto dir = get_round_direction(divisor, remainder, error);
|
|
||||||
if (dir == round_direction::unknown) return digits::error;
|
|
||||||
buf[size++] = dir == round_direction::up ? '1' : '0';
|
|
||||||
return digits::done;
|
|
||||||
}
|
|
||||||
|
|
||||||
digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder,
|
|
||||||
uint64_t error, int, 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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// A 128-bit integer type used internally,
|
// A 128-bit integer type used internally,
|
||||||
struct uint128_wrapper {
|
struct uint128_wrapper {
|
||||||
uint128_wrapper() = default;
|
uint128_wrapper() = default;
|
||||||
|
@ -897,8 +908,7 @@ inline uint64_t umul96_lower64(uint32_t x, uint64_t y) FMT_NOEXCEPT {
|
||||||
inline int floor_log10_pow2(int e) FMT_NOEXCEPT {
|
inline int floor_log10_pow2(int e) FMT_NOEXCEPT {
|
||||||
FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent");
|
FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent");
|
||||||
const int shift = 22;
|
const int shift = 22;
|
||||||
return (e * static_cast<int>(data::log10_2_significand >> (64 - shift))) >>
|
return (e * static_cast<int>(log10_2_significand >> (64 - shift))) >> shift;
|
||||||
shift;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Various fast log computations.
|
// Various fast log computations.
|
||||||
|
@ -916,8 +926,7 @@ inline int floor_log10_pow2_minus_log10_4_over_3(int e) FMT_NOEXCEPT {
|
||||||
FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent");
|
FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent");
|
||||||
const uint64_t log10_4_over_3_fractional_digits = 0x1ffbfc2bbc780375;
|
const uint64_t log10_4_over_3_fractional_digits = 0x1ffbfc2bbc780375;
|
||||||
const int shift_amount = 22;
|
const int shift_amount = 22;
|
||||||
return (e * static_cast<int>(data::log10_2_significand >>
|
return (e * static_cast<int>(log10_2_significand >> (64 - shift_amount)) -
|
||||||
(64 - shift_amount)) -
|
|
||||||
static_cast<int>(log10_4_over_3_fractional_digits >>
|
static_cast<int>(log10_4_over_3_fractional_digits >>
|
||||||
(64 - shift_amount))) >>
|
(64 - shift_amount))) >>
|
||||||
shift_amount;
|
shift_amount;
|
||||||
|
@ -1042,7 +1051,7 @@ template <> struct cache_accessor<float> {
|
||||||
static uint64_t get_cached_power(int k) FMT_NOEXCEPT {
|
static uint64_t get_cached_power(int k) FMT_NOEXCEPT {
|
||||||
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
|
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
|
||||||
"k is out of range");
|
"k is out of range");
|
||||||
constexpr const uint64_t pow10_significands[] = {
|
static constexpr const uint64_t pow10_significands[] = {
|
||||||
0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
|
0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
|
||||||
0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
|
0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
|
||||||
0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
|
0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
|
||||||
|
@ -2210,24 +2219,21 @@ small_divisor_case_label:
|
||||||
}
|
}
|
||||||
} // namespace dragonbox
|
} // namespace dragonbox
|
||||||
|
|
||||||
// Formats value using a variation of the Fixed-Precision Positive
|
// Formats a floating-point number using a variation of the Fixed-Precision
|
||||||
// Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
|
// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
|
||||||
// https://fmt.dev/papers/p372-steele.pdf.
|
// https://fmt.dev/papers/p372-steele.pdf.
|
||||||
template <typename Double>
|
FMT_CONSTEXPR20 inline void format_dragon(fp value, bool is_predecessor_closer,
|
||||||
void fallback_format(Double d, int num_digits, bool binary32, buffer<char>& buf,
|
int num_digits, buffer<char>& buf,
|
||||||
int& exp10) {
|
int& exp10) {
|
||||||
bigint numerator; // 2 * R in (FPP)^2.
|
bigint numerator; // 2 * R in (FPP)^2.
|
||||||
bigint denominator; // 2 * S in (FPP)^2.
|
bigint denominator; // 2 * S in (FPP)^2.
|
||||||
// lower and upper are differences between value and corresponding boundaries.
|
// lower and upper are differences between value and corresponding boundaries.
|
||||||
bigint lower; // (M^- in (FPP)^2).
|
bigint lower; // (M^- in (FPP)^2).
|
||||||
bigint upper_store; // upper's value if different from lower.
|
bigint upper_store; // upper's value if different from lower.
|
||||||
bigint* upper = nullptr; // (M^+ in (FPP)^2).
|
bigint* upper = nullptr; // (M^+ in (FPP)^2).
|
||||||
fp value;
|
|
||||||
// Shift numerator and denominator by an extra bit or two (if lower boundary
|
// Shift numerator and denominator by an extra bit or two (if lower boundary
|
||||||
// is closer) to make lower and upper integers. This eliminates multiplication
|
// is closer) to make lower and upper integers. This eliminates multiplication
|
||||||
// by 2 during later computations.
|
// by 2 during later computations.
|
||||||
const bool is_predecessor_closer =
|
|
||||||
binary32 ? value.assign(static_cast<float>(d)) : value.assign(d);
|
|
||||||
int shift = is_predecessor_closer ? 2 : 1;
|
int shift = is_predecessor_closer ? 2 : 1;
|
||||||
uint64_t significand = value.f << shift;
|
uint64_t significand = value.f << shift;
|
||||||
if (value.e >= 0) {
|
if (value.e >= 0) {
|
||||||
|
@ -2297,9 +2303,9 @@ void fallback_format(Double d, int num_digits, bool binary32, buffer<char>& buf,
|
||||||
// Generate the given number of digits.
|
// Generate the given number of digits.
|
||||||
exp10 -= num_digits - 1;
|
exp10 -= num_digits - 1;
|
||||||
if (num_digits == 0) {
|
if (num_digits == 0) {
|
||||||
buf.try_resize(1);
|
|
||||||
denominator *= 10;
|
denominator *= 10;
|
||||||
buf[0] = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
|
auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
|
||||||
|
buf.push_back(digit);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
buf.try_resize(to_unsigned(num_digits));
|
buf.try_resize(to_unsigned(num_digits));
|
||||||
|
@ -2330,9 +2336,12 @@ void fallback_format(Double d, int num_digits, bool binary32, buffer<char>& buf,
|
||||||
buf[num_digits - 1] = static_cast<char>('0' + digit);
|
buf[num_digits - 1] = static_cast<char>('0' + digit);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename Float>
|
||||||
int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision,
|
||||||
static_assert(!std::is_same<T, float>::value, "");
|
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");
|
FMT_ASSERT(value >= 0, "value is negative");
|
||||||
|
|
||||||
const bool fixed = specs.format == float_format::fixed;
|
const bool fixed = specs.format == float_format::fixed;
|
||||||
|
@ -2342,13 +2351,13 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
buf.try_resize(to_unsigned(precision));
|
buf.try_resize(to_unsigned(precision));
|
||||||
std::uninitialized_fill_n(buf.data(), precision, '0');
|
fill_n(buf.data(), precision, '0');
|
||||||
return -precision;
|
return -precision;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf);
|
if (specs.fallback) return snprintf_float(value, precision, specs, buf);
|
||||||
|
|
||||||
if (precision < 0) {
|
if (!is_constant_evaluated() && precision < 0) {
|
||||||
// Use Dragonbox for the shortest format.
|
// Use Dragonbox for the shortest format.
|
||||||
if (specs.binary32) {
|
if (specs.binary32) {
|
||||||
auto dec = dragonbox::to_decimal(static_cast<float>(value));
|
auto dec = dragonbox::to_decimal(static_cast<float>(value));
|
||||||
|
@ -2360,26 +2369,37 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
||||||
return dec.exponent;
|
return dec.exponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use Grisu + Dragon4 for the given precision:
|
|
||||||
// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
|
|
||||||
int exp = 0;
|
int exp = 0;
|
||||||
const int min_exp = -60; // alpha in Grisu.
|
bool use_dragon = true;
|
||||||
int cached_exp10 = 0; // K in Grisu.
|
if (is_fast_float<Float>()) {
|
||||||
fp normalized = normalize(fp(value));
|
// Use Grisu + Dragon4 for the given precision:
|
||||||
const auto cached_pow = get_cached_power(
|
// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
|
||||||
min_exp - (normalized.e + fp::significand_size), cached_exp10);
|
const int min_exp = -60; // alpha in Grisu.
|
||||||
normalized = normalized * cached_pow;
|
int cached_exp10 = 0; // K in Grisu.
|
||||||
// Limit precision to the maximum possible number of significant digits in an
|
fp normalized = normalize(fp(value));
|
||||||
// IEEE754 double because we don't need to generate zeros.
|
const auto cached_pow = get_cached_power(
|
||||||
const int max_double_digits = 767;
|
min_exp - (normalized.e + fp::num_significand_bits), cached_exp10);
|
||||||
if (precision > max_double_digits) precision = max_double_digits;
|
normalized = normalized * cached_pow;
|
||||||
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
|
gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
|
||||||
if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) {
|
if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error &&
|
||||||
exp += handler.size - cached_exp10 - 1;
|
!is_constant_evaluated()) {
|
||||||
fallback_format(value, handler.precision, specs.binary32, buf, exp);
|
exp += handler.exp10;
|
||||||
} else {
|
buf.try_resize(to_unsigned(handler.size));
|
||||||
exp += handler.exp10;
|
use_dragon = false;
|
||||||
buf.try_resize(to_unsigned(handler.size));
|
} 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) {
|
if (!fixed && !specs.showpoint) {
|
||||||
// Remove trailing zeros.
|
// Remove trailing zeros.
|
||||||
|
@ -2391,7 +2411,7 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
||||||
buf.try_resize(num_digits);
|
buf.try_resize(num_digits);
|
||||||
}
|
}
|
||||||
return exp;
|
return exp;
|
||||||
} // namespace detail
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
int snprintf_float(T value, int precision, float_specs specs,
|
int snprintf_float(T value, int precision, float_specs specs,
|
||||||
|
@ -2525,8 +2545,8 @@ template <> struct formatter<detail::bigint> {
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
||||||
for_each_codepoint(s, [this](uint32_t cp, int error) {
|
for_each_codepoint(s, [this](uint32_t cp, string_view) {
|
||||||
if (error != 0) FMT_THROW(std::runtime_error("invalid utf8"));
|
if (cp == invalid_code_point) FMT_THROW(std::runtime_error("invalid utf8"));
|
||||||
if (cp <= 0xFFFF) {
|
if (cp <= 0xFFFF) {
|
||||||
buffer_.push_back(static_cast<wchar_t>(cp));
|
buffer_.push_back(static_cast<wchar_t>(cp));
|
||||||
} else {
|
} else {
|
||||||
|
@ -2534,6 +2554,7 @@ FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
||||||
buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
|
buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
|
||||||
buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
|
buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
buffer_.push_back(0);
|
buffer_.push_back(0);
|
||||||
}
|
}
|
||||||
|
@ -2549,15 +2570,17 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
|
||||||
format_error_code(out, error_code, message);
|
format_error_code(out, error_code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC void detail::error_handler::on_error(const char* message) {
|
|
||||||
FMT_THROW(format_error(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC void report_system_error(int error_code,
|
FMT_FUNC void report_system_error(int error_code,
|
||||||
const char* message) FMT_NOEXCEPT {
|
const char* message) FMT_NOEXCEPT {
|
||||||
report_error(format_system_error, error_code, message);
|
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) {
|
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
|
// Don't optimize the "{}" case to keep the binary size small and because it
|
||||||
// can be better optimized in fmt::format anyway.
|
// can be better optimized in fmt::format anyway.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -21,17 +21,20 @@
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
|
#ifndef FMT_USE_FCNTL
|
||||||
// UWP doesn't provide _pipe.
|
// UWP doesn't provide _pipe.
|
||||||
#if FMT_HAS_INCLUDE("winapifamily.h")
|
# if FMT_HAS_INCLUDE("winapifamily.h")
|
||||||
# include <winapifamily.h>
|
# include <winapifamily.h>
|
||||||
#endif
|
# endif
|
||||||
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||||
defined(__linux__)) && \
|
defined(__linux__)) && \
|
||||||
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
(!defined(WINAPI_FAMILY) || \
|
||||||
# include <fcntl.h> // for O_RDONLY
|
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||||
# define FMT_USE_FCNTL 1
|
# include <fcntl.h> // for O_RDONLY
|
||||||
#else
|
# define FMT_USE_FCNTL 1
|
||||||
# define FMT_USE_FCNTL 0
|
# else
|
||||||
|
# define FMT_USE_FCNTL 0
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_POSIX
|
#ifndef FMT_POSIX
|
||||||
|
@ -390,23 +393,26 @@ struct ostream_params {
|
||||||
: ostream_params(params...) {
|
: ostream_params(params...) {
|
||||||
this->buffer_size = bs.value;
|
this->buffer_size = bs.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Intel has a bug that results in failure to deduce a constructor
|
||||||
|
// for empty parameter packs.
|
||||||
|
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
|
||||||
|
ostream_params(int new_oflag) : oflag(new_oflag) {}
|
||||||
|
ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
|
||||||
|
# endif
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_END_DETAIL_NAMESPACE
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
constexpr detail::buffer_size buffer_size;
|
// Added {} below to work around default constructor error known to
|
||||||
|
// occur in Xcode versions 7.2.1 and 8.2.1.
|
||||||
|
constexpr detail::buffer_size buffer_size{};
|
||||||
|
|
||||||
/** A fast output stream which is not thread-safe. */
|
/** A fast output stream which is not thread-safe. */
|
||||||
class FMT_API ostream final : private detail::buffer<char> {
|
class FMT_API ostream final : private detail::buffer<char> {
|
||||||
private:
|
private:
|
||||||
file file_;
|
file file_;
|
||||||
|
|
||||||
void flush() {
|
|
||||||
if (size() == 0) return;
|
|
||||||
file_.write(data(), size());
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void grow(size_t) override;
|
void grow(size_t) override;
|
||||||
|
|
||||||
ostream(cstring_view path, const detail::ostream_params& params)
|
ostream(cstring_view path, const detail::ostream_params& params)
|
||||||
|
@ -426,6 +432,12 @@ class FMT_API ostream final : private detail::buffer<char> {
|
||||||
delete[] data();
|
delete[] data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void flush() {
|
||||||
|
if (size() == 0) return;
|
||||||
|
file_.write(data(), size());
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
friend ostream output_file(cstring_view path, T... params);
|
friend ostream output_file(cstring_view path, T... params);
|
||||||
|
|
||||||
|
@ -500,7 +512,7 @@ class locale {
|
||||||
|
|
||||||
// Converts string to floating-point number and advances str past the end
|
// Converts string to floating-point number and advances str past the end
|
||||||
// of the parsed input.
|
// of the parsed input.
|
||||||
double strtod(const char*& str) const {
|
FMT_DEPRECATED double strtod(const char*& str) const {
|
||||||
char* end = nullptr;
|
char* end = nullptr;
|
||||||
double result = strtod_l(str, &end, locale_);
|
double result = strtod_l(str, &end, locale_);
|
||||||
str = end;
|
str = end;
|
||||||
|
|
|
@ -14,73 +14,20 @@
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
template <typename Char> class basic_printf_parse_context;
|
|
||||||
template <typename OutputIt, typename Char> class basic_printf_context;
|
template <typename OutputIt, typename Char> class basic_printf_context;
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
// Checks if T has a user-defined operator<<.
|
||||||
private:
|
template <typename T, typename Char, typename Enable = void>
|
||||||
using int_type = typename std::basic_streambuf<Char>::int_type;
|
class is_streamable {
|
||||||
using traits_type = typename std::basic_streambuf<Char>::traits_type;
|
|
||||||
|
|
||||||
buffer<Char>& buffer_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// The put-area is actually always empty. This makes the implementation
|
|
||||||
// simpler and has the advantage that the streambuf and the buffer are always
|
|
||||||
// in sync and sputc never writes into uninitialized memory. The obvious
|
|
||||||
// disadvantage is that each call to sputc always results in a (virtual) call
|
|
||||||
// to overflow. There is no disadvantage here for sputn since this always
|
|
||||||
// results in a call to xsputn.
|
|
||||||
|
|
||||||
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
|
|
||||||
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
|
||||||
buffer_.push_back(static_cast<Char>(ch));
|
|
||||||
return ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
|
|
||||||
buffer_.append(s, s + count);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct converter {
|
|
||||||
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
|
||||||
private:
|
|
||||||
void_t<> operator<<(converter);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Hide insertion operators for built-in types.
|
|
||||||
template <typename Char, typename Traits>
|
|
||||||
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
|
|
||||||
template <typename Char, typename Traits>
|
|
||||||
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
|
|
||||||
template <typename Traits>
|
|
||||||
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
|
|
||||||
template <typename Traits>
|
|
||||||
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
|
|
||||||
template <typename Traits>
|
|
||||||
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
|
|
||||||
|
|
||||||
// Checks if T has a user-defined operator<< (e.g. not a member of
|
|
||||||
// std::ostream).
|
|
||||||
template <typename T, typename Char> class is_streamable {
|
|
||||||
private:
|
private:
|
||||||
template <typename U>
|
template <typename U>
|
||||||
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
|
static auto test(int)
|
||||||
<< std::declval<U>()),
|
-> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
|
||||||
void_t<>>::value>
|
<< std::declval<U>()) != 0>;
|
||||||
test(int);
|
|
||||||
|
|
||||||
template <typename> static std::false_type test(...);
|
template <typename> static auto test(...) -> std::false_type;
|
||||||
|
|
||||||
using result = decltype(test<T>(0));
|
using result = decltype(test<T>(0));
|
||||||
|
|
||||||
|
@ -90,7 +37,21 @@ template <typename T, typename Char> class is_streamable {
|
||||||
static const bool value = result::value;
|
static const bool value = result::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Formatting of built-in types and arrays is intentionally disabled because
|
||||||
|
// it's handled by standard (non-ostream) formatters.
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct is_streamable<
|
||||||
|
T, Char,
|
||||||
|
enable_if_t<
|
||||||
|
std::is_arithmetic<T>::value || std::is_array<T>::value ||
|
||||||
|
std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
|
||||||
|
std::is_same<T, std::basic_string<Char>>::value ||
|
||||||
|
std::is_same<T, std_string_view<Char>>::value ||
|
||||||
|
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
|
||||||
|
: std::false_type {};
|
||||||
|
|
||||||
// Write the content of buf to os.
|
// Write the content of buf to os.
|
||||||
|
// It is a separate function rather than a part of vprint to simplify testing.
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||||
const Char* buf_data = buf.data();
|
const Char* buf_data = buf.data();
|
||||||
|
@ -108,8 +69,8 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||||
template <typename Char, typename T>
|
template <typename Char, typename T>
|
||||||
void format_value(buffer<Char>& buf, const T& value,
|
void format_value(buffer<Char>& buf, const T& value,
|
||||||
locale_ref loc = locale_ref()) {
|
locale_ref loc = locale_ref()) {
|
||||||
formatbuf<Char> format_buf(buf);
|
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
|
||||||
std::basic_ostream<Char> output(&format_buf);
|
auto&& output = std::basic_ostream<Char>(&format_buf);
|
||||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
if (loc) output.imbue(loc.get<std::locale>());
|
if (loc) output.imbue(loc.get<std::locale>());
|
||||||
#endif
|
#endif
|
||||||
|
@ -122,29 +83,22 @@ void format_value(buffer<Char>& buf, const T& value,
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||||
: private formatter<basic_string_view<Char>, Char> {
|
: private formatter<basic_string_view<Char>, Char> {
|
||||||
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
using formatter<basic_string_view<Char>, Char>::parse;
|
||||||
-> decltype(ctx.begin()) {
|
|
||||||
return formatter<basic_string_view<Char>, Char>::parse(ctx);
|
|
||||||
}
|
|
||||||
template <typename ParseCtx,
|
|
||||||
FMT_ENABLE_IF(std::is_same<
|
|
||||||
ParseCtx, basic_printf_parse_context<Char>>::value)>
|
|
||||||
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt>
|
template <typename OutputIt>
|
||||||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
|
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
|
||||||
-> OutputIt {
|
-> OutputIt {
|
||||||
basic_memory_buffer<Char> buffer;
|
auto buffer = basic_memory_buffer<Char>();
|
||||||
format_value(buffer, value, ctx.locale());
|
format_value(buffer, value, ctx.locale());
|
||||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
return formatter<basic_string_view<Char>, Char>::format(
|
||||||
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
{buffer.data(), buffer.size()}, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEPRECATED!
|
||||||
template <typename OutputIt>
|
template <typename OutputIt>
|
||||||
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
|
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
|
||||||
-> OutputIt {
|
-> OutputIt {
|
||||||
basic_memory_buffer<Char> buffer;
|
auto buffer = basic_memory_buffer<Char>();
|
||||||
format_value(buffer, value, ctx.locale());
|
format_value(buffer, value, ctx.locale());
|
||||||
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
||||||
}
|
}
|
||||||
|
@ -155,7 +109,7 @@ FMT_MODULE_EXPORT
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
auto buffer = basic_memory_buffer<Char>();
|
||||||
detail::vformat_to(buffer, format_str, args);
|
detail::vformat_to(buffer, format_str, args);
|
||||||
detail::write_buffer(os, buffer);
|
detail::write_buffer(os, buffer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,7 +233,7 @@ class printf_arg_formatter : public arg_formatter<Char> {
|
||||||
|
|
||||||
OutputIt write_null_pointer(bool is_string = false) {
|
OutputIt write_null_pointer(bool is_string = false) {
|
||||||
auto s = this->specs;
|
auto s = this->specs;
|
||||||
s.type = 0;
|
s.type = presentation_type::none;
|
||||||
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
|
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,8 +249,10 @@ class printf_arg_formatter : public arg_formatter<Char> {
|
||||||
// std::is_same instead.
|
// std::is_same instead.
|
||||||
if (std::is_same<T, Char>::value) {
|
if (std::is_same<T, Char>::value) {
|
||||||
format_specs fmt_specs = this->specs;
|
format_specs fmt_specs = this->specs;
|
||||||
if (fmt_specs.type && fmt_specs.type != 'c')
|
if (fmt_specs.type != presentation_type::none &&
|
||||||
|
fmt_specs.type != presentation_type::chr) {
|
||||||
return (*this)(static_cast<int>(value));
|
return (*this)(static_cast<int>(value));
|
||||||
|
}
|
||||||
fmt_specs.sign = sign::none;
|
fmt_specs.sign = sign::none;
|
||||||
fmt_specs.alt = false;
|
fmt_specs.alt = false;
|
||||||
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
|
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
|
||||||
|
@ -271,13 +273,13 @@ class printf_arg_formatter : public arg_formatter<Char> {
|
||||||
/** Formats a null-terminated C string. */
|
/** Formats a null-terminated C string. */
|
||||||
OutputIt operator()(const char* value) {
|
OutputIt operator()(const char* value) {
|
||||||
if (value) return base::operator()(value);
|
if (value) return base::operator()(value);
|
||||||
return write_null_pointer(this->specs.type != 'p');
|
return write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Formats a null-terminated wide C string. */
|
/** Formats a null-terminated wide C string. */
|
||||||
OutputIt operator()(const wchar_t* value) {
|
OutputIt operator()(const wchar_t* value) {
|
||||||
if (value) return base::operator()(value);
|
if (value) return base::operator()(value);
|
||||||
return write_null_pointer(this->specs.type != 'p');
|
return write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputIt operator()(basic_string_view<Char> value) {
|
OutputIt operator()(basic_string_view<Char> value) {
|
||||||
|
@ -490,13 +492,13 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||||
|
|
||||||
// Parse type.
|
// Parse type.
|
||||||
if (it == end) FMT_THROW(format_error("invalid format string"));
|
if (it == end) FMT_THROW(format_error("invalid format string"));
|
||||||
specs.type = static_cast<char>(*it++);
|
char type = static_cast<char>(*it++);
|
||||||
if (arg.is_integral()) {
|
if (arg.is_integral()) {
|
||||||
// Normalize type.
|
// Normalize type.
|
||||||
switch (specs.type) {
|
switch (type) {
|
||||||
case 'i':
|
case 'i':
|
||||||
case 'u':
|
case 'u':
|
||||||
specs.type = 'd';
|
type = 'd';
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
visit_format_arg(
|
visit_format_arg(
|
||||||
|
@ -505,6 +507,9 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
specs.type = parse_presentation_type(type);
|
||||||
|
if (specs.type == presentation_type::none)
|
||||||
|
parse_ctx.on_error("invalid type specifier");
|
||||||
|
|
||||||
start = it;
|
start = it;
|
||||||
|
|
||||||
|
|
|
@ -13,37 +13,13 @@
|
||||||
#define FMT_RANGES_H_
|
#define FMT_RANGES_H_
|
||||||
|
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
#include <tuple>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
template <typename Char, typename Enable = void> struct formatting_range {
|
|
||||||
#ifdef FMT_DEPRECATED_BRACED_RANGES
|
|
||||||
Char prefix = '{';
|
|
||||||
Char postfix = '}';
|
|
||||||
#else
|
|
||||||
Char prefix = '[';
|
|
||||||
Char postfix = ']';
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <typename ParseContext>
|
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename Enable = void> struct formatting_tuple {
|
|
||||||
Char prefix = '(';
|
|
||||||
Char postfix = ')';
|
|
||||||
|
|
||||||
template <typename ParseContext>
|
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template <typename RangeT, typename OutputIterator>
|
template <typename RangeT, typename OutputIterator>
|
||||||
|
@ -71,7 +47,7 @@ OutputIterator copy(wchar_t ch, OutputIterator out) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true value if T has std::string interface, like std::string_view.
|
// Returns true if T has a std::string-like interface, like std::string_view.
|
||||||
template <typename T> class is_std_string_like {
|
template <typename T> class is_std_string_like {
|
||||||
template <typename U>
|
template <typename U>
|
||||||
static auto check(U* p)
|
static auto check(U* p)
|
||||||
|
@ -80,12 +56,40 @@ template <typename T> class is_std_string_like {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static FMT_CONSTEXPR_DECL const bool value =
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
|
is_string<T>::value ||
|
||||||
|
std::is_convertible<T, std_string_view<char>>::value ||
|
||||||
|
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
|
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T> class is_map {
|
||||||
|
template <typename U> static auto check(U*) -> typename U::mapped_type;
|
||||||
|
template <typename> static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef FMT_FORMAT_MAP_AS_LIST
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value = false;
|
||||||
|
#else
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> class is_set {
|
||||||
|
template <typename U> static auto check(U*) -> typename U::key_type;
|
||||||
|
template <typename> static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef FMT_FORMAT_SET_AS_LIST
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value = false;
|
||||||
|
#else
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
template <typename... Ts> struct conditional_helper {};
|
template <typename... Ts> struct conditional_helper {};
|
||||||
|
|
||||||
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
||||||
|
@ -143,16 +147,16 @@ struct has_mutable_begin_end : std::false_type {};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct has_const_begin_end<
|
struct has_const_begin_end<
|
||||||
T, void_t<decltype(detail::range_begin(
|
T,
|
||||||
std::declval<const remove_cvref_t<T>&>())),
|
void_t<
|
||||||
decltype(detail::range_begin(
|
decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
|
||||||
std::declval<const remove_cvref_t<T>&>()))>>
|
decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
|
||||||
: std::true_type {};
|
: std::true_type {};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct has_mutable_begin_end<
|
struct has_mutable_begin_end<
|
||||||
T, void_t<decltype(detail::range_begin(std::declval<T>())),
|
T, void_t<decltype(detail::range_begin(std::declval<T>())),
|
||||||
decltype(detail::range_begin(std::declval<T>())),
|
decltype(detail::range_end(std::declval<T>())),
|
||||||
enable_if_t<std::is_copy_constructible<T>::value>>>
|
enable_if_t<std::is_copy_constructible<T>::value>>>
|
||||||
: std::true_type {};
|
: std::true_type {};
|
||||||
|
|
||||||
|
@ -160,34 +164,10 @@ template <typename T>
|
||||||
struct is_range_<T, void>
|
struct is_range_<T, void>
|
||||||
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
|
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
|
||||||
has_mutable_begin_end<T>::value)> {};
|
has_mutable_begin_end<T>::value)> {};
|
||||||
|
|
||||||
template <typename T, typename Enable = void> struct range_to_view;
|
|
||||||
template <typename T>
|
|
||||||
struct range_to_view<T, enable_if_t<has_const_begin_end<T>::value>> {
|
|
||||||
struct view_t {
|
|
||||||
const T* m_range_ptr;
|
|
||||||
|
|
||||||
auto begin() const FMT_DECLTYPE_RETURN(detail::range_begin(*m_range_ptr));
|
|
||||||
auto end() const FMT_DECLTYPE_RETURN(detail::range_end(*m_range_ptr));
|
|
||||||
};
|
|
||||||
static auto view(const T& range) -> view_t { return {&range}; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct range_to_view<T, enable_if_t<!has_const_begin_end<T>::value &&
|
|
||||||
has_mutable_begin_end<T>::value>> {
|
|
||||||
struct view_t {
|
|
||||||
T m_range_copy;
|
|
||||||
|
|
||||||
auto begin() FMT_DECLTYPE_RETURN(detail::range_begin(m_range_copy));
|
|
||||||
auto end() FMT_DECLTYPE_RETURN(detail::range_end(m_range_copy));
|
|
||||||
};
|
|
||||||
static auto view(const T& range) -> view_t { return {range}; }
|
|
||||||
};
|
|
||||||
# undef FMT_DECLTYPE_RETURN
|
# undef FMT_DECLTYPE_RETURN
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// tuple_size and tuple_element check.
|
// tuple_size and tuple_element check.
|
||||||
template <typename T> class is_tuple_like_ {
|
template <typename T> class is_tuple_like_ {
|
||||||
template <typename U>
|
template <typename U>
|
||||||
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
||||||
|
@ -241,9 +221,28 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
||||||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if FMT_MSC_VER
|
||||||
|
// Older MSVC doesn't get the reference type correctly for arrays.
|
||||||
|
template <typename R> struct range_reference_type_impl {
|
||||||
|
using type = decltype(*detail::range_begin(std::declval<R&>()));
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
|
||||||
|
using type = T&;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using range_reference_type = typename range_reference_type_impl<T>::type;
|
||||||
|
#else
|
||||||
template <typename Range>
|
template <typename Range>
|
||||||
using value_type =
|
using range_reference_type =
|
||||||
remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>;
|
decltype(*detail::range_begin(std::declval<Range&>()));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// We don't use the Range's value_type for anything, but we do need the Range's
|
||||||
|
// reference type, with cv-ref stripped.
|
||||||
|
template <typename Range>
|
||||||
|
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
|
||||||
|
|
||||||
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
|
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
|
||||||
*out++ = ',';
|
*out++ = ',';
|
||||||
|
@ -251,16 +250,295 @@ template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
struct singleton {
|
||||||
typename Char, typename OutputIt, typename Arg,
|
unsigned char upper;
|
||||||
FMT_ENABLE_IF(is_std_string_like<typename std::decay<Arg>::type>::value)>
|
unsigned char lower_count;
|
||||||
OutputIt write_range_entry(OutputIt out, const Arg& v) {
|
};
|
||||||
|
|
||||||
|
inline auto is_printable(uint16_t x, const singleton* singletons,
|
||||||
|
size_t singletons_size,
|
||||||
|
const unsigned char* singleton_lowers,
|
||||||
|
const unsigned char* normal, size_t normal_size)
|
||||||
|
-> bool {
|
||||||
|
auto upper = x >> 8;
|
||||||
|
auto lower_start = 0;
|
||||||
|
for (size_t i = 0; i < singletons_size; ++i) {
|
||||||
|
auto s = singletons[i];
|
||||||
|
auto lower_end = lower_start + s.lower_count;
|
||||||
|
if (upper < s.upper) break;
|
||||||
|
if (upper == s.upper) {
|
||||||
|
for (auto j = lower_start; j < lower_end; ++j) {
|
||||||
|
if (singleton_lowers[j] == (x & 0xff)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lower_start = lower_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xsigned = static_cast<int>(x);
|
||||||
|
auto current = true;
|
||||||
|
for (size_t i = 0; i < normal_size; ++i) {
|
||||||
|
auto v = static_cast<int>(normal[i]);
|
||||||
|
auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v;
|
||||||
|
xsigned -= len;
|
||||||
|
if (xsigned < 0) break;
|
||||||
|
current = !current;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true iff the code point cp is printable.
|
||||||
|
// This code is generated by support/printable.py.
|
||||||
|
inline auto is_printable(uint32_t cp) -> bool {
|
||||||
|
static constexpr singleton singletons0[] = {
|
||||||
|
{0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8},
|
||||||
|
{0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13},
|
||||||
|
{0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5},
|
||||||
|
{0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22},
|
||||||
|
{0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3},
|
||||||
|
{0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8},
|
||||||
|
{0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9},
|
||||||
|
};
|
||||||
|
static constexpr unsigned char singletons0_lower[] = {
|
||||||
|
0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90,
|
||||||
|
0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f,
|
||||||
|
0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
|
||||||
|
0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04,
|
||||||
|
0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d,
|
||||||
|
0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
|
||||||
|
0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
|
||||||
|
0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d,
|
||||||
|
0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
|
||||||
|
0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d,
|
||||||
|
0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5,
|
||||||
|
0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
|
||||||
|
0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49,
|
||||||
|
0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7,
|
||||||
|
0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
|
||||||
|
0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e,
|
||||||
|
0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16,
|
||||||
|
0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
|
||||||
|
0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f,
|
||||||
|
0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf,
|
||||||
|
0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
|
||||||
|
0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27,
|
||||||
|
0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91,
|
||||||
|
0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
|
||||||
|
0xfe, 0xff,
|
||||||
|
};
|
||||||
|
static constexpr singleton singletons1[] = {
|
||||||
|
{0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2},
|
||||||
|
{0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5},
|
||||||
|
{0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5},
|
||||||
|
{0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2},
|
||||||
|
{0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5},
|
||||||
|
{0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2},
|
||||||
|
{0xfa, 2}, {0xfb, 1},
|
||||||
|
};
|
||||||
|
static constexpr unsigned char singletons1_lower[] = {
|
||||||
|
0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07,
|
||||||
|
0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36,
|
||||||
|
0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
|
||||||
|
0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
|
||||||
|
0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b,
|
||||||
|
0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
|
||||||
|
0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66,
|
||||||
|
0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27,
|
||||||
|
0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
|
||||||
|
0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7,
|
||||||
|
0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6,
|
||||||
|
0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
|
||||||
|
0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66,
|
||||||
|
0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0,
|
||||||
|
0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
|
||||||
|
};
|
||||||
|
static constexpr unsigned char normal0[] = {
|
||||||
|
0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04,
|
||||||
|
0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0,
|
||||||
|
0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
|
||||||
|
0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03,
|
||||||
|
0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03,
|
||||||
|
0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
|
||||||
|
0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15,
|
||||||
|
0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f,
|
||||||
|
0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
|
||||||
|
0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07,
|
||||||
|
0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06,
|
||||||
|
0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
|
||||||
|
0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac,
|
||||||
|
0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c,
|
||||||
|
0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
|
||||||
|
0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c,
|
||||||
|
0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b,
|
||||||
|
0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
|
||||||
|
0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03,
|
||||||
|
0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80,
|
||||||
|
0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
|
||||||
|
0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c,
|
||||||
|
0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17,
|
||||||
|
0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
|
||||||
|
0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80,
|
||||||
|
0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d,
|
||||||
|
};
|
||||||
|
static constexpr unsigned char normal1[] = {
|
||||||
|
0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f,
|
||||||
|
0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e,
|
||||||
|
0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
|
||||||
|
0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09,
|
||||||
|
0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16,
|
||||||
|
0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
|
||||||
|
0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36,
|
||||||
|
0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33,
|
||||||
|
0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
|
||||||
|
0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e,
|
||||||
|
0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41,
|
||||||
|
0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
|
||||||
|
0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22,
|
||||||
|
0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04,
|
||||||
|
0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
|
||||||
|
0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03,
|
||||||
|
0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81,
|
||||||
|
0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
|
||||||
|
0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1,
|
||||||
|
0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a,
|
||||||
|
0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
|
||||||
|
0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09,
|
||||||
|
0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89,
|
||||||
|
0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
|
||||||
|
0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09,
|
||||||
|
0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50,
|
||||||
|
0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
|
||||||
|
0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83,
|
||||||
|
0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05,
|
||||||
|
0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
|
||||||
|
0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80,
|
||||||
|
0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07,
|
||||||
|
0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
|
||||||
|
0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07,
|
||||||
|
0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06,
|
||||||
|
};
|
||||||
|
auto lower = static_cast<uint16_t>(cp);
|
||||||
|
if (cp < 0x10000) {
|
||||||
|
return is_printable(lower, singletons0,
|
||||||
|
sizeof(singletons0) / sizeof(*singletons0),
|
||||||
|
singletons0_lower, normal0, sizeof(normal0));
|
||||||
|
}
|
||||||
|
if (cp < 0x20000) {
|
||||||
|
return is_printable(lower, singletons1,
|
||||||
|
sizeof(singletons1) / sizeof(*singletons1),
|
||||||
|
singletons1_lower, normal1, sizeof(normal1));
|
||||||
|
}
|
||||||
|
if (0x2a6de <= cp && cp < 0x2a700) return false;
|
||||||
|
if (0x2b735 <= cp && cp < 0x2b740) return false;
|
||||||
|
if (0x2b81e <= cp && cp < 0x2b820) return false;
|
||||||
|
if (0x2cea2 <= cp && cp < 0x2ceb0) return false;
|
||||||
|
if (0x2ebe1 <= cp && cp < 0x2f800) return false;
|
||||||
|
if (0x2fa1e <= cp && cp < 0x30000) return false;
|
||||||
|
if (0x3134b <= cp && cp < 0xe0100) return false;
|
||||||
|
if (0xe01f0 <= cp && cp < 0x110000) return false;
|
||||||
|
return cp < 0x110000;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto needs_escape(uint32_t cp) -> bool {
|
||||||
|
return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
|
||||||
|
!is_printable(cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char> struct find_escape_result {
|
||||||
|
const Char* begin;
|
||||||
|
const Char* end;
|
||||||
|
uint32_t cp;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
auto find_escape(const Char* begin, const Char* end)
|
||||||
|
-> find_escape_result<Char> {
|
||||||
|
for (; begin != end; ++begin) {
|
||||||
|
auto cp = static_cast<typename std::make_unsigned<Char>::type>(*begin);
|
||||||
|
if (sizeof(Char) == 1 && cp >= 0x80) continue;
|
||||||
|
if (needs_escape(cp)) return {begin, begin + 1, cp};
|
||||||
|
}
|
||||||
|
return {begin, nullptr, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto find_escape(const char* begin, const char* end)
|
||||||
|
-> find_escape_result<char> {
|
||||||
|
if (!is_utf8()) return find_escape<char>(begin, end);
|
||||||
|
auto result = find_escape_result<char>{end, nullptr, 0};
|
||||||
|
for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
|
||||||
|
[&](uint32_t cp, string_view sv) {
|
||||||
|
if (needs_escape(cp)) {
|
||||||
|
result = {sv.begin(), sv.end(), cp};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename OutputIt>
|
||||||
|
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
|
||||||
*out++ = '"';
|
*out++ = '"';
|
||||||
out = write<Char>(out, v);
|
auto begin = str.begin(), end = str.end();
|
||||||
|
do {
|
||||||
|
auto escape = find_escape(begin, end);
|
||||||
|
out = copy_str<Char>(begin, escape.begin, out);
|
||||||
|
begin = escape.end;
|
||||||
|
if (!begin) break;
|
||||||
|
auto c = static_cast<Char>(escape.cp);
|
||||||
|
switch (escape.cp) {
|
||||||
|
case '\n':
|
||||||
|
*out++ = '\\';
|
||||||
|
c = 'n';
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
*out++ = '\\';
|
||||||
|
c = 'r';
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
*out++ = '\\';
|
||||||
|
c = 't';
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
FMT_FALLTHROUGH;
|
||||||
|
case '\\':
|
||||||
|
*out++ = '\\';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (is_utf8()) {
|
||||||
|
if (escape.cp < 0x100) {
|
||||||
|
out = format_to(out, "\\x{:02x}", escape.cp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (escape.cp < 0x10000) {
|
||||||
|
out = format_to(out, "\\u{:04x}", escape.cp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (escape.cp < 0x110000) {
|
||||||
|
out = format_to(out, "\\U{:08x}", escape.cp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Char escape_char : basic_string_view<Char>(
|
||||||
|
escape.begin, to_unsigned(escape.end - escape.begin))) {
|
||||||
|
out = format_to(
|
||||||
|
out, "\\x{:02x}",
|
||||||
|
static_cast<typename std::make_unsigned<Char>::type>(escape_char));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*out++ = c;
|
||||||
|
} while (begin != end);
|
||||||
*out++ = '"';
|
*out++ = '"';
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename OutputIt, typename T,
|
||||||
|
FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
|
||||||
|
inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
|
||||||
|
auto sv = std_string_view<Char>(str);
|
||||||
|
return write_range_entry<Char>(out, basic_string_view<Char>(sv));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char, typename OutputIt, typename Arg,
|
template <typename Char, typename OutputIt, typename Arg,
|
||||||
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
|
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
|
||||||
OutputIt write_range_entry(OutputIt out, const Arg v) {
|
OutputIt write_range_entry(OutputIt out, const Arg v) {
|
||||||
|
@ -288,78 +566,169 @@ template <typename T> struct is_tuple_like {
|
||||||
template <typename TupleT, typename Char>
|
template <typename TupleT, typename Char>
|
||||||
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||||
private:
|
private:
|
||||||
// C++11 generic lambda for format()
|
// C++11 generic lambda for format().
|
||||||
template <typename FormatContext> struct format_each {
|
template <typename FormatContext> struct format_each {
|
||||||
template <typename T> void operator()(const T& v) {
|
template <typename T> void operator()(const T& v) {
|
||||||
if (i > 0) out = detail::write_delimiter(out);
|
if (i > 0) out = detail::write_delimiter(out);
|
||||||
out = detail::write_range_entry<Char>(out, v);
|
out = detail::write_range_entry<Char>(out, v);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
formatting_tuple<Char>& formatting;
|
int i;
|
||||||
size_t& i;
|
typename FormatContext::iterator& out;
|
||||||
typename std::add_lvalue_reference<
|
|
||||||
decltype(std::declval<FormatContext>().out())>::type out;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
formatting_tuple<Char> formatting;
|
|
||||||
|
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
return formatting.parse(ctx);
|
return ctx.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext = format_context>
|
template <typename FormatContext = format_context>
|
||||||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
auto out = ctx.out();
|
auto out = ctx.out();
|
||||||
size_t i = 0;
|
*out++ = '(';
|
||||||
|
detail::for_each(values, format_each<FormatContext>{0, out});
|
||||||
detail::copy(formatting.prefix, out);
|
*out++ = ')';
|
||||||
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
|
return out;
|
||||||
detail::copy(formatting.postfix, out);
|
|
||||||
|
|
||||||
return ctx.out();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename Char> struct is_range {
|
template <typename T, typename Char> struct is_range {
|
||||||
static FMT_CONSTEXPR_DECL const bool value =
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
|
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
|
||||||
|
!detail::is_map<T>::value &&
|
||||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||||
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename Context, typename Element> struct range_mapper {
|
||||||
|
using mapper = arg_mapper<Context>;
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
|
||||||
|
static auto map(T&& value) -> T&& {
|
||||||
|
return static_cast<T&&>(value);
|
||||||
|
}
|
||||||
|
template <typename T,
|
||||||
|
FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
|
||||||
|
static auto map(T&& value)
|
||||||
|
-> decltype(mapper().map(static_cast<T&&>(value))) {
|
||||||
|
return mapper().map(static_cast<T&&>(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Element>
|
||||||
|
using range_formatter_type =
|
||||||
|
conditional_t<is_formattable<Element, Char>::value,
|
||||||
|
formatter<remove_cvref_t<decltype(
|
||||||
|
range_mapper<buffer_context<Char>, Element>{}
|
||||||
|
.map(std::declval<Element>()))>,
|
||||||
|
Char>,
|
||||||
|
fallback_formatter<Element, Char>>;
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename R, typename Char>
|
||||||
|
struct formatter<
|
||||||
|
R, Char,
|
||||||
|
enable_if_t<fmt::is_range<R, Char>::value
|
||||||
|
// Workaround a bug in MSVC 2019 and earlier.
|
||||||
|
#if !FMT_MSC_VER
|
||||||
|
&& (is_formattable<detail::uncvref_type<R>, Char>::value ||
|
||||||
|
detail::has_fallback_formatter<detail::uncvref_type<R>,
|
||||||
|
Char>::value)
|
||||||
|
#endif
|
||||||
|
>> {
|
||||||
|
|
||||||
|
using formatter_type =
|
||||||
|
detail::range_formatter_type<Char, detail::uncvref_type<R>>;
|
||||||
|
formatter_type underlying_;
|
||||||
|
bool custom_specs_ = false;
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
auto it = ctx.begin();
|
||||||
|
auto end = ctx.end();
|
||||||
|
if (it == end || *it == '}') return it;
|
||||||
|
|
||||||
|
if (*it != ':')
|
||||||
|
FMT_THROW(format_error("no top-level range formatters supported"));
|
||||||
|
|
||||||
|
custom_specs_ = true;
|
||||||
|
++it;
|
||||||
|
ctx.advance_to(it);
|
||||||
|
return underlying_.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename FormatContext, typename U,
|
||||||
|
FMT_ENABLE_IF(
|
||||||
|
std::is_same<U, conditional_t<detail::has_const_begin_end<R>::value,
|
||||||
|
const R, R>>::value)>
|
||||||
|
auto format(U& range, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
|
#ifdef FMT_DEPRECATED_BRACED_RANGES
|
||||||
|
Char prefix = '{';
|
||||||
|
Char postfix = '}';
|
||||||
|
#else
|
||||||
|
Char prefix = detail::is_set<R>::value ? '{' : '[';
|
||||||
|
Char postfix = detail::is_set<R>::value ? '}' : ']';
|
||||||
|
#endif
|
||||||
|
detail::range_mapper<buffer_context<Char>, detail::uncvref_type<R>> mapper;
|
||||||
|
auto out = ctx.out();
|
||||||
|
*out++ = prefix;
|
||||||
|
int i = 0;
|
||||||
|
auto it = std::begin(range);
|
||||||
|
auto end = std::end(range);
|
||||||
|
for (; it != end; ++it) {
|
||||||
|
if (i > 0) out = detail::write_delimiter(out);
|
||||||
|
if (custom_specs_) {
|
||||||
|
ctx.advance_to(out);
|
||||||
|
out = underlying_.format(mapper.map(*it), ctx);
|
||||||
|
} else {
|
||||||
|
out = detail::write_range_entry<Char>(out, *it);
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
*out++ = postfix;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct formatter<
|
struct formatter<
|
||||||
T, Char,
|
T, Char,
|
||||||
enable_if_t<
|
enable_if_t<detail::is_map<T>::value
|
||||||
fmt::is_range<T, Char>::value
|
// Workaround a bug in MSVC 2019 and earlier.
|
||||||
// Workaround a bug in MSVC 2017 and earlier.
|
#if !FMT_MSC_VER
|
||||||
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
|
&& (is_formattable<detail::uncvref_type<T>, Char>::value ||
|
||||||
&& (has_formatter<detail::value_type<T>, format_context>::value ||
|
detail::has_fallback_formatter<detail::uncvref_type<T>,
|
||||||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
|
Char>::value)
|
||||||
#endif
|
#endif
|
||||||
>> {
|
>> {
|
||||||
formatting_range<Char> formatting;
|
|
||||||
|
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
return formatting.parse(ctx);
|
return ctx.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <
|
||||||
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
|
typename FormatContext, typename U,
|
||||||
auto out = detail::copy(formatting.prefix, ctx.out());
|
FMT_ENABLE_IF(
|
||||||
size_t i = 0;
|
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
|
||||||
auto view = detail::range_to_view<T>::view(values);
|
const T, T>>::value)>
|
||||||
auto it = view.begin();
|
auto format(U& map, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
auto end = view.end();
|
auto out = ctx.out();
|
||||||
for (; it != end; ++it) {
|
*out++ = '{';
|
||||||
|
int i = 0;
|
||||||
|
for (const auto& item : map) {
|
||||||
if (i > 0) out = detail::write_delimiter(out);
|
if (i > 0) out = detail::write_delimiter(out);
|
||||||
out = detail::write_range_entry<Char>(out, *it);
|
out = detail::write_range_entry<Char>(out, item.first);
|
||||||
|
*out++ = ':';
|
||||||
|
*out++ = ' ';
|
||||||
|
out = detail::write_range_entry<Char>(out, item.second);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
return detail::copy(formatting.postfix, out);
|
*out++ = '}';
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -374,46 +743,70 @@ template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
||||||
template <typename Char, typename... T>
|
template <typename Char, typename... T>
|
||||||
using tuple_arg_join = tuple_join_view<Char, T...>;
|
using tuple_arg_join = tuple_join_view<Char, T...>;
|
||||||
|
|
||||||
|
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
|
||||||
|
// support in tuple_join. It is disabled by default because of issues with
|
||||||
|
// the dynamic width and precision.
|
||||||
|
#ifndef FMT_TUPLE_JOIN_SPECIFIERS
|
||||||
|
# define FMT_TUPLE_JOIN_SPECIFIERS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
template <typename Char, typename... T>
|
template <typename Char, typename... T>
|
||||||
struct formatter<tuple_join_view<Char, T...>, Char> {
|
struct formatter<tuple_join_view<Char, T...>, Char> {
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
return ctx.begin();
|
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx) ->
|
auto format(const tuple_join_view<Char, T...>& value,
|
||||||
typename FormatContext::iterator {
|
FormatContext& ctx) const -> typename FormatContext::iterator {
|
||||||
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
|
return do_format(value, ctx,
|
||||||
|
std::integral_constant<size_t, sizeof...(T)>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename FormatContext, size_t... N>
|
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
|
||||||
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
|
||||||
detail::index_sequence<N...>) ->
|
template <typename ParseContext>
|
||||||
typename FormatContext::iterator {
|
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||||
using std::get;
|
std::integral_constant<size_t, 0>)
|
||||||
return format_args(value, ctx, get<N>(value.tuple)...);
|
-> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ParseContext, size_t N>
|
||||||
|
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||||
|
std::integral_constant<size_t, N>)
|
||||||
|
-> decltype(ctx.begin()) {
|
||||||
|
auto end = ctx.begin();
|
||||||
|
#if FMT_TUPLE_JOIN_SPECIFIERS
|
||||||
|
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
|
||||||
|
if (N > 1) {
|
||||||
|
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
|
||||||
|
if (end != end1)
|
||||||
|
FMT_THROW(format_error("incompatible format specs for tuple elements"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format_args(const tuple_join_view<Char, T...>&, FormatContext& ctx) ->
|
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
|
||||||
|
std::integral_constant<size_t, 0>) const ->
|
||||||
typename FormatContext::iterator {
|
typename FormatContext::iterator {
|
||||||
// NOTE: for compilers that support C++17, this empty function instantiation
|
|
||||||
// can be replaced with a constexpr branch in the variadic overload.
|
|
||||||
return ctx.out();
|
return ctx.out();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext, typename Arg, typename... Args>
|
template <typename FormatContext, size_t N>
|
||||||
auto format_args(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
||||||
const Arg& arg, const Args&... args) ->
|
std::integral_constant<size_t, N>) const ->
|
||||||
typename FormatContext::iterator {
|
typename FormatContext::iterator {
|
||||||
using base = formatter<typename std::decay<Arg>::type, Char>;
|
auto out = std::get<sizeof...(T) - N>(formatters_)
|
||||||
auto out = base().format(arg, ctx);
|
.format(std::get<sizeof...(T) - N>(value.tuple), ctx);
|
||||||
if (sizeof...(Args) > 0) {
|
if (N > 1) {
|
||||||
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||||
ctx.advance_to(out);
|
ctx.advance_to(out);
|
||||||
return format_args(value, ctx, args...);
|
return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
//
|
//
|
||||||
// For the license information refer to format.h.
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
#ifndef FMT_WCHAR_H_
|
#ifndef FMT_XCHAR_H_
|
||||||
#define FMT_WCHAR_H_
|
#define FMT_XCHAR_H_
|
||||||
|
|
||||||
#include <cwchar>
|
#include <cwchar>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
@ -217,11 +217,11 @@ inline void vprint(wstring_view fmt, wformat_args args) {
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
|
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
|
||||||
return vprint(f, wstring_view(fmt), make_wformat_args(args...));
|
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
|
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
|
||||||
return vprint(wstring_view(fmt), make_wformat_args(args...));
|
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -233,4 +233,4 @@ template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
|
||||||
FMT_MODULE_EXPORT_END
|
FMT_MODULE_EXPORT_END
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_WCHAR_H_
|
#endif // FMT_XCHAR_H_
|
||||||
|
|
|
@ -79,7 +79,6 @@ export module fmt;
|
||||||
#define FMT_END_DETAIL_NAMESPACE \
|
#define FMT_END_DETAIL_NAMESPACE \
|
||||||
} \
|
} \
|
||||||
export {
|
export {
|
||||||
|
|
||||||
// all library-provided declarations and definitions
|
// all library-provided declarations and definitions
|
||||||
// must be in the module purview to be exported
|
// must be in the module purview to be exported
|
||||||
#include "fmt/args.h"
|
#include "fmt/args.h"
|
||||||
|
|
|
@ -10,6 +10,52 @@
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
// DEPRECATED!
|
||||||
|
template <typename T = void> struct basic_data {
|
||||||
|
FMT_API static constexpr const char digits[100][2] = {
|
||||||
|
{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'},
|
||||||
|
{'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'},
|
||||||
|
{'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'},
|
||||||
|
{'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'},
|
||||||
|
{'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
|
||||||
|
{'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'},
|
||||||
|
{'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'},
|
||||||
|
{'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'},
|
||||||
|
{'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'},
|
||||||
|
{'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
|
||||||
|
{'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'},
|
||||||
|
{'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'},
|
||||||
|
{'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'},
|
||||||
|
{'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'},
|
||||||
|
{'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
|
||||||
|
{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'},
|
||||||
|
{'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}};
|
||||||
|
FMT_API static constexpr const char hex_digits[] = "0123456789abcdef";
|
||||||
|
FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '};
|
||||||
|
FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1,
|
||||||
|
0};
|
||||||
|
FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1,
|
||||||
|
0};
|
||||||
|
FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
|
||||||
|
0x1000000u | ' '};
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef FMT_SHARED
|
||||||
|
// Required for -flto, -fivisibility=hidden and -shared to work
|
||||||
|
extern template struct basic_data<void>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __cplusplus < 201703L
|
||||||
|
// DEPRECATED! These are here only for ABI compatiblity.
|
||||||
|
template <typename T> constexpr const char basic_data<T>::digits[][2];
|
||||||
|
template <typename T> constexpr const char basic_data<T>::hex_digits[];
|
||||||
|
template <typename T> constexpr const char basic_data<T>::signs[];
|
||||||
|
template <typename T> constexpr const char basic_data<T>::left_padding_shifts[];
|
||||||
|
template <typename T>
|
||||||
|
constexpr const char basic_data<T>::right_padding_shifts[];
|
||||||
|
template <typename T> constexpr const unsigned basic_data<T>::prefixes[];
|
||||||
|
#endif
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
int format_float(char* buf, std::size_t size, const char* format, int precision,
|
int format_float(char* buf, std::size_t size, const char* format, int precision,
|
||||||
T value) {
|
T value) {
|
||||||
|
|
|
@ -26,19 +26,17 @@
|
||||||
# endif
|
# endif
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
|
|
||||||
# define O_CREAT _O_CREAT
|
|
||||||
# define O_TRUNC _O_TRUNC
|
|
||||||
|
|
||||||
# ifndef S_IRUSR
|
# ifndef S_IRUSR
|
||||||
# define S_IRUSR _S_IREAD
|
# define S_IRUSR _S_IREAD
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# ifndef S_IWUSR
|
# ifndef S_IWUSR
|
||||||
# define S_IWUSR _S_IWRITE
|
# define S_IWUSR _S_IWRITE
|
||||||
# endif
|
# endif
|
||||||
|
# ifndef S_IRGRP
|
||||||
# ifdef __MINGW32__
|
# define S_IRGRP 0
|
||||||
# define _SH_DENYNO 0x40
|
# endif
|
||||||
|
# ifndef S_IROTH
|
||||||
|
# define S_IROTH 0
|
||||||
# endif
|
# endif
|
||||||
# endif // _WIN32
|
# endif // _WIN32
|
||||||
#endif // FMT_USE_FCNTL
|
#endif // FMT_USE_FCNTL
|
||||||
|
@ -213,7 +211,10 @@ int buffered_file::fileno() const {
|
||||||
|
|
||||||
#if FMT_USE_FCNTL
|
#if FMT_USE_FCNTL
|
||||||
file::file(cstring_view path, int oflag) {
|
file::file(cstring_view path, int oflag) {
|
||||||
int mode = S_IRUSR | S_IWUSR;
|
# ifdef _WIN32
|
||||||
|
using mode_t = int;
|
||||||
|
# endif
|
||||||
|
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
fd_ = -1;
|
fd_ = -1;
|
||||||
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
|
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
|
||||||
|
|
|
@ -240,7 +240,7 @@ def release(args):
|
||||||
# Update the version in the changelog.
|
# Update the version in the changelog.
|
||||||
title_len = 0
|
title_len = 0
|
||||||
for line in fileinput.input(changelog_path, inplace=True):
|
for line in fileinput.input(changelog_path, inplace=True):
|
||||||
if line.decode('utf-8').startswith(version + ' - TBD'):
|
if line.startswith(version + ' - TBD'):
|
||||||
line = version + ' - ' + datetime.date.today().isoformat()
|
line = version + ' - ' + datetime.date.today().isoformat()
|
||||||
title_len = len(line)
|
title_len = len(line)
|
||||||
line += '\n'
|
line += '\n'
|
||||||
|
@ -270,9 +270,9 @@ def release(args):
|
||||||
|
|
||||||
# Create a release on GitHub.
|
# Create a release on GitHub.
|
||||||
fmt_repo.push('origin', 'release')
|
fmt_repo.push('origin', 'release')
|
||||||
params = {'access_token': os.getenv('FMT_TOKEN')}
|
auth_headers = {'Authorization': 'token ' + os.getenv('FMT_TOKEN')}
|
||||||
r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases',
|
r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases',
|
||||||
params=params,
|
headers=auth_headers,
|
||||||
data=json.dumps({'tag_name': version,
|
data=json.dumps({'tag_name': version,
|
||||||
'target_commitish': 'release',
|
'target_commitish': 'release',
|
||||||
'body': changes, 'draft': True}))
|
'body': changes, 'draft': True}))
|
||||||
|
@ -283,8 +283,8 @@ def release(args):
|
||||||
package = 'fmt-{}.zip'.format(version)
|
package = 'fmt-{}.zip'.format(version)
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
'{}/{}/assets?name={}'.format(uploads_url, id, package),
|
'{}/{}/assets?name={}'.format(uploads_url, id, package),
|
||||||
headers={'Content-Type': 'application/zip'},
|
headers={'Content-Type': 'application/zip'} | auth_headers,
|
||||||
params=params, data=open('build/fmt/' + package, 'rb'))
|
data=open('build/fmt/' + package, 'rb'))
|
||||||
if r.status_code != 201:
|
if r.status_code != 201:
|
||||||
raise Exception('Failed to upload an asset ' + str(r))
|
raise Exception('Failed to upload an asset ' + str(r))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue