@@ -117,6 +117,7 @@ INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/" | |||
"${CMAKE_SOURCE_DIR}/contrib/lc-btrie" | |||
"${CMAKE_SOURCE_DIR}/contrib/lua-lpeg" | |||
"${CMAKE_SOURCE_DIR}/contrib/frozen/include" | |||
"${CMAKE_SOURCE_DIR}/contrib/fmt/include" | |||
"${CMAKE_BINARY_DIR}/src" #Stored in the binary dir | |||
"${CMAKE_BINARY_DIR}/src/libcryptobox") | |||
@@ -650,7 +651,7 @@ ADD_SUBDIRECTORY(contrib/libev) | |||
ADD_SUBDIRECTORY(contrib/kann) | |||
ADD_SUBDIRECTORY(contrib/fastutf8) | |||
ADD_SUBDIRECTORY(contrib/google-ced) | |||
ADD_SUBDIRECTORY(contrib/fmt) | |||
IF (NOT WITH_LUAJIT) | |||
ADD_SUBDIRECTORY(contrib/lua-bit) |
@@ -31,3 +31,4 @@ | |||
| expected | v1.0 | Public Domain / CC0 | NO | | | |||
| robin-hood | 3.9.1 | MIT | NO | | | |||
| frozen | 1.0.1 | Apache 2 | NO | | | |||
| fmt | 7.1.3 | MIT | NO | | |
@@ -0,0 +1,316 @@ | |||
cmake_minimum_required(VERSION 3.1...3.18) | |||
# Fallback for using newer policies on CMake <3.12. | |||
if(${CMAKE_VERSION} VERSION_LESS 3.12) | |||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) | |||
endif() | |||
# Determine if fmt is built as a subproject (using add_subdirectory) | |||
# or if it is the master project. | |||
set(MASTER_PROJECT OFF) | |||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) | |||
set(MASTER_PROJECT ON) | |||
message(STATUS "CMake version: ${CMAKE_VERSION}") | |||
endif () | |||
# Joins arguments and places the results in ${result_var}. | |||
function(join result_var) | |||
set(result ) | |||
foreach (arg ${ARGN}) | |||
set(result "${result}${arg}") | |||
endforeach () | |||
set(${result_var} "${result}" PARENT_SCOPE) | |||
endfunction() | |||
include(CMakeParseArguments) | |||
# Sets a cache variable with a docstring joined from multiple arguments: | |||
# set(<variable> <value>... CACHE <type> <docstring>...) | |||
# This allows splitting a long docstring for readability. | |||
function(set_verbose) | |||
# cmake_parse_arguments is broken in CMake 3.4 (cannot parse CACHE) so use | |||
# list instead. | |||
list(GET ARGN 0 var) | |||
list(REMOVE_AT ARGN 0) | |||
list(GET ARGN 0 val) | |||
list(REMOVE_AT ARGN 0) | |||
list(REMOVE_AT ARGN 0) | |||
list(GET ARGN 0 type) | |||
list(REMOVE_AT ARGN 0) | |||
join(doc ${ARGN}) | |||
set(${var} ${val} CACHE ${type} ${doc}) | |||
endfunction() | |||
# Set the default CMAKE_BUILD_TYPE to Release. | |||
# This should be done before the project command since the latter can set | |||
# CMAKE_BUILD_TYPE itself (it does so for nmake). | |||
if (MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE) | |||
set_verbose(CMAKE_BUILD_TYPE Release CACHE STRING | |||
"Choose the type of build, options are: None(CMAKE_CXX_FLAGS or " | |||
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") | |||
endif () | |||
project(FMT CXX) | |||
include(GNUInstallDirs) | |||
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING | |||
"Installation directory for include files, a relative path that " | |||
"will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute path.") | |||
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF) | |||
option(FMT_WERROR "Halt the compilation with an error on compiler warnings." | |||
OFF) | |||
# Options that control generation of various targets. | |||
option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT}) | |||
option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT}) | |||
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT}) | |||
option(FMT_FUZZ "Generate the fuzz target." OFF) | |||
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF) | |||
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON) | |||
# Get version from core.h | |||
file(READ include/fmt/core.h core_h) | |||
if (NOT core_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])") | |||
message(FATAL_ERROR "Cannot get FMT_VERSION from core.h.") | |||
endif () | |||
# Use math to skip leading zeros if any. | |||
math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1}) | |||
math(EXPR CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2}) | |||
math(EXPR CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3}) | |||
join(FMT_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}. | |||
${CPACK_PACKAGE_VERSION_PATCH}) | |||
message(STATUS "Version: ${FMT_VERSION}") | |||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") | |||
if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) | |||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) | |||
endif () | |||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") | |||
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic | |||
-Wold-style-cast -Wundef | |||
-Wredundant-decls -Wwrite-strings -Wpointer-arith | |||
-Wcast-qual -Wformat=2 -Wmissing-include-dirs | |||
-Wcast-align | |||
-Wctor-dtor-privacy -Wdisabled-optimization | |||
-Winvalid-pch -Woverloaded-virtual | |||
-Wconversion -Wswitch-enum -Wundef | |||
-Wno-ctor-dtor-privacy -Wno-format-nonliteral) | |||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6) | |||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept | |||
-Wno-dangling-else -Wno-unused-local-typedefs) | |||
endif () | |||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) | |||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion | |||
-Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast | |||
-Wvector-operation-performance -Wsized-deallocation) | |||
endif () | |||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) | |||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2 | |||
-Wnull-dereference -Wduplicated-cond) | |||
endif () | |||
set(WERROR_FLAG -Werror) | |||
endif () | |||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") | |||
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wundef | |||
-Wdeprecated -Wweak-vtables) | |||
set(WERROR_FLAG -Werror) | |||
endif () | |||
if (MSVC) | |||
set(PEDANTIC_COMPILE_FLAGS /W3) | |||
set(WERROR_FLAG /WX) | |||
endif () | |||
set(strtod_l_headers stdlib.h) | |||
if (APPLE) | |||
set(strtod_l_headers ${strtod_l_headers} xlocale.h) | |||
endif () | |||
include(CheckSymbolExists) | |||
if (WIN32) | |||
check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L) | |||
else () | |||
check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L) | |||
endif () | |||
function(add_headers VAR) | |||
set(headers ${${VAR}}) | |||
foreach (header ${ARGN}) | |||
set(headers ${headers} include/fmt/${header}) | |||
endforeach() | |||
set(${VAR} ${headers} PARENT_SCOPE) | |||
endfunction() | |||
# Define the fmt library, its includes and the needed defines. | |||
add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h | |||
locale.h os.h ostream.h posix.h printf.h ranges.h) | |||
if (FMT_OS) | |||
set(FMT_SOURCES src/format.cc src/os.cc) | |||
else() | |||
set(FMT_SOURCES src/format.cc) | |||
endif () | |||
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst) | |||
add_library(fmt::fmt ALIAS fmt) | |||
if (HAVE_STRTOD_L) | |||
target_compile_definitions(fmt PUBLIC FMT_LOCALE) | |||
endif () | |||
if (FMT_WERROR) | |||
target_compile_options(fmt PRIVATE ${WERROR_FLAG}) | |||
endif () | |||
if (FMT_PEDANTIC) | |||
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS}) | |||
endif () | |||
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES}) | |||
target_include_directories(fmt PUBLIC | |||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> | |||
$<INSTALL_INTERFACE:${FMT_INC_DIR}>) | |||
set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.") | |||
set_target_properties(fmt PROPERTIES | |||
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR} | |||
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}") | |||
# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target | |||
# property because it's not set by default. | |||
set(FMT_LIB_NAME fmt) | |||
if (CMAKE_BUILD_TYPE STREQUAL "Debug") | |||
set(FMT_LIB_NAME ${FMT_LIB_NAME}${FMT_DEBUG_POSTFIX}) | |||
endif () | |||
if (BUILD_SHARED_LIBS) | |||
if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" AND | |||
NOT EMSCRIPTEN) | |||
# Fix rpmlint warning: | |||
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6. | |||
target_link_libraries(fmt -Wl,--as-needed) | |||
endif () | |||
target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED) | |||
endif () | |||
if (FMT_SAFE_DURATION_CAST) | |||
target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST) | |||
endif() | |||
add_library(fmt-header-only INTERFACE) | |||
add_library(fmt::fmt-header-only ALIAS fmt-header-only) | |||
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1) | |||
target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES}) | |||
target_include_directories(fmt-header-only INTERFACE | |||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> | |||
$<INSTALL_INTERFACE:${FMT_INC_DIR}>) | |||
# Install targets. | |||
if (FMT_INSTALL) | |||
include(CMakePackageConfigHelpers) | |||
set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING | |||
"Installation directory for cmake files, a relative path that " | |||
"will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute " | |||
"path.") | |||
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake) | |||
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake) | |||
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc) | |||
set(targets_export_name fmt-targets) | |||
set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING | |||
"Installation directory for libraries, a relative path that " | |||
"will be joined to ${CMAKE_INSTALL_PREFIX} or an absolute path.") | |||
set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH | |||
"Installation directory for pkgconfig (.pc) files, a relative " | |||
"path that will be joined with ${CMAKE_INSTALL_PREFIX} or an " | |||
"absolute path.") | |||
# Generate the version, config and target files into the build directory. | |||
write_basic_package_version_file( | |||
${version_config} | |||
VERSION ${FMT_VERSION} | |||
COMPATIBILITY AnyNewerVersion) | |||
join_paths(libdir_for_pc_file "\${exec_prefix}" "${FMT_LIB_DIR}") | |||
join_paths(includedir_for_pc_file "\${prefix}" "${FMT_INC_DIR}") | |||
configure_file( | |||
"${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in" | |||
"${pkgconfig}" | |||
@ONLY) | |||
configure_package_config_file( | |||
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in | |||
${project_config} | |||
INSTALL_DESTINATION ${FMT_CMAKE_DIR}) | |||
set(INSTALL_TARGETS fmt fmt-header-only) | |||
# Use a namespace because CMake provides better diagnostics for namespaced | |||
# imported targets. | |||
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt:: | |||
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake) | |||
# Install version, config and target files. | |||
install( | |||
FILES ${project_config} ${version_config} | |||
DESTINATION ${FMT_CMAKE_DIR}) | |||
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR} | |||
NAMESPACE fmt::) | |||
# Install the library and headers. | |||
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} | |||
LIBRARY DESTINATION ${FMT_LIB_DIR} | |||
ARCHIVE DESTINATION ${FMT_LIB_DIR} | |||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) | |||
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}> | |||
DESTINATION ${FMT_LIB_DIR} OPTIONAL) | |||
install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt") | |||
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") | |||
endif () | |||
if (FMT_DOC) | |||
add_subdirectory(doc) | |||
endif () | |||
if (FMT_TEST) | |||
enable_testing() | |||
add_subdirectory(test) | |||
endif () | |||
# Control fuzzing independent of the unit tests. | |||
if (FMT_FUZZ) | |||
add_subdirectory(test/fuzzing) | |||
# The FMT_FUZZ macro is used to prevent resource exhaustion in fuzzing | |||
# mode and make fuzzing practically possible. It is similar to | |||
# FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION but uses a different name to | |||
# avoid interfering with fuzzing of projects that use {fmt}. | |||
# See also https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode. | |||
target_compile_definitions(fmt PUBLIC FMT_FUZZ) | |||
endif () | |||
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore) | |||
if (MASTER_PROJECT AND EXISTS ${gitignore}) | |||
# Get the list of ignored files from .gitignore. | |||
file (STRINGS ${gitignore} lines) | |||
list(REMOVE_ITEM lines /doc/html) | |||
foreach (line ${lines}) | |||
string(REPLACE "." "[.]" line "${line}") | |||
string(REPLACE "*" ".*" line "${line}") | |||
set(ignored_files ${ignored_files} "${line}$" "${line}/") | |||
endforeach () | |||
set(ignored_files ${ignored_files} | |||
/.git /breathe /format-benchmark sphinx/ .buildinfo .doctrees) | |||
set(CPACK_SOURCE_GENERATOR ZIP) | |||
set(CPACK_SOURCE_IGNORE_FILES ${ignored_files}) | |||
set(CPACK_SOURCE_PACKAGE_FILE_NAME fmt-${FMT_VERSION}) | |||
set(CPACK_PACKAGE_NAME fmt) | |||
set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.rst) | |||
include(CPack) | |||
endif () |
@@ -0,0 +1,27 @@ | |||
Copyright (c) 2012 - present, Victor Zverovich | |||
Permission is hereby granted, free of charge, to any person obtaining | |||
a copy of this software and associated documentation files (the | |||
"Software"), to deal in the Software without restriction, including | |||
without limitation the rights to use, copy, modify, merge, publish, | |||
distribute, sublicense, and/or sell copies of the Software, and to | |||
permit persons to whom the Software is furnished to do so, subject to | |||
the following conditions: | |||
The above copyright notice and this permission notice shall be | |||
included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
--- Optional exception to the license --- | |||
As an exception, if, as a result of your compiling your source code, portions | |||
of this Software are embedded into a machine-executable object form of such | |||
source code, you may redistribute such embedded portions in such object form | |||
without including the above copyright and permission notices. |
@@ -0,0 +1,506 @@ | |||
{fmt} | |||
===== | |||
.. image:: https://travis-ci.org/fmtlib/fmt.png?branch=master | |||
:target: https://travis-ci.org/fmtlib/fmt | |||
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v | |||
:target: https://ci.appveyor.com/project/vitaut/fmt | |||
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg | |||
:alt: fmt is continuously fuzzed at oss-fuzz | |||
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\ | |||
colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\ | |||
Summary&q=proj%3Dfmt&can=1 | |||
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg | |||
:alt: Ask questions at StackOverflow with the tag fmt | |||
:target: https://stackoverflow.com/questions/tagged/fmt | |||
**{fmt}** is an open-source formatting library providing a fast and safe | |||
alternative to C stdio and C++ iostreams. | |||
If you like this project, please consider donating to BYSOL, | |||
an initiative to help victims of political repressions in Belarus: | |||
https://www.facebook.com/donate/759400044849707/108388587646909/. | |||
`Documentation <https://fmt.dev>`__ | |||
Q&A: ask questions on `StackOverflow with the tag fmt | |||
<https://stackoverflow.com/questions/tagged/fmt>`_. | |||
Try {fmt} in `Compiler Explorer <https://godbolt.org/z/Eq5763>`_. | |||
Features | |||
-------- | |||
* Simple `format API <https://fmt.dev/latest/api.html>`_ with positional arguments | |||
for localization | |||
* Implementation of `C++20 std::format | |||
<https://en.cppreference.com/w/cpp/utility/format>`__ | |||
* `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar to Python's | |||
`format <https://docs.python.org/3/library/stdtypes.html#str.format>`_ | |||
* Fast IEEE 754 floating-point formatter with correct rounding, shortness and | |||
round-trip guarantees | |||
* Safe `printf implementation | |||
<https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX | |||
extension for positional arguments | |||
* Extensibility: `support for user-defined types | |||
<https://fmt.dev/latest/api.html#formatting-user-defined-types>`_ | |||
* High performance: faster than common standard library implementations of | |||
``(s)printf``, iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_ | |||
and `Converting a hundred million integers to strings per second | |||
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_ | |||
* Small code size both in terms of source code with the minimum configuration | |||
consisting of just three files, ``core.h``, ``format.h`` and ``format-inl.h``, | |||
and compiled code; see `Compile time and code bloat`_ | |||
* Reliability: the library has an extensive set of `tests | |||
<https://github.com/fmtlib/fmt/tree/master/test>`_ and is `continuously fuzzed | |||
<https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20 | |||
Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1>`_ | |||
* Safety: the library is fully type safe, errors in format strings can be | |||
reported at compile time, automatic memory management prevents buffer overflow | |||
errors | |||
* Ease of use: small self-contained code base, no external dependencies, | |||
permissive MIT `license | |||
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_ | |||
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with | |||
consistent output across platforms and support for older compilers | |||
* Clean warning-free codebase even on high warning levels such as | |||
``-Wall -Wextra -pedantic`` | |||
* Locale-independence by default | |||
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro | |||
See the `documentation <https://fmt.dev>`_ for more details. | |||
Examples | |||
-------- | |||
**Print to stdout** (`run <https://godbolt.org/z/Tevcjh>`_) | |||
.. code:: c++ | |||
#include <fmt/core.h> | |||
int main() { | |||
fmt::print("Hello, world!\n"); | |||
} | |||
**Format a string** (`run <https://godbolt.org/z/oK8h33>`_) | |||
.. code:: c++ | |||
std::string s = fmt::format("The answer is {}.", 42); | |||
// s == "The answer is 42." | |||
**Format a string using positional arguments** (`run <https://godbolt.org/z/Yn7Txe>`_) | |||
.. code:: c++ | |||
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy"); | |||
// s == "I'd rather be happy than right." | |||
**Print chrono durations** (`run <https://godbolt.org/z/K8s4Mc>`_) | |||
.. code:: c++ | |||
#include <fmt/chrono.h> | |||
int main() { | |||
using namespace std::literals::chrono_literals; | |||
fmt::print("Default format: {} {}\n", 42s, 100ms); | |||
fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s); | |||
} | |||
Output:: | |||
Default format: 42s 100ms | |||
strftime-like format: 03:15:30 | |||
**Print a container** (`run <https://godbolt.org/z/MjsY7c>`_) | |||
.. code:: c++ | |||
#include <vector> | |||
#include <fmt/ranges.h> | |||
int main() { | |||
std::vector<int> v = {1, 2, 3}; | |||
fmt::print("{}\n", v); | |||
} | |||
Output:: | |||
{1, 2, 3} | |||
**Check a format string at compile time** | |||
.. code:: c++ | |||
std::string s = fmt::format(FMT_STRING("{:d}"), "don't panic"); | |||
This gives a compile-time error because ``d`` is an invalid format specifier for | |||
a string. | |||
**Write a file from a single thread** | |||
.. code:: c++ | |||
#include <fmt/os.h> | |||
int main() { | |||
auto out = fmt::output_file("guide.txt"); | |||
out.print("Don't {}", "Panic"); | |||
} | |||
This can be `5 to 9 times faster than fprintf | |||
<http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html>`_. | |||
**Print with colors and text styles** | |||
.. code:: c++ | |||
#include <fmt/color.h> | |||
int main() { | |||
fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, | |||
"Hello, {}!\n", "world"); | |||
fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) | | |||
fmt::emphasis::underline, "Hello, {}!\n", "мир"); | |||
fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, | |||
"Hello, {}!\n", "世界"); | |||
} | |||
Output on a modern terminal: | |||
.. image:: https://user-images.githubusercontent.com/ | |||
576385/88485597-d312f600-cf2b-11ea-9cbe-61f535a86e28.png | |||
Benchmarks | |||
---------- | |||
Speed tests | |||
~~~~~~~~~~~ | |||
================= ============= =========== | |||
Library Method Run Time, s | |||
================= ============= =========== | |||
libc printf 1.04 | |||
libc++ std::ostream 3.05 | |||
{fmt} 6.1.1 fmt::print 0.75 | |||
Boost Format 1.67 boost::format 7.24 | |||
Folly Format folly::format 2.23 | |||
================= ============= =========== | |||
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``. | |||
The above results were generated by building ``tinyformat_test.cpp`` on macOS | |||
10.14.6 with ``clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT``, and taking the | |||
best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` | |||
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for | |||
further details refer to the `source | |||
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_. | |||
{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on | |||
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_) | |||
and faster than `double-conversion <https://github.com/google/double-conversion>`_ and | |||
`ryu <https://github.com/ulfjack/ryu>`_: | |||
.. image:: https://user-images.githubusercontent.com/576385/ | |||
95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png | |||
:target: https://fmt.dev/unknown_mac64_clang12.0.html | |||
Compile time and code bloat | |||
~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
The script `bloat-test.py | |||
<https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py>`_ | |||
from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_ | |||
tests compile time and code bloat for nontrivial projects. | |||
It generates 100 translation units and uses ``printf()`` or its alternative | |||
five times in each to simulate a medium sized project. The resulting | |||
executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42), | |||
macOS Sierra, best of three) is shown in the following tables. | |||
**Optimized build (-O3)** | |||
============= =============== ==================== ================== | |||
Method Compile Time, s Executable size, KiB Stripped size, KiB | |||
============= =============== ==================== ================== | |||
printf 2.6 29 26 | |||
printf+string 16.4 29 26 | |||
iostreams 31.1 59 55 | |||
{fmt} 19.0 37 34 | |||
Boost Format 91.9 226 203 | |||
Folly Format 115.7 101 88 | |||
============= =============== ==================== ================== | |||
As you can see, {fmt} has 60% less overhead in terms of resulting binary code | |||
size compared to iostreams and comes pretty close to ``printf``. Boost Format | |||
and Folly Format have the largest overheads. | |||
``printf+string`` is the same as ``printf`` but with extra ``<string>`` | |||
include to measure the overhead of the latter. | |||
**Non-optimized build** | |||
============= =============== ==================== ================== | |||
Method Compile Time, s Executable size, KiB Stripped size, KiB | |||
============= =============== ==================== ================== | |||
printf 2.2 33 30 | |||
printf+string 16.0 33 30 | |||
iostreams 28.3 56 52 | |||
{fmt} 18.2 59 50 | |||
Boost Format 54.1 365 303 | |||
Folly Format 79.9 445 430 | |||
============= =============== ==================== ================== | |||
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared libraries to | |||
compare formatting function overhead only. Boost Format is a | |||
header-only library so it doesn't provide any linkage options. | |||
Running the tests | |||
~~~~~~~~~~~~~~~~~ | |||
Please refer to `Building the library`__ for the instructions on how to build | |||
the library and run the unit tests. | |||
__ https://fmt.dev/latest/usage.html#building-the-library | |||
Benchmarks reside in a separate repository, | |||
`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_, | |||
so to run the benchmarks you first need to clone this repository and | |||
generate Makefiles with CMake:: | |||
$ git clone --recursive https://github.com/fmtlib/format-benchmark.git | |||
$ cd format-benchmark | |||
$ cmake . | |||
Then you can run the speed test:: | |||
$ make speed-test | |||
or the bloat test:: | |||
$ make bloat-test | |||
Projects using this library | |||
--------------------------- | |||
* `0 A.D. <https://play0ad.com/>`_: a free, open-source, cross-platform | |||
real-time strategy game | |||
* `AMPL/MP <https://github.com/ampl/mp>`_: | |||
an open-source library for mathematical programming | |||
* `Aseprite <https://github.com/aseprite/aseprite>`_: | |||
animated sprite editor & pixel art tool | |||
* `AvioBook <https://www.aviobook.aero/en>`_: a comprehensive aircraft | |||
operations suite | |||
* `Blizzard Battle.net <https://battle.net/>`_: an online gaming platform | |||
* `Celestia <https://celestia.space/>`_: real-time 3D visualization of space | |||
* `Ceph <https://ceph.com/>`_: a scalable distributed storage system | |||
* `ccache <https://ccache.dev/>`_: a compiler cache | |||
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database | |||
management system | |||
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater | |||
vehicle | |||
* `Drake <https://drake.mit.edu/>`_: a planning, control, and analysis toolbox | |||
for nonlinear dynamical systems (MIT) | |||
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus | |||
(Lyft) | |||
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V | |||
* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library | |||
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_: | |||
Player vs Player Gaming Network with tweaks | |||
* `KBEngine <https://github.com/kbengine/kbengine>`_: an open-source MMOG server | |||
engine | |||
* `Keypirinha <https://keypirinha.com/>`_: a semantic launcher for Windows | |||
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): home theater software | |||
* `Knuth <https://kth.cash/>`_: high-performance Bitcoin full-node | |||
* `Microsoft Verona <https://github.com/microsoft/verona>`_: | |||
research programming language for concurrent ownership | |||
* `MongoDB <https://mongodb.com/>`_: distributed document database | |||
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: a small tool to | |||
generate randomized datasets | |||
* `OpenSpace <https://openspaceproject.com/>`_: an open-source | |||
astrovisualization framework | |||
* `PenUltima Online (POL) <https://www.polserver.com/>`_: | |||
an MMO server, compatible with most Ultima Online clients | |||
* `PyTorch <https://github.com/pytorch/pytorch>`_: an open-source machine | |||
learning library | |||
* `quasardb <https://www.quasardb.net/>`_: a distributed, high-performance, | |||
associative database | |||
* `Quill <https://github.com/odygrd/quill>`_: asynchronous low-latency logging library | |||
* `QKW <https://github.com/ravijanjam/qkw>`_: generalizing aliasing to simplify | |||
navigation, and executing complex multi-line terminal command sequences | |||
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: a Redis cluster | |||
proxy | |||
* `redpanda <https://vectorized.io/redpanda>`_: a 10x faster Kafka® replacement | |||
for mission critical systems written in C++ | |||
* `rpclib <http://rpclib.net/>`_: a modern C++ msgpack-RPC server and client | |||
library | |||
* `Salesforce Analytics Cloud | |||
<https://www.salesforce.com/analytics-cloud/overview/>`_: | |||
business intelligence software | |||
* `Scylla <https://www.scylladb.com/>`_: a Cassandra-compatible NoSQL data store | |||
that can handle 1 million transactions per second on a single server | |||
* `Seastar <http://www.seastar-project.org/>`_: an advanced, open-source C++ | |||
framework for high-performance server applications on modern hardware | |||
* `spdlog <https://github.com/gabime/spdlog>`_: super fast C++ logging library | |||
* `Stellar <https://www.stellar.org/>`_: financial platform | |||
* `Touch Surgery <https://www.touchsurgery.com/>`_: surgery simulator | |||
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: open-source | |||
MMORPG framework | |||
* `Windows Terminal <https://github.com/microsoft/terminal>`_: the new Windows | |||
terminal | |||
`More... <https://github.com/search?q=fmtlib&type=Code>`_ | |||
If you are aware of other projects using this library, please let me know | |||
by `email <mailto:victor.zverovich@gmail.com>`_ or by submitting an | |||
`issue <https://github.com/fmtlib/fmt/issues>`_. | |||
Motivation | |||
---------- | |||
So why yet another formatting library? | |||
There are plenty of methods for doing this task, from standard ones like | |||
the printf family of function and iostreams to Boost Format and FastFormat | |||
libraries. The reason for creating a new library is that every existing | |||
solution that I found either had serious issues or didn't provide | |||
all the features I needed. | |||
printf | |||
~~~~~~ | |||
The good thing about ``printf`` is that it is pretty fast and readily available | |||
being a part of the C standard library. The main drawback is that it | |||
doesn't support user-defined types. ``printf`` also has safety issues although | |||
they are somewhat mitigated with `__attribute__ ((format (printf, ...)) | |||
<https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC. | |||
There is a POSIX extension that adds positional arguments required for | |||
`i18n <https://en.wikipedia.org/wiki/Internationalization_and_localization>`_ | |||
to ``printf`` but it is not a part of C99 and may not be available on some | |||
platforms. | |||
iostreams | |||
~~~~~~~~~ | |||
The main issue with iostreams is best illustrated with an example: | |||
.. code:: c++ | |||
std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n"; | |||
which is a lot of typing compared to printf: | |||
.. code:: c++ | |||
printf("%.2f\n", 1.23456); | |||
Matthew Wilson, the author of FastFormat, called this "chevron hell". iostreams | |||
don't support positional arguments by design. | |||
The good part is that iostreams support user-defined types and are safe although | |||
error handling is awkward. | |||
Boost Format | |||
~~~~~~~~~~~~ | |||
This is a very powerful library which supports both ``printf``-like format | |||
strings and positional arguments. Its main drawback is performance. According to | |||
various, benchmarks it is much slower than other methods considered here. Boost | |||
Format also has excessive build times and severe code bloat issues (see | |||
`Benchmarks`_). | |||
FastFormat | |||
~~~~~~~~~~ | |||
This is an interesting library which is fast, safe and has positional arguments. | |||
However, it has significant limitations, citing its author: | |||
Three features that have no hope of being accommodated within the | |||
current design are: | |||
* Leading zeros (or any other non-space padding) | |||
* Octal/hexadecimal encoding | |||
* Runtime width/alignment specification | |||
It is also quite big and has a heavy dependency, STLSoft, which might be too | |||
restrictive for using it in some projects. | |||
Boost Spirit.Karma | |||
~~~~~~~~~~~~~~~~~~ | |||
This is not really a formatting library but I decided to include it here for | |||
completeness. As iostreams, it suffers from the problem of mixing verbatim text | |||
with arguments. The library is pretty fast, but slower on integer formatting | |||
than ``fmt::format_to`` with format string compilation on Karma's own benchmark, | |||
see `Converting a hundred million integers to strings per second | |||
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_. | |||
License | |||
------- | |||
{fmt} is distributed under the MIT `license | |||
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_. | |||
Documentation License | |||
--------------------- | |||
The `Format String Syntax <https://fmt.dev/latest/syntax.html>`_ | |||
section in the documentation is based on the one from Python `string module | |||
documentation <https://docs.python.org/3/library/string.html#module-string>`_. | |||
For this reason the documentation is distributed under the Python Software | |||
Foundation license available in `doc/python-license.txt | |||
<https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt>`_. | |||
It only applies if you distribute the documentation of {fmt}. | |||
Maintainers | |||
----------- | |||
The {fmt} library is maintained by Victor Zverovich (`vitaut | |||
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan | |||
<https://github.com/foonathan>`_) with contributions from many other people. | |||
See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and | |||
`Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names. | |||
Let us know if your contribution is not listed or mentioned incorrectly and | |||
we'll make it right. |
@@ -0,0 +1,603 @@ | |||
// Formatting library for C++ - color support | |||
// | |||
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors | |||
// All rights reserved. | |||
// | |||
// For the license information refer to format.h. | |||
#ifndef FMT_COLOR_H_ | |||
#define FMT_COLOR_H_ | |||
#include "format.h" | |||
FMT_BEGIN_NAMESPACE | |||
enum class color : uint32_t { | |||
alice_blue = 0xF0F8FF, // rgb(240,248,255) | |||
antique_white = 0xFAEBD7, // rgb(250,235,215) | |||
aqua = 0x00FFFF, // rgb(0,255,255) | |||
aquamarine = 0x7FFFD4, // rgb(127,255,212) | |||
azure = 0xF0FFFF, // rgb(240,255,255) | |||
beige = 0xF5F5DC, // rgb(245,245,220) | |||
bisque = 0xFFE4C4, // rgb(255,228,196) | |||
black = 0x000000, // rgb(0,0,0) | |||
blanched_almond = 0xFFEBCD, // rgb(255,235,205) | |||
blue = 0x0000FF, // rgb(0,0,255) | |||
blue_violet = 0x8A2BE2, // rgb(138,43,226) | |||
brown = 0xA52A2A, // rgb(165,42,42) | |||
burly_wood = 0xDEB887, // rgb(222,184,135) | |||
cadet_blue = 0x5F9EA0, // rgb(95,158,160) | |||
chartreuse = 0x7FFF00, // rgb(127,255,0) | |||
chocolate = 0xD2691E, // rgb(210,105,30) | |||
coral = 0xFF7F50, // rgb(255,127,80) | |||
cornflower_blue = 0x6495ED, // rgb(100,149,237) | |||
cornsilk = 0xFFF8DC, // rgb(255,248,220) | |||
crimson = 0xDC143C, // rgb(220,20,60) | |||
cyan = 0x00FFFF, // rgb(0,255,255) | |||
dark_blue = 0x00008B, // rgb(0,0,139) | |||
dark_cyan = 0x008B8B, // rgb(0,139,139) | |||
dark_golden_rod = 0xB8860B, // rgb(184,134,11) | |||
dark_gray = 0xA9A9A9, // rgb(169,169,169) | |||
dark_green = 0x006400, // rgb(0,100,0) | |||
dark_khaki = 0xBDB76B, // rgb(189,183,107) | |||
dark_magenta = 0x8B008B, // rgb(139,0,139) | |||
dark_olive_green = 0x556B2F, // rgb(85,107,47) | |||
dark_orange = 0xFF8C00, // rgb(255,140,0) | |||
dark_orchid = 0x9932CC, // rgb(153,50,204) | |||
dark_red = 0x8B0000, // rgb(139,0,0) | |||
dark_salmon = 0xE9967A, // rgb(233,150,122) | |||
dark_sea_green = 0x8FBC8F, // rgb(143,188,143) | |||
dark_slate_blue = 0x483D8B, // rgb(72,61,139) | |||
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) | |||
dark_turquoise = 0x00CED1, // rgb(0,206,209) | |||
dark_violet = 0x9400D3, // rgb(148,0,211) | |||
deep_pink = 0xFF1493, // rgb(255,20,147) | |||
deep_sky_blue = 0x00BFFF, // rgb(0,191,255) | |||
dim_gray = 0x696969, // rgb(105,105,105) | |||
dodger_blue = 0x1E90FF, // rgb(30,144,255) | |||
fire_brick = 0xB22222, // rgb(178,34,34) | |||
floral_white = 0xFFFAF0, // rgb(255,250,240) | |||
forest_green = 0x228B22, // rgb(34,139,34) | |||
fuchsia = 0xFF00FF, // rgb(255,0,255) | |||
gainsboro = 0xDCDCDC, // rgb(220,220,220) | |||
ghost_white = 0xF8F8FF, // rgb(248,248,255) | |||
gold = 0xFFD700, // rgb(255,215,0) | |||
golden_rod = 0xDAA520, // rgb(218,165,32) | |||
gray = 0x808080, // rgb(128,128,128) | |||
green = 0x008000, // rgb(0,128,0) | |||
green_yellow = 0xADFF2F, // rgb(173,255,47) | |||
honey_dew = 0xF0FFF0, // rgb(240,255,240) | |||
hot_pink = 0xFF69B4, // rgb(255,105,180) | |||
indian_red = 0xCD5C5C, // rgb(205,92,92) | |||
indigo = 0x4B0082, // rgb(75,0,130) | |||
ivory = 0xFFFFF0, // rgb(255,255,240) | |||
khaki = 0xF0E68C, // rgb(240,230,140) | |||
lavender = 0xE6E6FA, // rgb(230,230,250) | |||
lavender_blush = 0xFFF0F5, // rgb(255,240,245) | |||
lawn_green = 0x7CFC00, // rgb(124,252,0) | |||
lemon_chiffon = 0xFFFACD, // rgb(255,250,205) | |||
light_blue = 0xADD8E6, // rgb(173,216,230) | |||
light_coral = 0xF08080, // rgb(240,128,128) | |||
light_cyan = 0xE0FFFF, // rgb(224,255,255) | |||
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) | |||
light_gray = 0xD3D3D3, // rgb(211,211,211) | |||
light_green = 0x90EE90, // rgb(144,238,144) | |||
light_pink = 0xFFB6C1, // rgb(255,182,193) | |||
light_salmon = 0xFFA07A, // rgb(255,160,122) | |||
light_sea_green = 0x20B2AA, // rgb(32,178,170) | |||
light_sky_blue = 0x87CEFA, // rgb(135,206,250) | |||
light_slate_gray = 0x778899, // rgb(119,136,153) | |||
light_steel_blue = 0xB0C4DE, // rgb(176,196,222) | |||
light_yellow = 0xFFFFE0, // rgb(255,255,224) | |||
lime = 0x00FF00, // rgb(0,255,0) | |||
lime_green = 0x32CD32, // rgb(50,205,50) | |||
linen = 0xFAF0E6, // rgb(250,240,230) | |||
magenta = 0xFF00FF, // rgb(255,0,255) | |||
maroon = 0x800000, // rgb(128,0,0) | |||
medium_aquamarine = 0x66CDAA, // rgb(102,205,170) | |||
medium_blue = 0x0000CD, // rgb(0,0,205) | |||
medium_orchid = 0xBA55D3, // rgb(186,85,211) | |||
medium_purple = 0x9370DB, // rgb(147,112,219) | |||
medium_sea_green = 0x3CB371, // rgb(60,179,113) | |||
medium_slate_blue = 0x7B68EE, // rgb(123,104,238) | |||
medium_spring_green = 0x00FA9A, // rgb(0,250,154) | |||
medium_turquoise = 0x48D1CC, // rgb(72,209,204) | |||
medium_violet_red = 0xC71585, // rgb(199,21,133) | |||
midnight_blue = 0x191970, // rgb(25,25,112) | |||
mint_cream = 0xF5FFFA, // rgb(245,255,250) | |||
misty_rose = 0xFFE4E1, // rgb(255,228,225) | |||
moccasin = 0xFFE4B5, // rgb(255,228,181) | |||
navajo_white = 0xFFDEAD, // rgb(255,222,173) | |||
navy = 0x000080, // rgb(0,0,128) | |||
old_lace = 0xFDF5E6, // rgb(253,245,230) | |||
olive = 0x808000, // rgb(128,128,0) | |||
olive_drab = 0x6B8E23, // rgb(107,142,35) | |||
orange = 0xFFA500, // rgb(255,165,0) | |||
orange_red = 0xFF4500, // rgb(255,69,0) | |||
orchid = 0xDA70D6, // rgb(218,112,214) | |||
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) | |||
pale_green = 0x98FB98, // rgb(152,251,152) | |||
pale_turquoise = 0xAFEEEE, // rgb(175,238,238) | |||
pale_violet_red = 0xDB7093, // rgb(219,112,147) | |||
papaya_whip = 0xFFEFD5, // rgb(255,239,213) | |||
peach_puff = 0xFFDAB9, // rgb(255,218,185) | |||
peru = 0xCD853F, // rgb(205,133,63) | |||
pink = 0xFFC0CB, // rgb(255,192,203) | |||
plum = 0xDDA0DD, // rgb(221,160,221) | |||
powder_blue = 0xB0E0E6, // rgb(176,224,230) | |||
purple = 0x800080, // rgb(128,0,128) | |||
rebecca_purple = 0x663399, // rgb(102,51,153) | |||
red = 0xFF0000, // rgb(255,0,0) | |||
rosy_brown = 0xBC8F8F, // rgb(188,143,143) | |||
royal_blue = 0x4169E1, // rgb(65,105,225) | |||
saddle_brown = 0x8B4513, // rgb(139,69,19) | |||
salmon = 0xFA8072, // rgb(250,128,114) | |||
sandy_brown = 0xF4A460, // rgb(244,164,96) | |||
sea_green = 0x2E8B57, // rgb(46,139,87) | |||
sea_shell = 0xFFF5EE, // rgb(255,245,238) | |||
sienna = 0xA0522D, // rgb(160,82,45) | |||
silver = 0xC0C0C0, // rgb(192,192,192) | |||
sky_blue = 0x87CEEB, // rgb(135,206,235) | |||
slate_blue = 0x6A5ACD, // rgb(106,90,205) | |||
slate_gray = 0x708090, // rgb(112,128,144) | |||
snow = 0xFFFAFA, // rgb(255,250,250) | |||
spring_green = 0x00FF7F, // rgb(0,255,127) | |||
steel_blue = 0x4682B4, // rgb(70,130,180) | |||
tan = 0xD2B48C, // rgb(210,180,140) | |||
teal = 0x008080, // rgb(0,128,128) | |||
thistle = 0xD8BFD8, // rgb(216,191,216) | |||
tomato = 0xFF6347, // rgb(255,99,71) | |||
turquoise = 0x40E0D0, // rgb(64,224,208) | |||
violet = 0xEE82EE, // rgb(238,130,238) | |||
wheat = 0xF5DEB3, // rgb(245,222,179) | |||
white = 0xFFFFFF, // rgb(255,255,255) | |||
white_smoke = 0xF5F5F5, // rgb(245,245,245) | |||
yellow = 0xFFFF00, // rgb(255,255,0) | |||
yellow_green = 0x9ACD32 // rgb(154,205,50) | |||
}; // enum class color | |||
enum class terminal_color : uint8_t { | |||
black = 30, | |||
red, | |||
green, | |||
yellow, | |||
blue, | |||
magenta, | |||
cyan, | |||
white, | |||
bright_black = 90, | |||
bright_red, | |||
bright_green, | |||
bright_yellow, | |||
bright_blue, | |||
bright_magenta, | |||
bright_cyan, | |||
bright_white | |||
}; | |||
enum class emphasis : uint8_t { | |||
bold = 1, | |||
italic = 1 << 1, | |||
underline = 1 << 2, | |||
strikethrough = 1 << 3 | |||
}; | |||
// rgb is a struct for red, green and blue colors. | |||
// Using the name "rgb" makes some editors show the color in a tooltip. | |||
struct rgb { | |||
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} | |||
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} | |||
FMT_CONSTEXPR rgb(uint32_t hex) | |||
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} | |||
FMT_CONSTEXPR rgb(color hex) | |||
: r((uint32_t(hex) >> 16) & 0xFF), | |||
g((uint32_t(hex) >> 8) & 0xFF), | |||
b(uint32_t(hex) & 0xFF) {} | |||
uint8_t r; | |||
uint8_t g; | |||
uint8_t b; | |||
}; | |||
namespace detail { | |||
// color is a struct of either a rgb color or a terminal color. | |||
struct color_type { | |||
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} | |||
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), | |||
value{} { | |||
value.rgb_color = static_cast<uint32_t>(rgb_color); | |||
} | |||
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} { | |||
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) | | |||
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b; | |||
} | |||
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), | |||
value{} { | |||
value.term_color = static_cast<uint8_t>(term_color); | |||
} | |||
bool is_rgb; | |||
union color_union { | |||
uint8_t term_color; | |||
uint32_t rgb_color; | |||
} value; | |||
}; | |||
} // namespace detail | |||
// Experimental text formatting support. | |||
class text_style { | |||
public: | |||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT | |||
: set_foreground_color(), | |||
set_background_color(), | |||
ems(em) {} | |||
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { | |||
if (!set_foreground_color) { | |||
set_foreground_color = rhs.set_foreground_color; | |||
foreground_color = rhs.foreground_color; | |||
} else if (rhs.set_foreground_color) { | |||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) | |||
FMT_THROW(format_error("can't OR a terminal color")); | |||
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; | |||
} | |||
if (!set_background_color) { | |||
set_background_color = rhs.set_background_color; | |||
background_color = rhs.background_color; | |||
} else if (rhs.set_background_color) { | |||
if (!background_color.is_rgb || !rhs.background_color.is_rgb) | |||
FMT_THROW(format_error("can't OR a terminal color")); | |||
background_color.value.rgb_color |= rhs.background_color.value.rgb_color; | |||
} | |||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) | | |||
static_cast<uint8_t>(rhs.ems)); | |||
return *this; | |||
} | |||
friend FMT_CONSTEXPR text_style operator|(text_style lhs, | |||
const text_style& rhs) { | |||
return lhs |= rhs; | |||
} | |||
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) { | |||
if (!set_foreground_color) { | |||
set_foreground_color = rhs.set_foreground_color; | |||
foreground_color = rhs.foreground_color; | |||
} else if (rhs.set_foreground_color) { | |||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) | |||
FMT_THROW(format_error("can't AND a terminal color")); | |||
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; | |||
} | |||
if (!set_background_color) { | |||
set_background_color = rhs.set_background_color; | |||
background_color = rhs.background_color; | |||
} else if (rhs.set_background_color) { | |||
if (!background_color.is_rgb || !rhs.background_color.is_rgb) | |||
FMT_THROW(format_error("can't AND a terminal color")); | |||
background_color.value.rgb_color &= rhs.background_color.value.rgb_color; | |||
} | |||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) & | |||
static_cast<uint8_t>(rhs.ems)); | |||
return *this; | |||
} | |||
friend FMT_CONSTEXPR text_style operator&(text_style lhs, | |||
const text_style& rhs) { | |||
return lhs &= rhs; | |||
} | |||
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { | |||
return set_foreground_color; | |||
} | |||
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { | |||
return set_background_color; | |||
} | |||
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { | |||
return static_cast<uint8_t>(ems) != 0; | |||
} | |||
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT { | |||
FMT_ASSERT(has_foreground(), "no foreground specified for this style"); | |||
return foreground_color; | |||
} | |||
FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT { | |||
FMT_ASSERT(has_background(), "no background specified for this style"); | |||
return background_color; | |||
} | |||
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { | |||
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); | |||
return ems; | |||
} | |||
private: | |||
FMT_CONSTEXPR text_style(bool is_foreground, | |||
detail::color_type text_color) FMT_NOEXCEPT | |||
: set_foreground_color(), | |||
set_background_color(), | |||
ems() { | |||
if (is_foreground) { | |||
foreground_color = text_color; | |||
set_foreground_color = true; | |||
} else { | |||
background_color = text_color; | |||
set_background_color = true; | |||
} | |||
} | |||
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground) | |||
FMT_NOEXCEPT; | |||
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background) | |||
FMT_NOEXCEPT; | |||
detail::color_type foreground_color; | |||
detail::color_type background_color; | |||
bool set_foreground_color; | |||
bool set_background_color; | |||
emphasis ems; | |||
}; | |||
FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT { | |||
return text_style(/*is_foreground=*/true, foreground); | |||
} | |||
FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT { | |||
return text_style(/*is_foreground=*/false, background); | |||
} | |||
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT { | |||
return text_style(lhs) | rhs; | |||
} | |||
namespace detail { | |||
template <typename Char> struct ansi_color_escape { | |||
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, | |||
const char* esc) FMT_NOEXCEPT { | |||
// If we have a terminal color, we need to output another escape code | |||
// sequence. | |||
if (!text_color.is_rgb) { | |||
bool is_background = esc == detail::data::background_color; | |||
uint32_t value = text_color.value.term_color; | |||
// Background ASCII codes are the same as the foreground ones but with | |||
// 10 more. | |||
if (is_background) value += 10u; | |||
size_t index = 0; | |||
buffer[index++] = static_cast<Char>('\x1b'); | |||
buffer[index++] = static_cast<Char>('['); | |||
if (value >= 100u) { | |||
buffer[index++] = static_cast<Char>('1'); | |||
value %= 100u; | |||
} | |||
buffer[index++] = static_cast<Char>('0' + value / 10u); | |||
buffer[index++] = static_cast<Char>('0' + value % 10u); | |||
buffer[index++] = static_cast<Char>('m'); | |||
buffer[index++] = static_cast<Char>('\0'); | |||
return; | |||
} | |||
for (int i = 0; i < 7; i++) { | |||
buffer[i] = static_cast<Char>(esc[i]); | |||
} | |||
rgb color(text_color.value.rgb_color); | |||
to_esc(color.r, buffer + 7, ';'); | |||
to_esc(color.g, buffer + 11, ';'); | |||
to_esc(color.b, buffer + 15, 'm'); | |||
buffer[19] = static_cast<Char>(0); | |||
} | |||
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { | |||
uint8_t em_codes[4] = {}; | |||
uint8_t em_bits = static_cast<uint8_t>(em); | |||
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1; | |||
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3; | |||
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4; | |||
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough)) | |||
em_codes[3] = 9; | |||
size_t index = 0; | |||
for (int i = 0; i < 4; ++i) { | |||
if (!em_codes[i]) continue; | |||
buffer[index++] = static_cast<Char>('\x1b'); | |||
buffer[index++] = static_cast<Char>('['); | |||
buffer[index++] = static_cast<Char>('0' + em_codes[i]); | |||
buffer[index++] = static_cast<Char>('m'); | |||
} | |||
buffer[index++] = static_cast<Char>(0); | |||
} | |||
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } | |||
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } | |||
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT { | |||
return buffer + std::char_traits<Char>::length(buffer); | |||
} | |||
private: | |||
Char buffer[7u + 3u * 4u + 1u]; | |||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, | |||
char delimiter) FMT_NOEXCEPT { | |||
out[0] = static_cast<Char>('0' + c / 100); | |||
out[1] = static_cast<Char>('0' + c / 10 % 10); | |||
out[2] = static_cast<Char>('0' + c % 10); | |||
out[3] = static_cast<Char>(delimiter); | |||
} | |||
}; | |||
template <typename Char> | |||
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color( | |||
detail::color_type foreground) FMT_NOEXCEPT { | |||
return ansi_color_escape<Char>(foreground, detail::data::foreground_color); | |||
} | |||
template <typename Char> | |||
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color( | |||
detail::color_type background) FMT_NOEXCEPT { | |||
return ansi_color_escape<Char>(background, detail::data::background_color); | |||
} | |||
template <typename Char> | |||
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT { | |||
return ansi_color_escape<Char>(em); | |||
} | |||
template <typename Char> | |||
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { | |||
std::fputs(chars, stream); | |||
} | |||
template <> | |||
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { | |||
std::fputws(chars, stream); | |||
} | |||
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT { | |||
fputs(detail::data::reset_color, stream); | |||
} | |||
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT { | |||
fputs(detail::data::wreset_color, stream); | |||
} | |||
template <typename Char> | |||
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT { | |||
const char* begin = data::reset_color; | |||
const char* end = begin + sizeof(data::reset_color) - 1; | |||
buffer.append(begin, end); | |||
} | |||
template <typename Char> | |||
void vformat_to(buffer<Char>& buf, const text_style& ts, | |||
basic_string_view<Char> format_str, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { | |||
bool has_style = false; | |||
if (ts.has_emphasis()) { | |||
has_style = true; | |||
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis()); | |||
buf.append(emphasis.begin(), emphasis.end()); | |||
} | |||
if (ts.has_foreground()) { | |||
has_style = true; | |||
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground()); | |||
buf.append(foreground.begin(), foreground.end()); | |||
} | |||
if (ts.has_background()) { | |||
has_style = true; | |||
auto background = detail::make_background_color<Char>(ts.get_background()); | |||
buf.append(background.begin(), background.end()); | |||
} | |||
detail::vformat_to(buf, format_str, args); | |||
if (has_style) detail::reset_color<Char>(buf); | |||
} | |||
} // namespace detail | |||
template <typename S, typename Char = char_t<S>> | |||
void vprint(std::FILE* f, const text_style& ts, const S& format, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { | |||
basic_memory_buffer<Char> buf; | |||
detail::vformat_to(buf, ts, to_string_view(format), args); | |||
buf.push_back(Char(0)); | |||
detail::fputs(buf.data(), f); | |||
} | |||
/** | |||
\rst | |||
Formats a string and prints it to the specified file stream using ANSI | |||
escape sequences to specify text formatting. | |||
**Example**:: | |||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), | |||
"Elapsed time: {0:.2f} seconds", 1.23); | |||
\endrst | |||
*/ | |||
template <typename S, typename... Args, | |||
FMT_ENABLE_IF(detail::is_string<S>::value)> | |||
void print(std::FILE* f, const text_style& ts, const S& format_str, | |||
const Args&... args) { | |||
vprint(f, ts, format_str, | |||
fmt::make_args_checked<Args...>(format_str, args...)); | |||
} | |||
/** | |||
Formats a string and prints it to stdout using ANSI escape sequences to | |||
specify text formatting. | |||
Example: | |||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), | |||
"Elapsed time: {0:.2f} seconds", 1.23); | |||
*/ | |||
template <typename S, typename... Args, | |||
FMT_ENABLE_IF(detail::is_string<S>::value)> | |||
void print(const text_style& ts, const S& format_str, const Args&... args) { | |||
return print(stdout, ts, format_str, args...); | |||
} | |||
template <typename S, typename Char = char_t<S>> | |||
inline std::basic_string<Char> vformat( | |||
const text_style& ts, const S& format_str, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { | |||
basic_memory_buffer<Char> buf; | |||
detail::vformat_to(buf, ts, to_string_view(format_str), args); | |||
return fmt::to_string(buf); | |||
} | |||
/** | |||
\rst | |||
Formats arguments and returns the result as a string using ANSI | |||
escape sequences to specify text formatting. | |||
**Example**:: | |||
#include <fmt/color.h> | |||
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), | |||
"The answer is {}", 42); | |||
\endrst | |||
*/ | |||
template <typename S, typename... Args, typename Char = char_t<S>> | |||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str, | |||
const Args&... args) { | |||
return vformat(ts, to_string_view(format_str), | |||
fmt::make_args_checked<Args...>(format_str, args...)); | |||
} | |||
/** | |||
Formats a string with the given text_style and writes the output to ``out``. | |||
*/ | |||
template <typename OutputIt, typename Char, | |||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)> | |||
OutputIt vformat_to( | |||
OutputIt out, const text_style& ts, basic_string_view<Char> format_str, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { | |||
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out)); | |||
detail::vformat_to(buf, ts, format_str, args); | |||
return detail::get_iterator(buf); | |||
} | |||
/** | |||
\rst | |||
Formats arguments with the given text_style, writes the result to the output | |||
iterator ``out`` and returns the iterator past the end of the output range. | |||
**Example**:: | |||
std::vector<char> out; | |||
fmt::format_to(std::back_inserter(out), | |||
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); | |||
\endrst | |||
*/ | |||
template <typename OutputIt, typename S, typename... Args, | |||
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&& | |||
detail::is_string<S>::value> | |||
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, | |||
Args&&... args) -> | |||
typename std::enable_if<enable, OutputIt>::type { | |||
return vformat_to(out, ts, to_string_view(format_str), | |||
fmt::make_args_checked<Args...>(format_str, args...)); | |||
} | |||
FMT_END_NAMESPACE | |||
#endif // FMT_COLOR_H_ |
@@ -0,0 +1,701 @@ | |||
// Formatting library for C++ - experimental format string compilation | |||
// | |||
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors | |||
// All rights reserved. | |||
// | |||
// For the license information refer to format.h. | |||
#ifndef FMT_COMPILE_H_ | |||
#define FMT_COMPILE_H_ | |||
#include <vector> | |||
#include "format.h" | |||
FMT_BEGIN_NAMESPACE | |||
namespace detail { | |||
// A compile-time string which is compiled into fast formatting code. | |||
class compiled_string {}; | |||
template <typename S> | |||
struct is_compiled_string : std::is_base_of<compiled_string, S> {}; | |||
/** | |||
\rst | |||
Converts a string literal *s* into a format string that will be parsed at | |||
compile time and converted into efficient formatting code. Requires C++17 | |||
``constexpr if`` compiler support. | |||
**Example**:: | |||
// Converts 42 into std::string using the most efficient method and no | |||
// runtime format string processing. | |||
std::string s = fmt::format(FMT_COMPILE("{}"), 42); | |||
\endrst | |||
*/ | |||
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string) | |||
template <typename T, typename... Tail> | |||
const T& first(const T& value, const Tail&...) { | |||
return value; | |||
} | |||
// Part of a compiled format string. It can be either literal text or a | |||
// replacement field. | |||
template <typename Char> struct format_part { | |||
enum class kind { arg_index, arg_name, text, replacement }; | |||
struct replacement { | |||
arg_ref<Char> arg_id; | |||
dynamic_format_specs<Char> specs; | |||
}; | |||
kind part_kind; | |||
union value { | |||
int arg_index; | |||
basic_string_view<Char> str; | |||
replacement repl; | |||
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {} | |||
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {} | |||
FMT_CONSTEXPR value(replacement r) : repl(r) {} | |||
} val; | |||
// Position past the end of the argument id. | |||
const Char* arg_id_end = nullptr; | |||
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {}) | |||
: part_kind(k), val(v) {} | |||
static FMT_CONSTEXPR format_part make_arg_index(int index) { | |||
return format_part(kind::arg_index, index); | |||
} | |||
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) { | |||
return format_part(kind::arg_name, name); | |||
} | |||
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) { | |||
return format_part(kind::text, text); | |||
} | |||
static FMT_CONSTEXPR format_part make_replacement(replacement repl) { | |||
return format_part(kind::replacement, repl); | |||
} | |||
}; | |||
template <typename Char> struct part_counter { | |||
unsigned num_parts = 0; | |||
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { | |||
if (begin != end) ++num_parts; | |||
} | |||
FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; } | |||
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; } | |||
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) { | |||
return ++num_parts, 0; | |||
} | |||
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} | |||
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin, | |||
const Char* end) { | |||
// Find the matching brace. | |||
unsigned brace_counter = 0; | |||
for (; begin != end; ++begin) { | |||
if (*begin == '{') { | |||
++brace_counter; | |||
} else if (*begin == '}') { | |||
if (brace_counter == 0u) break; | |||
--brace_counter; | |||
} | |||
} | |||
return begin; | |||
} | |||
FMT_CONSTEXPR void on_error(const char*) {} | |||
}; | |||
// Counts the number of parts in a format string. | |||
template <typename Char> | |||
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) { | |||
part_counter<Char> counter; | |||
parse_format_string<true>(format_str, counter); | |||
return counter.num_parts; | |||
} | |||
template <typename Char, typename PartHandler> | |||
class format_string_compiler : public error_handler { | |||
private: | |||
using part = format_part<Char>; | |||
PartHandler handler_; | |||
part part_; | |||
basic_string_view<Char> format_str_; | |||
basic_format_parse_context<Char> parse_context_; | |||
public: | |||
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str, | |||
PartHandler handler) | |||
: handler_(handler), | |||
format_str_(format_str), | |||
parse_context_(format_str) {} | |||
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { | |||
if (begin != end) | |||
handler_(part::make_text({begin, to_unsigned(end - begin)})); | |||
} | |||
FMT_CONSTEXPR int on_arg_id() { | |||
part_ = part::make_arg_index(parse_context_.next_arg_id()); | |||
return 0; | |||
} | |||
FMT_CONSTEXPR int on_arg_id(int id) { | |||
parse_context_.check_arg_id(id); | |||
part_ = part::make_arg_index(id); | |||
return 0; | |||
} | |||
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) { | |||
part_ = part::make_arg_name(id); | |||
return 0; | |||
} | |||
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) { | |||
part_.arg_id_end = ptr; | |||
handler_(part_); | |||
} | |||
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin, | |||
const Char* end) { | |||
auto repl = typename part::replacement(); | |||
dynamic_specs_handler<basic_format_parse_context<Char>> handler( | |||
repl.specs, parse_context_); | |||
auto it = parse_format_specs(begin, end, handler); | |||
if (*it != '}') on_error("missing '}' in format string"); | |||
repl.arg_id = part_.part_kind == part::kind::arg_index | |||
? arg_ref<Char>(part_.val.arg_index) | |||
: arg_ref<Char>(part_.val.str); | |||
auto part = part::make_replacement(repl); | |||
part.arg_id_end = begin; | |||
handler_(part); | |||
return it; | |||
} | |||
}; | |||
// Compiles a format string and invokes handler(part) for each parsed part. | |||
template <bool IS_CONSTEXPR, typename Char, typename PartHandler> | |||
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str, | |||
PartHandler handler) { | |||
parse_format_string<IS_CONSTEXPR>( | |||
format_str, | |||
format_string_compiler<Char, PartHandler>(format_str, handler)); | |||
} | |||
template <typename OutputIt, typename Context, typename Id> | |||
void format_arg( | |||
basic_format_parse_context<typename Context::char_type>& parse_ctx, | |||
Context& ctx, Id arg_id) { | |||
ctx.advance_to(visit_format_arg( | |||
arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx), | |||
ctx.arg(arg_id))); | |||
} | |||
// vformat_to is defined in a subnamespace to prevent ADL. | |||
namespace cf { | |||
template <typename Context, typename OutputIt, typename CompiledFormat> | |||
auto vformat_to(OutputIt out, CompiledFormat& cf, | |||
basic_format_args<Context> args) -> typename Context::iterator { | |||
using char_type = typename Context::char_type; | |||
basic_format_parse_context<char_type> parse_ctx( | |||
to_string_view(cf.format_str_)); | |||
Context ctx(out, args); | |||
const auto& parts = cf.parts(); | |||
for (auto part_it = std::begin(parts); part_it != std::end(parts); | |||
++part_it) { | |||
const auto& part = *part_it; | |||
const auto& value = part.val; | |||
using format_part_t = format_part<char_type>; | |||
switch (part.part_kind) { | |||
case format_part_t::kind::text: { | |||
const auto text = value.str; | |||
auto output = ctx.out(); | |||
auto&& it = reserve(output, text.size()); | |||
it = std::copy_n(text.begin(), text.size(), it); | |||
ctx.advance_to(output); | |||
break; | |||
} | |||
case format_part_t::kind::arg_index: | |||
advance_to(parse_ctx, part.arg_id_end); | |||
detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index); | |||
break; | |||
case format_part_t::kind::arg_name: | |||
advance_to(parse_ctx, part.arg_id_end); | |||
detail::format_arg<OutputIt>(parse_ctx, ctx, value.str); | |||
break; | |||
case format_part_t::kind::replacement: { | |||
const auto& arg_id_value = value.repl.arg_id.val; | |||
const auto arg = value.repl.arg_id.kind == arg_id_kind::index | |||
? ctx.arg(arg_id_value.index) | |||
: ctx.arg(arg_id_value.name); | |||
auto specs = value.repl.specs; | |||
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx); | |||
handle_dynamic_spec<precision_checker>(specs.precision, | |||
specs.precision_ref, ctx); | |||
error_handler h; | |||
numeric_specs_checker<error_handler> checker(h, arg.type()); | |||
if (specs.align == align::numeric) checker.require_numeric_argument(); | |||
if (specs.sign != sign::none) checker.check_sign(); | |||
if (specs.alt) checker.require_numeric_argument(); | |||
if (specs.precision >= 0) checker.check_precision(); | |||
advance_to(parse_ctx, part.arg_id_end); | |||
ctx.advance_to( | |||
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>( | |||
ctx, nullptr, &specs), | |||
arg)); | |||
break; | |||
} | |||
} | |||
} | |||
return ctx.out(); | |||
} | |||
} // namespace cf | |||
struct basic_compiled_format {}; | |||
template <typename S, typename = void> | |||
struct compiled_format_base : basic_compiled_format { | |||
using char_type = char_t<S>; | |||
using parts_container = std::vector<detail::format_part<char_type>>; | |||
parts_container compiled_parts; | |||
explicit compiled_format_base(basic_string_view<char_type> format_str) { | |||
compile_format_string<false>(format_str, | |||
[this](const format_part<char_type>& part) { | |||
compiled_parts.push_back(part); | |||
}); | |||
} | |||
const parts_container& parts() const { return compiled_parts; } | |||
}; | |||
template <typename Char, unsigned N> struct format_part_array { | |||
format_part<Char> data[N] = {}; | |||
FMT_CONSTEXPR format_part_array() = default; | |||
}; | |||
template <typename Char, unsigned N> | |||
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts( | |||
basic_string_view<Char> format_str) { | |||
format_part_array<Char, N> parts; | |||
unsigned counter = 0; | |||
// This is not a lambda for compatibility with older compilers. | |||
struct { | |||
format_part<Char>* parts; | |||
unsigned* counter; | |||
FMT_CONSTEXPR void operator()(const format_part<Char>& part) { | |||
parts[(*counter)++] = part; | |||
} | |||
} collector{parts.data, &counter}; | |||
compile_format_string<true>(format_str, collector); | |||
if (counter < N) { | |||
parts.data[counter] = | |||
format_part<Char>::make_text(basic_string_view<Char>()); | |||
} | |||
return parts; | |||
} | |||
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) { | |||
return (a < b) ? b : a; | |||
} | |||
template <typename S> | |||
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>> | |||
: basic_compiled_format { | |||
using char_type = char_t<S>; | |||
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {} | |||
// Workaround for old compilers. Format string compilation will not be | |||
// performed there anyway. | |||
#if FMT_USE_CONSTEXPR | |||
static FMT_CONSTEXPR_DECL const unsigned num_format_parts = | |||
constexpr_max(count_parts(to_string_view(S())), 1u); | |||
#else | |||
static const unsigned num_format_parts = 1; | |||
#endif | |||
using parts_container = format_part<char_type>[num_format_parts]; | |||
const parts_container& parts() const { | |||
static FMT_CONSTEXPR_DECL const auto compiled_parts = | |||
compile_to_parts<char_type, num_format_parts>( | |||
detail::to_string_view(S())); | |||
return compiled_parts.data; | |||
} | |||
}; | |||
template <typename S, typename... Args> | |||
class compiled_format : private compiled_format_base<S> { | |||
public: | |||
using typename compiled_format_base<S>::char_type; | |||
private: | |||
basic_string_view<char_type> format_str_; | |||
template <typename Context, typename OutputIt, typename CompiledFormat> | |||
friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf, | |||
basic_format_args<Context> args) -> | |||
typename Context::iterator; | |||
public: | |||
compiled_format() = delete; | |||
explicit constexpr compiled_format(basic_string_view<char_type> format_str) | |||
: compiled_format_base<S>(format_str), format_str_(format_str) {} | |||
}; | |||
#ifdef __cpp_if_constexpr | |||
template <typename... Args> struct type_list {}; | |||
// Returns a reference to the argument at index N from [first, rest...]. | |||
template <int N, typename T, typename... Args> | |||
constexpr const auto& get([[maybe_unused]] const T& first, | |||
[[maybe_unused]] const Args&... rest) { | |||
static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); | |||
if constexpr (N == 0) | |||
return first; | |||
else | |||
return get<N - 1>(rest...); | |||
} | |||
template <int N, typename> struct get_type_impl; | |||
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> { | |||
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>; | |||
}; | |||
template <int N, typename T> | |||
using get_type = typename get_type_impl<N, T>::type; | |||
template <typename T> struct is_compiled_format : std::false_type {}; | |||
template <typename Char> struct text { | |||
basic_string_view<Char> data; | |||
using char_type = Char; | |||
template <typename OutputIt, typename... Args> | |||
OutputIt format(OutputIt out, const Args&...) const { | |||
return write<Char>(out, data); | |||
} | |||
}; | |||
template <typename Char> | |||
struct is_compiled_format<text<Char>> : std::true_type {}; | |||
template <typename Char> | |||
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos, | |||
size_t size) { | |||
return {{&s[pos], size}}; | |||
} | |||
template <typename Char> struct code_unit { | |||
Char value; | |||
using char_type = Char; | |||
template <typename OutputIt, typename... Args> | |||
OutputIt format(OutputIt out, const Args&...) const { | |||
return write<Char>(out, value); | |||
} | |||
}; | |||
template <typename Char> | |||
struct is_compiled_format<code_unit<Char>> : std::true_type {}; | |||
// A replacement field that refers to argument N. | |||
template <typename Char, typename T, int N> struct field { | |||
using char_type = Char; | |||
template <typename OutputIt, typename... Args> | |||
OutputIt format(OutputIt out, const Args&... args) const { | |||
// This ensures that the argument type is convertile to `const T&`. | |||
const T& arg = get<N>(args...); | |||
return write<Char>(out, arg); | |||
} | |||
}; | |||
template <typename Char, typename T, int N> | |||
struct is_compiled_format<field<Char, T, N>> : std::true_type {}; | |||
// A replacement field that refers to argument N and has format specifiers. | |||
template <typename Char, typename T, int N> struct spec_field { | |||
using char_type = Char; | |||
mutable formatter<T, Char> fmt; | |||
template <typename OutputIt, typename... Args> | |||
OutputIt format(OutputIt out, const Args&... args) const { | |||
// This ensures that the argument type is convertile to `const T&`. | |||
const T& arg = get<N>(args...); | |||
const auto& vargs = | |||
make_format_args<basic_format_context<OutputIt, Char>>(args...); | |||
basic_format_context<OutputIt, Char> ctx(out, vargs); | |||
return fmt.format(arg, ctx); | |||
} | |||
}; | |||
template <typename Char, typename T, int N> | |||
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {}; | |||
template <typename L, typename R> struct concat { | |||
L lhs; | |||
R rhs; | |||
using char_type = typename L::char_type; | |||
template <typename OutputIt, typename... Args> | |||
OutputIt format(OutputIt out, const Args&... args) const { | |||
out = lhs.format(out, args...); | |||
return rhs.format(out, args...); | |||
} | |||
}; | |||
template <typename L, typename R> | |||
struct is_compiled_format<concat<L, R>> : std::true_type {}; | |||
template <typename L, typename R> | |||
constexpr concat<L, R> make_concat(L lhs, R rhs) { | |||
return {lhs, rhs}; | |||
} | |||
struct unknown_format {}; | |||
template <typename Char> | |||
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) { | |||
for (size_t size = str.size(); pos != size; ++pos) { | |||
if (str[pos] == '{' || str[pos] == '}') break; | |||
} | |||
return pos; | |||
} | |||
template <typename Args, size_t POS, int ID, typename S> | |||
constexpr auto compile_format_string(S format_str); | |||
template <typename Args, size_t POS, int ID, typename T, typename S> | |||
constexpr auto parse_tail(T head, S format_str) { | |||
if constexpr (POS != | |||
basic_string_view<typename S::char_type>(format_str).size()) { | |||
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str); | |||
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>, | |||
unknown_format>()) | |||
return tail; | |||
else | |||
return make_concat(head, tail); | |||
} else { | |||
return head; | |||
} | |||
} | |||
template <typename T, typename Char> struct parse_specs_result { | |||
formatter<T, Char> fmt; | |||
size_t end; | |||
int next_arg_id; | |||
}; | |||
template <typename T, typename Char> | |||
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, | |||
size_t pos, int arg_id) { | |||
str.remove_prefix(pos); | |||
auto ctx = basic_format_parse_context<Char>(str, {}, arg_id + 1); | |||
auto f = formatter<T, Char>(); | |||
auto end = f.parse(ctx); | |||
return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()}; | |||
} | |||
// Compiles a non-empty format string and returns the compiled representation | |||
// or unknown_format() on unrecognized input. | |||
template <typename Args, size_t POS, int ID, typename S> | |||
constexpr auto compile_format_string(S format_str) { | |||
using char_type = typename S::char_type; | |||
constexpr basic_string_view<char_type> str = format_str; | |||
if constexpr (str[POS] == '{') { | |||
if (POS + 1 == str.size()) | |||
throw format_error("unmatched '{' in format string"); | |||
if constexpr (str[POS + 1] == '{') { | |||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); | |||
} else if constexpr (str[POS + 1] == '}') { | |||
using type = get_type<ID, Args>; | |||
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(), | |||
format_str); | |||
} else if constexpr (str[POS + 1] == ':') { | |||
using type = get_type<ID, Args>; | |||
constexpr auto result = parse_specs<type>(str, POS + 2, ID); | |||
return parse_tail<Args, result.end, result.next_arg_id>( | |||
spec_field<char_type, type, ID>{result.fmt}, format_str); | |||
} else { | |||
return unknown_format(); | |||
} | |||
} else if constexpr (str[POS] == '}') { | |||
if (POS + 1 == str.size()) | |||
throw format_error("unmatched '}' in format string"); | |||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); | |||
} else { | |||
constexpr auto end = parse_text(str, POS + 1); | |||
if constexpr (end - POS > 1) { | |||
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), | |||
format_str); | |||
} else { | |||
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, | |||
format_str); | |||
} | |||
} | |||
} | |||
template <typename... Args, typename S, | |||
FMT_ENABLE_IF(is_compile_string<S>::value || | |||
detail::is_compiled_string<S>::value)> | |||
constexpr auto compile(S format_str) { | |||
constexpr basic_string_view<typename S::char_type> str = format_str; | |||
if constexpr (str.size() == 0) { | |||
return detail::make_text(str, 0, 0); | |||
} else { | |||
constexpr auto result = | |||
detail::compile_format_string<detail::type_list<Args...>, 0, 0>( | |||
format_str); | |||
if constexpr (std::is_same<remove_cvref_t<decltype(result)>, | |||
detail::unknown_format>()) { | |||
return detail::compiled_format<S, Args...>(to_string_view(format_str)); | |||
} else { | |||
return result; | |||
} | |||
} | |||
} | |||
#else | |||
template <typename... Args, typename S, | |||
FMT_ENABLE_IF(is_compile_string<S>::value)> | |||
constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> { | |||
return detail::compiled_format<S, Args...>(to_string_view(format_str)); | |||
} | |||
#endif // __cpp_if_constexpr | |||
// Compiles the format string which must be a string literal. | |||
template <typename... Args, typename Char, size_t N> | |||
auto compile(const Char (&format_str)[N]) | |||
-> detail::compiled_format<const Char*, Args...> { | |||
return detail::compiled_format<const Char*, Args...>( | |||
basic_string_view<Char>(format_str, N - 1)); | |||
} | |||
} // namespace detail | |||
// DEPRECATED! use FMT_COMPILE instead. | |||
template <typename... Args> | |||
FMT_DEPRECATED auto compile(const Args&... args) | |||
-> decltype(detail::compile(args...)) { | |||
return detail::compile(args...); | |||
} | |||
#if FMT_USE_CONSTEXPR | |||
# ifdef __cpp_if_constexpr | |||
template <typename CompiledFormat, typename... Args, | |||
typename Char = typename CompiledFormat::char_type, | |||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> | |||
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf, | |||
const Args&... args) { | |||
basic_memory_buffer<Char> buffer; | |||
cf.format(detail::buffer_appender<Char>(buffer), args...); | |||
return to_string(buffer); | |||
} | |||
template <typename OutputIt, typename CompiledFormat, typename... Args, | |||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> | |||
OutputIt format_to(OutputIt out, const CompiledFormat& cf, | |||
const Args&... args) { | |||
return cf.format(out, args...); | |||
} | |||
# endif // __cpp_if_constexpr | |||
#endif // FMT_USE_CONSTEXPR | |||
template <typename CompiledFormat, typename... Args, | |||
typename Char = typename CompiledFormat::char_type, | |||
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format, | |||
CompiledFormat>::value)> | |||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) { | |||
basic_memory_buffer<Char> buffer; | |||
using context = buffer_context<Char>; | |||
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf, | |||
make_format_args<context>(args...)); | |||
return to_string(buffer); | |||
} | |||
template <typename S, typename... Args, | |||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> | |||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&, | |||
Args&&... args) { | |||
#ifdef __cpp_if_constexpr | |||
if constexpr (std::is_same<typename S::char_type, char>::value) { | |||
constexpr basic_string_view<typename S::char_type> str = S(); | |||
if (str.size() == 2 && str[0] == '{' && str[1] == '}') | |||
return fmt::to_string(detail::first(args...)); | |||
} | |||
#endif | |||
constexpr auto compiled = detail::compile<Args...>(S()); | |||
return format(compiled, std::forward<Args>(args)...); | |||
} | |||
template <typename OutputIt, typename CompiledFormat, typename... Args, | |||
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format, | |||
CompiledFormat>::value)> | |||
OutputIt format_to(OutputIt out, const CompiledFormat& cf, | |||
const Args&... args) { | |||
using char_type = typename CompiledFormat::char_type; | |||
using context = format_context_t<OutputIt, char_type>; | |||
return detail::cf::vformat_to<context>(out, cf, | |||
make_format_args<context>(args...)); | |||
} | |||
template <typename OutputIt, typename S, typename... Args, | |||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> | |||
OutputIt format_to(OutputIt out, const S&, const Args&... args) { | |||
constexpr auto compiled = detail::compile<Args...>(S()); | |||
return format_to(out, compiled, args...); | |||
} | |||
template <typename OutputIt, typename CompiledFormat, typename... Args> | |||
auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf, | |||
const Args&... args) -> | |||
typename std::enable_if< | |||
detail::is_output_iterator<OutputIt, | |||
typename CompiledFormat::char_type>::value && | |||
std::is_base_of<detail::basic_compiled_format, | |||
CompiledFormat>::value, | |||
format_to_n_result<OutputIt>>::type { | |||
auto it = | |||
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...); | |||
return {it.base(), it.count()}; | |||
} | |||
template <typename OutputIt, typename S, typename... Args, | |||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> | |||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&, | |||
const Args&... args) { | |||
constexpr auto compiled = detail::compile<Args...>(S()); | |||
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), compiled, | |||
args...); | |||
return {it.base(), it.count()}; | |||
} | |||
template <typename CompiledFormat, typename... Args> | |||
size_t formatted_size(const CompiledFormat& cf, const Args&... args) { | |||
return format_to(detail::counting_iterator(), cf, args...).count(); | |||
} | |||
FMT_END_NAMESPACE | |||
#endif // FMT_COMPILE_H_ |
@@ -0,0 +1,64 @@ | |||
// Formatting library for C++ - std::locale support | |||
// | |||
// Copyright (c) 2012 - present, Victor Zverovich | |||
// All rights reserved. | |||
// | |||
// For the license information refer to format.h. | |||
#ifndef FMT_LOCALE_H_ | |||
#define FMT_LOCALE_H_ | |||
#include <locale> | |||
#include "format.h" | |||
FMT_BEGIN_NAMESPACE | |||
namespace detail { | |||
template <typename Char> | |||
std::basic_string<Char> vformat( | |||
const std::locale& loc, basic_string_view<Char> format_str, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { | |||
basic_memory_buffer<Char> buffer; | |||
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc)); | |||
return fmt::to_string(buffer); | |||
} | |||
} // namespace detail | |||
template <typename S, typename Char = char_t<S>> | |||
inline std::basic_string<Char> vformat( | |||
const std::locale& loc, const S& format_str, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { | |||
return detail::vformat(loc, to_string_view(format_str), args); | |||
} | |||
template <typename S, typename... Args, typename Char = char_t<S>> | |||
inline std::basic_string<Char> format(const std::locale& loc, | |||
const S& format_str, Args&&... args) { | |||
return detail::vformat(loc, to_string_view(format_str), | |||
fmt::make_args_checked<Args...>(format_str, args...)); | |||
} | |||
template <typename S, typename OutputIt, typename... Args, | |||
typename Char = char_t<S>, | |||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)> | |||
inline OutputIt vformat_to( | |||
OutputIt out, const std::locale& loc, const S& format_str, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { | |||
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out)); | |||
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc)); | |||
return detail::get_iterator(buf); | |||
} | |||
template <typename OutputIt, typename S, typename... Args, | |||
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value> | |||
inline auto format_to(OutputIt out, const std::locale& loc, | |||
const S& format_str, Args&&... args) -> | |||
typename std::enable_if<enable, OutputIt>::type { | |||
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); | |||
return vformat_to(out, loc, to_string_view(format_str), vargs); | |||
} | |||
FMT_END_NAMESPACE | |||
#endif // FMT_LOCALE_H_ |
@@ -0,0 +1,480 @@ | |||
// Formatting library for C++ - optional OS-specific functionality | |||
// | |||
// Copyright (c) 2012 - present, Victor Zverovich | |||
// All rights reserved. | |||
// | |||
// For the license information refer to format.h. | |||
#ifndef FMT_OS_H_ | |||
#define FMT_OS_H_ | |||
#if defined(__MINGW32__) || defined(__CYGWIN__) | |||
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/. | |||
# undef __STRICT_ANSI__ | |||
#endif | |||
#include <cerrno> | |||
#include <clocale> // for locale_t | |||
#include <cstddef> | |||
#include <cstdio> | |||
#include <cstdlib> // for strtod_l | |||
#if defined __APPLE__ || defined(__FreeBSD__) | |||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X | |||
#endif | |||
#include "format.h" | |||
// UWP doesn't provide _pipe. | |||
#if FMT_HAS_INCLUDE("winapifamily.h") | |||
# include <winapifamily.h> | |||
#endif | |||
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \ | |||
defined(__linux__)) && \ | |||
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) | |||
# include <fcntl.h> // for O_RDONLY | |||
# define FMT_USE_FCNTL 1 | |||
#else | |||
# define FMT_USE_FCNTL 0 | |||
#endif | |||
#ifndef FMT_POSIX | |||
# if defined(_WIN32) && !defined(__MINGW32__) | |||
// Fix warnings about deprecated symbols. | |||
# define FMT_POSIX(call) _##call | |||
# else | |||
# define FMT_POSIX(call) call | |||
# endif | |||
#endif | |||
// Calls to system functions are wrapped in FMT_SYSTEM for testability. | |||
#ifdef FMT_SYSTEM | |||
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call) | |||
#else | |||
# define FMT_SYSTEM(call) ::call | |||
# ifdef _WIN32 | |||
// Fix warnings about deprecated symbols. | |||
# define FMT_POSIX_CALL(call) ::_##call | |||
# else | |||
# define FMT_POSIX_CALL(call) ::call | |||
# endif | |||
#endif | |||
// Retries the expression while it evaluates to error_result and errno | |||
// equals to EINTR. | |||
#ifndef _WIN32 | |||
# define FMT_RETRY_VAL(result, expression, error_result) \ | |||
do { \ | |||
(result) = (expression); \ | |||
} while ((result) == (error_result) && errno == EINTR) | |||
#else | |||
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression) | |||
#endif | |||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) | |||
FMT_BEGIN_NAMESPACE | |||
/** | |||
\rst | |||
A reference to a null-terminated string. It can be constructed from a C | |||
string or ``std::string``. | |||
You can use one of the following type aliases for common character types: | |||
+---------------+-----------------------------+ | |||
| Type | Definition | | |||
+===============+=============================+ | |||
| cstring_view | basic_cstring_view<char> | | |||
+---------------+-----------------------------+ | |||
| wcstring_view | basic_cstring_view<wchar_t> | | |||
+---------------+-----------------------------+ | |||
This class is most useful as a parameter type to allow passing | |||
different types of strings to a function, for example:: | |||
template <typename... Args> | |||
std::string format(cstring_view format_str, const Args & ... args); | |||
format("{}", 42); | |||
format(std::string("{}"), 42); | |||
\endrst | |||
*/ | |||
template <typename Char> class basic_cstring_view { | |||
private: | |||
const Char* data_; | |||
public: | |||
/** Constructs a string reference object from a C string. */ | |||
basic_cstring_view(const Char* s) : data_(s) {} | |||
/** | |||
\rst | |||
Constructs a string reference from an ``std::string`` object. | |||
\endrst | |||
*/ | |||
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {} | |||
/** Returns the pointer to a C string. */ | |||
const Char* c_str() const { return data_; } | |||
}; | |||
using cstring_view = basic_cstring_view<char>; | |||
using wcstring_view = basic_cstring_view<wchar_t>; | |||
// An error code. | |||
class error_code { | |||
private: | |||
int value_; | |||
public: | |||
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {} | |||
int get() const FMT_NOEXCEPT { return value_; } | |||
}; | |||
#ifdef _WIN32 | |||
namespace detail { | |||
// A converter from UTF-16 to UTF-8. | |||
// It is only provided for Windows since other systems support UTF-8 natively. | |||
class utf16_to_utf8 { | |||
private: | |||
memory_buffer buffer_; | |||
public: | |||
utf16_to_utf8() {} | |||
FMT_API explicit utf16_to_utf8(wstring_view s); | |||
operator string_view() const { return string_view(&buffer_[0], size()); } | |||
size_t size() const { return buffer_.size() - 1; } | |||
const char* c_str() const { return &buffer_[0]; } | |||
std::string str() const { return std::string(&buffer_[0], size()); } | |||
// Performs conversion returning a system error code instead of | |||
// throwing exception on conversion error. This method may still throw | |||
// in case of memory allocation error. | |||
FMT_API int convert(wstring_view s); | |||
}; | |||
FMT_API void format_windows_error(buffer<char>& out, int error_code, | |||
string_view message) FMT_NOEXCEPT; | |||
} // namespace detail | |||
/** A Windows error. */ | |||
class windows_error : public system_error { | |||
private: | |||
FMT_API void init(int error_code, string_view format_str, format_args args); | |||
public: | |||
/** | |||
\rst | |||
Constructs a :class:`fmt::windows_error` object with the description | |||
of the form | |||
.. parsed-literal:: | |||
*<message>*: *<system-message>* | |||
where *<message>* is the formatted message and *<system-message>* is the | |||
system message corresponding to the error code. | |||
*error_code* is a Windows error code as given by ``GetLastError``. | |||
If *error_code* is not a valid error code such as -1, the system message | |||
will look like "error -1". | |||
**Example**:: | |||
// This throws a windows_error with the description | |||
// cannot open file 'madeup': The system cannot find the file specified. | |||
// or similar (system message may vary). | |||
const char *filename = "madeup"; | |||
LPOFSTRUCT of = LPOFSTRUCT(); | |||
HFILE file = OpenFile(filename, &of, OF_READ); | |||
if (file == HFILE_ERROR) { | |||
throw fmt::windows_error(GetLastError(), | |||
"cannot open file '{}'", filename); | |||
} | |||
\endrst | |||
*/ | |||
template <typename... Args> | |||
windows_error(int error_code, string_view message, const Args&... args) { | |||
init(error_code, message, make_format_args(args...)); | |||
} | |||
}; | |||
// Reports a Windows error without throwing an exception. | |||
// Can be used to report errors from destructors. | |||
FMT_API void report_windows_error(int error_code, | |||
string_view message) FMT_NOEXCEPT; | |||
#endif // _WIN32 | |||
// A buffered file. | |||
class buffered_file { | |||
private: | |||
FILE* file_; | |||
friend class file; | |||
explicit buffered_file(FILE* f) : file_(f) {} | |||
public: | |||
buffered_file(const buffered_file&) = delete; | |||
void operator=(const buffered_file&) = delete; | |||
// Constructs a buffered_file object which doesn't represent any file. | |||
buffered_file() FMT_NOEXCEPT : file_(nullptr) {} | |||
// Destroys the object closing the file it represents if any. | |||
FMT_API ~buffered_file() FMT_NOEXCEPT; | |||
public: | |||
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) { | |||
other.file_ = nullptr; | |||
} | |||
buffered_file& operator=(buffered_file&& other) { | |||
close(); | |||
file_ = other.file_; | |||
other.file_ = nullptr; | |||
return *this; | |||
} | |||
// Opens a file. | |||
FMT_API buffered_file(cstring_view filename, cstring_view mode); | |||
// Closes the file. | |||
FMT_API void close(); | |||
// Returns the pointer to a FILE object representing this file. | |||
FILE* get() const FMT_NOEXCEPT { return file_; } | |||
// We place parentheses around fileno to workaround a bug in some versions | |||
// of MinGW that define fileno as a macro. | |||
FMT_API int(fileno)() const; | |||
void vprint(string_view format_str, format_args args) { | |||
fmt::vprint(file_, format_str, args); | |||
} | |||
template <typename... Args> | |||
inline void print(string_view format_str, const Args&... args) { | |||
vprint(format_str, make_format_args(args...)); | |||
} | |||
}; | |||
#if FMT_USE_FCNTL | |||
// A file. Closed file is represented by a file object with descriptor -1. | |||
// Methods that are not declared with FMT_NOEXCEPT may throw | |||
// fmt::system_error in case of failure. Note that some errors such as | |||
// closing the file multiple times will cause a crash on Windows rather | |||
// than an exception. You can get standard behavior by overriding the | |||
// invalid parameter handler with _set_invalid_parameter_handler. | |||
class file { | |||
private: | |||
int fd_; // File descriptor. | |||
// Constructs a file object with a given descriptor. | |||
explicit file(int fd) : fd_(fd) {} | |||
public: | |||
// Possible values for the oflag argument to the constructor. | |||
enum { | |||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. | |||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. | |||
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing. | |||
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist. | |||
APPEND = FMT_POSIX(O_APPEND) // Open in append mode. | |||
}; | |||
// Constructs a file object which doesn't represent any file. | |||
file() FMT_NOEXCEPT : fd_(-1) {} | |||
// Opens a file and constructs a file object representing this file. | |||
FMT_API file(cstring_view path, int oflag); | |||
public: | |||
file(const file&) = delete; | |||
void operator=(const file&) = delete; | |||
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } | |||
file& operator=(file&& other) FMT_NOEXCEPT { | |||
close(); | |||
fd_ = other.fd_; | |||
other.fd_ = -1; | |||
return *this; | |||
} | |||
// Destroys the object closing the file it represents if any. | |||
FMT_API ~file() FMT_NOEXCEPT; | |||
// Returns the file descriptor. | |||
int descriptor() const FMT_NOEXCEPT { return fd_; } | |||
// Closes the file. | |||
FMT_API void close(); | |||
// Returns the file size. The size has signed type for consistency with | |||
// stat::st_size. | |||
FMT_API long long size() const; | |||
// Attempts to read count bytes from the file into the specified buffer. | |||
FMT_API size_t read(void* buffer, size_t count); | |||
// Attempts to write count bytes from the specified buffer to the file. | |||
FMT_API size_t write(const void* buffer, size_t count); | |||
// Duplicates a file descriptor with the dup function and returns | |||
// the duplicate as a file object. | |||
FMT_API static file dup(int fd); | |||
// Makes fd be the copy of this file descriptor, closing fd first if | |||
// necessary. | |||
FMT_API void dup2(int fd); | |||
// Makes fd be the copy of this file descriptor, closing fd first if | |||
// necessary. | |||
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT; | |||
// Creates a pipe setting up read_end and write_end file objects for reading | |||
// and writing respectively. | |||
FMT_API static void pipe(file& read_end, file& write_end); | |||
// Creates a buffered_file object associated with this file and detaches | |||
// this file object from the file. | |||
FMT_API buffered_file fdopen(const char* mode); | |||
}; | |||
// Returns the memory page size. | |||
long getpagesize(); | |||
namespace detail { | |||
struct buffer_size { | |||
size_t value = 0; | |||
buffer_size operator=(size_t val) const { | |||
auto bs = buffer_size(); | |||
bs.value = val; | |||
return bs; | |||
} | |||
}; | |||
struct ostream_params { | |||
int oflag = file::WRONLY | file::CREATE; | |||
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; | |||
ostream_params() {} | |||
template <typename... T> | |||
ostream_params(T... params, int oflag) : ostream_params(params...) { | |||
this->oflag = oflag; | |||
} | |||
template <typename... T> | |||
ostream_params(T... params, detail::buffer_size bs) | |||
: ostream_params(params...) { | |||
this->buffer_size = bs.value; | |||
} | |||
}; | |||
} // namespace detail | |||
static constexpr detail::buffer_size buffer_size; | |||
// A fast output stream which is not thread-safe. | |||
class ostream final : private detail::buffer<char> { | |||
private: | |||
file file_; | |||
void flush() { | |||
if (size() == 0) return; | |||
file_.write(data(), size()); | |||
clear(); | |||
} | |||
FMT_API void grow(size_t) override final; | |||
ostream(cstring_view path, const detail::ostream_params& params) | |||
: file_(path, params.oflag) { | |||
set(new char[params.buffer_size], params.buffer_size); | |||
} | |||
public: | |||
ostream(ostream&& other) | |||
: detail::buffer<char>(other.data(), other.size(), other.capacity()), | |||
file_(std::move(other.file_)) { | |||
other.set(nullptr, 0); | |||
} | |||
~ostream() { | |||
flush(); | |||
delete[] data(); | |||
} | |||
template <typename... T> | |||
friend ostream output_file(cstring_view path, T... params); | |||
void close() { | |||
flush(); | |||
file_.close(); | |||
} | |||
template <typename S, typename... Args> | |||
void print(const S& format_str, const Args&... args) { | |||
format_to(detail::buffer_appender<char>(*this), format_str, args...); | |||
} | |||
}; | |||
/** | |||
Opens a file for writing. Supported parameters passed in `params`: | |||
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default) | |||
* ``buffer_size=<integer>``: Output buffer size | |||
*/ | |||
template <typename... T> | |||
inline ostream output_file(cstring_view path, T... params) { | |||
return {path, detail::ostream_params(params...)}; | |||
} | |||
#endif // FMT_USE_FCNTL | |||
#ifdef FMT_LOCALE | |||
// A "C" numeric locale. | |||
class locale { | |||
private: | |||
# ifdef _WIN32 | |||
using locale_t = _locale_t; | |||
static void freelocale(locale_t loc) { _free_locale(loc); } | |||
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) { | |||
return _strtod_l(nptr, endptr, loc); | |||
} | |||
# endif | |||
locale_t locale_; | |||
public: | |||
using type = locale_t; | |||
locale(const locale&) = delete; | |||
void operator=(const locale&) = delete; | |||
locale() { | |||
# ifndef _WIN32 | |||
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr)); | |||
# else | |||
locale_ = _create_locale(LC_NUMERIC, "C"); | |||
# endif | |||
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale")); | |||
} | |||
~locale() { freelocale(locale_); } | |||
type get() const { return locale_; } | |||
// Converts string to floating-point number and advances str past the end | |||
// of the parsed input. | |||
double strtod(const char*& str) const { | |||
char* end = nullptr; | |||
double result = strtod_l(str, &end, locale_); | |||
str = end; | |||
return result; | |||
} | |||
}; | |||
using Locale FMT_DEPRECATED_ALIAS = locale; | |||
#endif // FMT_LOCALE | |||
FMT_END_NAMESPACE | |||
#endif // FMT_OS_H_ |
@@ -0,0 +1,177 @@ | |||
// Formatting library for C++ - std::ostream support | |||
// | |||
// Copyright (c) 2012 - present, Victor Zverovich | |||
// All rights reserved. | |||
// | |||
// For the license information refer to format.h. | |||
#ifndef FMT_OSTREAM_H_ | |||
#define FMT_OSTREAM_H_ | |||
#include <ostream> | |||
#include "format.h" | |||
FMT_BEGIN_NAMESPACE | |||
template <typename Char> class basic_printf_parse_context; | |||
template <typename OutputIt, typename Char> class basic_printf_context; | |||
namespace detail { | |||
template <class Char> class formatbuf : public std::basic_streambuf<Char> { | |||
private: | |||
using int_type = typename std::basic_streambuf<Char>::int_type; | |||
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: | |||
template <typename U> | |||
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>() | |||
<< std::declval<U>()), | |||
void_t<>>::value> | |||
test(int); | |||
template <typename> static std::false_type test(...); | |||
using result = decltype(test<T>(0)); | |||
public: | |||
static const bool value = result::value; | |||
}; | |||
// Write the content of buf to os. | |||
template <typename Char> | |||
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { | |||
const Char* buf_data = buf.data(); | |||
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; | |||
unsigned_streamsize size = buf.size(); | |||
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>()); | |||
do { | |||
unsigned_streamsize n = size <= max_size ? size : max_size; | |||
os.write(buf_data, static_cast<std::streamsize>(n)); | |||
buf_data += n; | |||
size -= n; | |||
} while (size != 0); | |||
} | |||
template <typename Char, typename T> | |||
void format_value(buffer<Char>& buf, const T& value, | |||
locale_ref loc = locale_ref()) { | |||
formatbuf<Char> format_buf(buf); | |||
std::basic_ostream<Char> output(&format_buf); | |||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) | |||
if (loc) output.imbue(loc.get<std::locale>()); | |||
#endif | |||
output << value; | |||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); | |||
buf.try_resize(buf.size()); | |||
} | |||
// Formats an object of type T that has an overloaded ostream operator<<. | |||
template <typename T, typename Char> | |||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> | |||
: private formatter<basic_string_view<Char>, Char> { | |||
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) | |||
-> 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> | |||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) | |||
-> OutputIt { | |||
basic_memory_buffer<Char> buffer; | |||
format_value(buffer, value, ctx.locale()); | |||
basic_string_view<Char> str(buffer.data(), buffer.size()); | |||
return formatter<basic_string_view<Char>, Char>::format(str, ctx); | |||
} | |||
template <typename OutputIt> | |||
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx) | |||
-> OutputIt { | |||
basic_memory_buffer<Char> buffer; | |||
format_value(buffer, value, ctx.locale()); | |||
return std::copy(buffer.begin(), buffer.end(), ctx.out()); | |||
} | |||
}; | |||
} // namespace detail | |||
template <typename Char> | |||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, | |||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { | |||
basic_memory_buffer<Char> buffer; | |||
detail::vformat_to(buffer, format_str, args); | |||
detail::write_buffer(os, buffer); | |||
} | |||
/** | |||
\rst | |||
Prints formatted data to the stream *os*. | |||
**Example**:: | |||
fmt::print(cerr, "Don't {}!", "panic"); | |||
\endrst | |||
*/ | |||
template <typename S, typename... Args, | |||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> | |||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) { | |||
vprint(os, to_string_view(format_str), | |||
fmt::make_args_checked<Args...>(format_str, args...)); | |||
} | |||
FMT_END_NAMESPACE | |||
#endif // FMT_OSTREAM_H_ |
@@ -0,0 +1,2 @@ | |||
#include "os.h" | |||
#warning "fmt/posix.h is deprecated; use fmt/os.h instead" |
@@ -0,0 +1,751 @@ | |||
// Formatting library for C++ - legacy printf implementation | |||
// | |||
// Copyright (c) 2012 - 2016, Victor Zverovich | |||
// All rights reserved. | |||
// | |||
// For the license information refer to format.h. | |||
#ifndef FMT_PRINTF_H_ | |||
#define FMT_PRINTF_H_ | |||
#include <algorithm> // std::max | |||
#include <limits> // std::numeric_limits | |||
#include "ostream.h" | |||
FMT_BEGIN_NAMESPACE | |||
namespace detail { | |||
// Checks if a value fits in int - used to avoid warnings about comparing | |||
// signed and unsigned integers. | |||
template <bool IsSigned> struct int_checker { | |||
template <typename T> static bool fits_in_int(T value) { | |||
unsigned max = max_value<int>(); | |||
return value <= max; | |||
} | |||
static bool fits_in_int(bool) { return true; } | |||
}; | |||
template <> struct int_checker<true> { | |||
template <typename T> static bool fits_in_int(T value) { | |||
return value >= (std::numeric_limits<int>::min)() && | |||
value <= max_value<int>(); | |||
} | |||
static bool fits_in_int(int) { return true; } | |||
}; | |||
class printf_precision_handler { | |||
public: | |||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> | |||
int operator()(T value) { | |||
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) | |||
FMT_THROW(format_error("number is too big")); | |||
return (std::max)(static_cast<int>(value), 0); | |||
} | |||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> | |||
int operator()(T) { | |||
FMT_THROW(format_error("precision is not integer")); | |||
return 0; | |||
} | |||
}; | |||
// An argument visitor that returns true iff arg is a zero integer. | |||
class is_zero_int { | |||
public: | |||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> | |||
bool operator()(T value) { | |||
return value == 0; | |||
} | |||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> | |||
bool operator()(T) { | |||
return false; | |||
} | |||
}; | |||
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {}; | |||
template <> struct make_unsigned_or_bool<bool> { using type = bool; }; | |||
template <typename T, typename Context> class arg_converter { | |||
private: | |||
using char_type = typename Context::char_type; | |||
basic_format_arg<Context>& arg_; | |||
char_type type_; | |||
public: | |||
arg_converter(basic_format_arg<Context>& arg, char_type type) | |||
: arg_(arg), type_(type) {} | |||
void operator()(bool value) { | |||
if (type_ != 's') operator()<bool>(value); | |||
} | |||
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)> | |||
void operator()(U value) { | |||
bool is_signed = type_ == 'd' || type_ == 'i'; | |||
using target_type = conditional_t<std::is_same<T, void>::value, U, T>; | |||
if (const_check(sizeof(target_type) <= sizeof(int))) { | |||
// Extra casts are used to silence warnings. | |||
if (is_signed) { | |||
arg_ = detail::make_arg<Context>( | |||
static_cast<int>(static_cast<target_type>(value))); | |||
} else { | |||
using unsigned_type = typename make_unsigned_or_bool<target_type>::type; | |||
arg_ = detail::make_arg<Context>( | |||
static_cast<unsigned>(static_cast<unsigned_type>(value))); | |||
} | |||
} else { | |||
if (is_signed) { | |||
// glibc's printf doesn't sign extend arguments of smaller types: | |||
// std::printf("%lld", -42); // prints "4294967254" | |||
// but we don't have to do the same because it's a UB. | |||
arg_ = detail::make_arg<Context>(static_cast<long long>(value)); | |||
} else { | |||
arg_ = detail::make_arg<Context>( | |||
static_cast<typename make_unsigned_or_bool<U>::type>(value)); | |||
} | |||
} | |||
} | |||
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)> | |||
void operator()(U) {} // No conversion needed for non-integral types. | |||
}; | |||
// Converts an integer argument to T for printf, if T is an integral type. | |||
// If T is void, the argument is converted to corresponding signed or unsigned | |||
// type depending on the type specifier: 'd' and 'i' - signed, other - | |||
// unsigned). | |||
template <typename T, typename Context, typename Char> | |||
void convert_arg(basic_format_arg<Context>& arg, Char type) { | |||
visit_format_arg(arg_converter<T, Context>(arg, type), arg); | |||
} | |||
// Converts an integer argument to char for printf. | |||
template <typename Context> class char_converter { | |||
private: | |||
basic_format_arg<Context>& arg_; | |||
public: | |||
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {} | |||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> | |||
void operator()(T value) { | |||
arg_ = detail::make_arg<Context>( | |||
static_cast<typename Context::char_type>(value)); | |||
} | |||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> | |||
void operator()(T) {} // No conversion needed for non-integral types. | |||
}; | |||
// An argument visitor that return a pointer to a C string if argument is a | |||
// string or null otherwise. | |||
template <typename Char> struct get_cstring { | |||
template <typename T> const Char* operator()(T) { return nullptr; } | |||
const Char* operator()(const Char* s) { return s; } | |||
}; | |||
// Checks if an argument is a valid printf width specifier and sets | |||
// left alignment if it is negative. | |||
template <typename Char> class printf_width_handler { | |||
private: | |||
using format_specs = basic_format_specs<Char>; | |||
format_specs& specs_; | |||
public: | |||
explicit printf_width_handler(format_specs& specs) : specs_(specs) {} | |||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> | |||
unsigned operator()(T value) { | |||
auto width = static_cast<uint32_or_64_or_128_t<T>>(value); | |||
if (detail::is_negative(value)) { | |||
specs_.align = align::left; | |||
width = 0 - width; | |||
} | |||
unsigned int_max = max_value<int>(); | |||
if (width > int_max) FMT_THROW(format_error("number is too big")); | |||
return static_cast<unsigned>(width); | |||
} | |||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> | |||
unsigned operator()(T) { | |||
FMT_THROW(format_error("width is not integer")); | |||
return 0; | |||
} | |||
}; | |||
template <typename Char, typename Context> | |||
void vprintf(buffer<Char>& buf, basic_string_view<Char> format, | |||
basic_format_args<Context> args) { | |||
Context(buffer_appender<Char>(buf), format, args).format(); | |||
} | |||
} // namespace detail | |||
// For printing into memory_buffer. | |||
template <typename Char, typename Context> | |||
FMT_DEPRECATED void printf(detail::buffer<Char>& buf, | |||
basic_string_view<Char> format, | |||
basic_format_args<Context> args) { | |||
return detail::vprintf(buf, format, args); | |||
} | |||
using detail::vprintf; | |||
template <typename Char> | |||
class basic_printf_parse_context : public basic_format_parse_context<Char> { | |||
using basic_format_parse_context<Char>::basic_format_parse_context; | |||
}; | |||
template <typename OutputIt, typename Char> class basic_printf_context; | |||
/** | |||
\rst | |||
The ``printf`` argument formatter. | |||
\endrst | |||
*/ | |||
template <typename OutputIt, typename Char> | |||
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> { | |||
public: | |||
using iterator = OutputIt; | |||
private: | |||
using char_type = Char; | |||
using base = detail::arg_formatter_base<OutputIt, Char>; | |||
using context_type = basic_printf_context<OutputIt, Char>; | |||
context_type& context_; | |||
void write_null_pointer(char) { | |||
this->specs()->type = 0; | |||
this->write("(nil)"); | |||
} | |||
void write_null_pointer(wchar_t) { | |||
this->specs()->type = 0; | |||
this->write(L"(nil)"); | |||
} | |||
public: | |||
using format_specs = typename base::format_specs; | |||
/** | |||
\rst | |||
Constructs an argument formatter object. | |||
*buffer* is a reference to the output buffer and *specs* contains format | |||
specifier information for standard argument types. | |||
\endrst | |||
*/ | |||
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx) | |||
: base(iter, &specs, detail::locale_ref()), context_(ctx) {} | |||
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)> | |||
iterator operator()(T value) { | |||
// MSVC2013 fails to compile separate overloads for bool and char_type so | |||
// use std::is_same instead. | |||
if (std::is_same<T, bool>::value) { | |||
format_specs& fmt_specs = *this->specs(); | |||
if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0); | |||
fmt_specs.type = 0; | |||
this->write(value != 0); | |||
} else if (std::is_same<T, char_type>::value) { | |||
format_specs& fmt_specs = *this->specs(); | |||
if (fmt_specs.type && fmt_specs.type != 'c') | |||
return (*this)(static_cast<int>(value)); | |||
fmt_specs.sign = sign::none; | |||
fmt_specs.alt = false; | |||
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types. | |||
// align::numeric needs to be overwritten here since the '0' flag is | |||
// ignored for non-numeric types | |||
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) | |||
fmt_specs.align = align::right; | |||
return base::operator()(value); | |||
} else { | |||
return base::operator()(value); | |||
} | |||
return this->out(); | |||
} | |||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> | |||
iterator operator()(T value) { | |||
return base::operator()(value); | |||
} | |||
/** Formats a null-terminated C string. */ | |||
iterator operator()(const char* value) { | |||
if (value) | |||
base::operator()(value); | |||
else if (this->specs()->type == 'p') | |||
write_null_pointer(char_type()); | |||
else | |||
this->write("(null)"); | |||
return this->out(); | |||
} | |||
/** Formats a null-terminated wide C string. */ | |||
iterator operator()(const wchar_t* value) { | |||
if (value) | |||
base::operator()(value); | |||
else if (this->specs()->type == 'p') | |||
write_null_pointer(char_type()); | |||
else | |||
this->write(L"(null)"); | |||
return this->out(); | |||
} | |||
iterator operator()(basic_string_view<char_type> value) { | |||
return base::operator()(value); | |||
} | |||
iterator operator()(monostate value) { return base::operator()(value); } | |||
/** Formats a pointer. */ | |||
iterator operator()(const void* value) { | |||
if (value) return base::operator()(value); | |||
this->specs()->type = 0; | |||
write_null_pointer(char_type()); | |||
return this->out(); | |||
} | |||
/** Formats an argument of a custom (user-defined) type. */ | |||
iterator operator()(typename basic_format_arg<context_type>::handle handle) { | |||
handle.format(context_.parse_context(), context_); | |||
return this->out(); | |||
} | |||
}; | |||
template <typename T> struct printf_formatter { | |||
printf_formatter() = delete; | |||
template <typename ParseContext> | |||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |||
return ctx.begin(); | |||
} | |||
template <typename FormatContext> | |||
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) { | |||
detail::format_value(detail::get_container(ctx.out()), value); | |||
return ctx.out(); | |||
} | |||
}; | |||
/** | |||
This template formats data and writes the output through an output iterator. | |||
*/ | |||
template <typename OutputIt, typename Char> class basic_printf_context { | |||
public: | |||
/** The character type for the output. */ | |||
using char_type = Char; | |||
using iterator = OutputIt; | |||
using format_arg = basic_format_arg<basic_printf_context>; | |||
using parse_context_type = basic_printf_parse_context<Char>; | |||
template <typename T> using formatter_type = printf_formatter<T>; | |||
private: | |||
using format_specs = basic_format_specs<char_type>; | |||
OutputIt out_; | |||
basic_format_args<basic_printf_context> args_; | |||
parse_context_type parse_ctx_; | |||
static void parse_flags(format_specs& specs, const Char*& it, | |||
const Char* end); | |||
// Returns the argument with specified index or, if arg_index is -1, the next | |||
// argument. | |||
format_arg get_arg(int arg_index = -1); | |||
// Parses argument index, flags and width and returns the argument index. | |||
int parse_header(const Char*& it, const Char* end, format_specs& specs); | |||
public: | |||
/** | |||
\rst | |||
Constructs a ``printf_context`` object. References to the arguments are | |||
stored in the context object so make sure they have appropriate lifetimes. | |||
\endrst | |||
*/ | |||
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str, | |||
basic_format_args<basic_printf_context> args) | |||
: out_(out), args_(args), parse_ctx_(format_str) {} | |||
OutputIt out() { return out_; } | |||
void advance_to(OutputIt it) { out_ = it; } | |||
detail::locale_ref locale() { return {}; } | |||
format_arg arg(int id) const { return args_.get(id); } | |||
parse_context_type& parse_context() { return parse_ctx_; } | |||
FMT_CONSTEXPR void on_error(const char* message) { | |||
parse_ctx_.on_error(message); | |||
} | |||
/** Formats stored arguments and writes the output to the range. */ | |||
template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>> | |||
OutputIt format(); | |||
}; | |||
template <typename OutputIt, typename Char> | |||
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs, | |||
const Char*& it, | |||
const Char* end) { | |||
for (; it != end; ++it) { | |||
switch (*it) { | |||
case '-': | |||
specs.align = align::left; | |||
break; | |||
case '+': | |||
specs.sign = sign::plus; | |||
break; | |||
case '0': | |||
specs.fill[0] = '0'; | |||
break; | |||
case ' ': | |||
if (specs.sign != sign::plus) { | |||
specs.sign = sign::space; | |||
} | |||
break; | |||
case '#': | |||
specs.alt = true; | |||
break; | |||
default: | |||
return; | |||
} | |||
} | |||
} | |||
template <typename OutputIt, typename Char> | |||
typename basic_printf_context<OutputIt, Char>::format_arg | |||
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) { | |||
if (arg_index < 0) | |||
arg_index = parse_ctx_.next_arg_id(); | |||
else | |||
parse_ctx_.check_arg_id(--arg_index); | |||
return detail::get_arg(*this, arg_index); | |||
} | |||
template <typename OutputIt, typename Char> | |||
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it, | |||
const Char* end, | |||
format_specs& specs) { | |||
int arg_index = -1; | |||
char_type c = *it; | |||
if (c >= '0' && c <= '9') { | |||
// Parse an argument index (if followed by '$') or a width possibly | |||
// preceded with '0' flag(s). | |||
detail::error_handler eh; | |||
int value = parse_nonnegative_int(it, end, eh); | |||
if (it != end && *it == '$') { // value is an argument index | |||
++it; | |||
arg_index = value; | |||
} else { | |||
if (c == '0') specs.fill[0] = '0'; | |||
if (value != 0) { | |||
// Nonzero value means that we parsed width and don't need to | |||
// parse it or flags again, so return now. | |||
specs.width = value; | |||
return arg_index; | |||
} | |||
} | |||
} | |||
parse_flags(specs, it, end); | |||
// Parse width. | |||
if (it != end) { | |||
if (*it >= '0' && *it <= '9') { | |||
detail::error_handler eh; | |||
specs.width = parse_nonnegative_int(it, end, eh); | |||
} else if (*it == '*') { | |||
++it; | |||
specs.width = static_cast<int>(visit_format_arg( | |||
detail::printf_width_handler<char_type>(specs), get_arg())); | |||
} | |||
} | |||
return arg_index; | |||
} | |||
template <typename OutputIt, typename Char> | |||
template <typename ArgFormatter> | |||
OutputIt basic_printf_context<OutputIt, Char>::format() { | |||
auto out = this->out(); | |||
const Char* start = parse_ctx_.begin(); | |||
const Char* end = parse_ctx_.end(); | |||
auto it = start; | |||
while (it != end) { | |||
char_type c = *it++; | |||
if (c != '%') continue; | |||
if (it != end && *it == c) { | |||
out = std::copy(start, it, out); | |||
start = ++it; | |||
continue; | |||
} | |||
out = std::copy(start, it - 1, out); | |||
format_specs specs; | |||
specs.align = align::right; | |||
// Parse argument index, flags and width. | |||
int arg_index = parse_header(it, end, specs); | |||
if (arg_index == 0) on_error("argument not found"); | |||
// Parse precision. | |||
if (it != end && *it == '.') { | |||
++it; | |||
c = it != end ? *it : 0; | |||
if ('0' <= c && c <= '9') { | |||
detail::error_handler eh; | |||
specs.precision = parse_nonnegative_int(it, end, eh); | |||
} else if (c == '*') { | |||
++it; | |||
specs.precision = static_cast<int>( | |||
visit_format_arg(detail::printf_precision_handler(), get_arg())); | |||
} else { | |||
specs.precision = 0; | |||
} | |||
} | |||
format_arg arg = get_arg(arg_index); | |||
// For d, i, o, u, x, and X conversion specifiers, if a precision is | |||
// specified, the '0' flag is ignored | |||
if (specs.precision >= 0 && arg.is_integral()) | |||
specs.fill[0] = | |||
' '; // Ignore '0' flag for non-numeric types or if '-' present. | |||
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) { | |||
auto str = visit_format_arg(detail::get_cstring<Char>(), arg); | |||
auto str_end = str + specs.precision; | |||
auto nul = std::find(str, str_end, Char()); | |||
arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>( | |||
str, | |||
detail::to_unsigned(nul != str_end ? nul - str : specs.precision))); | |||
} | |||
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg)) | |||
specs.alt = false; | |||
if (specs.fill[0] == '0') { | |||
if (arg.is_arithmetic() && specs.align != align::left) | |||
specs.align = align::numeric; | |||
else | |||
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-' | |||
// flag is also present. | |||
} | |||
// Parse length and convert the argument to the required type. | |||
c = it != end ? *it++ : 0; | |||
char_type t = it != end ? *it : 0; | |||
using detail::convert_arg; | |||
switch (c) { | |||
case 'h': | |||
if (t == 'h') { | |||
++it; | |||
t = it != end ? *it : 0; | |||
convert_arg<signed char>(arg, t); | |||
} else { | |||
convert_arg<short>(arg, t); | |||
} | |||
break; | |||
case 'l': | |||
if (t == 'l') { | |||
++it; | |||
t = it != end ? *it : 0; | |||
convert_arg<long long>(arg, t); | |||
} else { | |||
convert_arg<long>(arg, t); | |||
} | |||
break; | |||
case 'j': | |||
convert_arg<intmax_t>(arg, t); | |||
break; | |||
case 'z': | |||
convert_arg<size_t>(arg, t); | |||
break; | |||
case 't': | |||
convert_arg<std::ptrdiff_t>(arg, t); | |||
break; | |||
case 'L': | |||
// printf produces garbage when 'L' is omitted for long double, no | |||
// need to do the same. | |||
break; | |||
default: | |||
--it; | |||
convert_arg<void>(arg, c); | |||
} | |||
// Parse type. | |||
if (it == end) FMT_THROW(format_error("invalid format string")); | |||
specs.type = static_cast<char>(*it++); | |||
if (arg.is_integral()) { | |||
// Normalize type. | |||
switch (specs.type) { | |||
case 'i': | |||
case 'u': | |||
specs.type = 'd'; | |||
break; | |||
case 'c': | |||
visit_format_arg(detail::char_converter<basic_printf_context>(arg), | |||
arg); | |||
break; | |||
} | |||
} | |||
start = it; | |||
// Format argument. | |||
out = visit_format_arg(ArgFormatter(out, specs, *this), arg); | |||
} | |||
return std::copy(start, it, out); | |||
} | |||
template <typename Char> | |||
using basic_printf_context_t = | |||
basic_printf_context<detail::buffer_appender<Char>, Char>; | |||
using printf_context = basic_printf_context_t<char>; | |||
using wprintf_context = basic_printf_context_t<wchar_t>; | |||
using printf_args = basic_format_args<printf_context>; | |||
using wprintf_args = basic_format_args<wprintf_context>; | |||
/** | |||
\rst | |||
Constructs an `~fmt::format_arg_store` object that contains references to | |||
arguments and can be implicitly converted to `~fmt::printf_args`. | |||
\endrst | |||
*/ | |||
template <typename... Args> | |||
inline format_arg_store<printf_context, Args...> make_printf_args( | |||
const Args&... args) { | |||
return {args...}; | |||
} | |||
/** | |||
\rst | |||
Constructs an `~fmt::format_arg_store` object that contains references to | |||
arguments and can be implicitly converted to `~fmt::wprintf_args`. | |||
\endrst | |||
*/ | |||
template <typename... Args> | |||
inline format_arg_store<wprintf_context, Args...> make_wprintf_args( | |||
const Args&... args) { | |||
return {args...}; | |||
} | |||
template <typename S, typename Char = char_t<S>> | |||
inline std::basic_string<Char> vsprintf( | |||
const S& format, | |||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { | |||
basic_memory_buffer<Char> buffer; | |||
vprintf(buffer, to_string_view(format), args); | |||
return to_string(buffer); | |||
} | |||
/** | |||
\rst | |||
Formats arguments and returns the result as a string. | |||
**Example**:: | |||
std::string message = fmt::sprintf("The answer is %d", 42); | |||
\endrst | |||
*/ | |||
template <typename S, typename... Args, | |||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> | |||
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) { | |||
using context = basic_printf_context_t<Char>; | |||
return vsprintf(to_string_view(format), make_format_args<context>(args...)); | |||
} | |||
template <typename S, typename Char = char_t<S>> | |||
inline int vfprintf( | |||
std::FILE* f, const S& format, | |||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { | |||
basic_memory_buffer<Char> buffer; | |||
vprintf(buffer, to_string_view(format), args); | |||
size_t size = buffer.size(); | |||
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size | |||
? -1 | |||
: static_cast<int>(size); | |||
} | |||
/** | |||
\rst | |||
Prints formatted data to the file *f*. | |||
**Example**:: | |||
fmt::fprintf(stderr, "Don't %s!", "panic"); | |||
\endrst | |||
*/ | |||
template <typename S, typename... Args, | |||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> | |||
inline int fprintf(std::FILE* f, const S& format, const Args&... args) { | |||
using context = basic_printf_context_t<Char>; | |||
return vfprintf(f, to_string_view(format), | |||
make_format_args<context>(args...)); | |||
} | |||
template <typename S, typename Char = char_t<S>> | |||
inline int vprintf( | |||
const S& format, | |||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { | |||
return vfprintf(stdout, to_string_view(format), args); | |||
} | |||
/** | |||
\rst | |||
Prints formatted data to ``stdout``. | |||
**Example**:: | |||
fmt::printf("Elapsed time: %.2f seconds", 1.23); | |||
\endrst | |||
*/ | |||
template <typename S, typename... Args, | |||
FMT_ENABLE_IF(detail::is_string<S>::value)> | |||
inline int printf(const S& format_str, const Args&... args) { | |||
using context = basic_printf_context_t<char_t<S>>; | |||
return vprintf(to_string_view(format_str), | |||
make_format_args<context>(args...)); | |||
} | |||
template <typename S, typename Char = char_t<S>> | |||
inline int vfprintf( | |||
std::basic_ostream<Char>& os, const S& format, | |||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { | |||
basic_memory_buffer<Char> buffer; | |||
vprintf(buffer, to_string_view(format), args); | |||
detail::write_buffer(os, buffer); | |||
return static_cast<int>(buffer.size()); | |||
} | |||
/** Formats arguments and writes the output to the range. */ | |||
template <typename ArgFormatter, typename Char, | |||
typename Context = | |||
basic_printf_context<typename ArgFormatter::iterator, Char>> | |||
typename ArgFormatter::iterator vprintf( | |||
detail::buffer<Char>& out, basic_string_view<Char> format_str, | |||
basic_format_args<type_identity_t<Context>> args) { | |||
typename ArgFormatter::iterator iter(out); | |||
Context(iter, format_str, args).template format<ArgFormatter>(); | |||
return iter; | |||
} | |||
/** | |||
\rst | |||
Prints formatted data to the stream *os*. | |||
**Example**:: | |||
fmt::fprintf(cerr, "Don't %s!", "panic"); | |||
\endrst | |||
*/ | |||
template <typename S, typename... Args, typename Char = char_t<S>> | |||
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str, | |||
const Args&... args) { | |||
using context = basic_printf_context_t<Char>; | |||
return vfprintf(os, to_string_view(format_str), | |||
make_format_args<context>(args...)); | |||
} | |||
FMT_END_NAMESPACE | |||
#endif // FMT_PRINTF_H_ |
@@ -0,0 +1,396 @@ | |||
// Formatting library for C++ - experimental range support | |||
// | |||
// Copyright (c) 2012 - present, Victor Zverovich | |||
// All rights reserved. | |||
// | |||
// For the license information refer to format.h. | |||
// | |||
// Copyright (c) 2018 - present, Remotion (Igor Schulz) | |||
// All Rights Reserved | |||
// {fmt} support for ranges, containers and types tuple interface. | |||
#ifndef FMT_RANGES_H_ | |||
#define FMT_RANGES_H_ | |||
#include <initializer_list> | |||
#include <type_traits> | |||
#include "format.h" | |||
// output only up to N items from the range. | |||
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT | |||
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256 | |||
#endif | |||
FMT_BEGIN_NAMESPACE | |||
template <typename Char> struct formatting_base { | |||
template <typename ParseContext> | |||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |||
return ctx.begin(); | |||
} | |||
}; | |||
template <typename Char, typename Enable = void> | |||
struct formatting_range : formatting_base<Char> { | |||
static FMT_CONSTEXPR_DECL const size_t range_length_limit = | |||
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the | |||
// range. | |||
Char prefix; | |||
Char delimiter; | |||
Char postfix; | |||
formatting_range() : prefix('{'), delimiter(','), postfix('}') {} | |||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; | |||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; | |||
}; | |||
template <typename Char, typename Enable = void> | |||
struct formatting_tuple : formatting_base<Char> { | |||
Char prefix; | |||
Char delimiter; | |||
Char postfix; | |||
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {} | |||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; | |||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; | |||
}; | |||
namespace detail { | |||
template <typename RangeT, typename OutputIterator> | |||
OutputIterator copy(const RangeT& range, OutputIterator out) { | |||
for (auto it = range.begin(), end = range.end(); it != end; ++it) | |||
*out++ = *it; | |||
return out; | |||
} | |||
template <typename OutputIterator> | |||
OutputIterator copy(const char* str, OutputIterator out) { | |||
while (*str) *out++ = *str++; | |||
return out; | |||
} | |||
template <typename OutputIterator> | |||
OutputIterator copy(char ch, OutputIterator out) { | |||
*out++ = ch; | |||
return out; | |||
} | |||
/// Return true value if T has std::string interface, like std::string_view. | |||
template <typename T> class is_like_std_string { | |||
template <typename U> | |||
static auto check(U* p) | |||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); | |||
template <typename> static void check(...); | |||
public: | |||
static FMT_CONSTEXPR_DECL const bool value = | |||
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value; | |||
}; | |||
template <typename Char> | |||
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {}; | |||
template <typename... Ts> struct conditional_helper {}; | |||
template <typename T, typename _ = void> struct is_range_ : std::false_type {}; | |||
#if !FMT_MSC_VER || FMT_MSC_VER > 1800 | |||
template <typename T> | |||
struct is_range_< | |||
T, conditional_t<false, | |||
conditional_helper<decltype(std::declval<T>().begin()), | |||
decltype(std::declval<T>().end())>, | |||
void>> : std::true_type {}; | |||
#endif | |||
/// tuple_size and tuple_element check. | |||
template <typename T> class is_tuple_like_ { | |||
template <typename U> | |||
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int()); | |||
template <typename> static void check(...); | |||
public: | |||
static FMT_CONSTEXPR_DECL const bool value = | |||
!std::is_void<decltype(check<T>(nullptr))>::value; | |||
}; | |||
// Check for integer_sequence | |||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 | |||
template <typename T, T... N> | |||
using integer_sequence = std::integer_sequence<T, N...>; | |||
template <size_t... N> using index_sequence = std::index_sequence<N...>; | |||
template <size_t N> using make_index_sequence = std::make_index_sequence<N>; | |||
#else | |||
template <typename T, T... N> struct integer_sequence { | |||
using value_type = T; | |||
static FMT_CONSTEXPR size_t size() { return sizeof...(N); } | |||
}; | |||
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>; | |||
template <typename T, size_t N, T... Ns> | |||
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {}; | |||
template <typename T, T... Ns> | |||
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {}; | |||
template <size_t N> | |||
using make_index_sequence = make_integer_sequence<size_t, N>; | |||
#endif | |||
template <class Tuple, class F, size_t... Is> | |||
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT { | |||
using std::get; | |||
// using free function get<I>(T) now. | |||
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; | |||
(void)_; // blocks warnings | |||
} | |||
template <class T> | |||
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes( | |||
T const&) { | |||
return {}; | |||
} | |||
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) { | |||
const auto indexes = get_indexes(tup); | |||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); | |||
} | |||
template <typename Range> | |||
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>; | |||
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string< | |||
typename std::decay<Arg>::type>::value)> | |||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { | |||
return add_space ? " {}" : "{}"; | |||
} | |||
template <typename Arg, FMT_ENABLE_IF(is_like_std_string< | |||
typename std::decay<Arg>::type>::value)> | |||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { | |||
return add_space ? " \"{}\"" : "\"{}\""; | |||
} | |||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) { | |||
return add_space ? " \"{}\"" : "\"{}\""; | |||
} | |||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) { | |||
return add_space ? L" \"{}\"" : L"\"{}\""; | |||
} | |||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) { | |||
return add_space ? " '{}'" : "'{}'"; | |||
} | |||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) { | |||
return add_space ? L" '{}'" : L"'{}'"; | |||
} | |||
} // namespace detail | |||
template <typename T> struct is_tuple_like { | |||
static FMT_CONSTEXPR_DECL const bool value = | |||
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value; | |||
}; | |||
template <typename TupleT, typename Char> | |||
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> { | |||
private: | |||
// C++11 generic lambda for format() | |||
template <typename FormatContext> struct format_each { | |||
template <typename T> void operator()(const T& v) { | |||
if (i > 0) { | |||
if (formatting.add_prepostfix_space) { | |||
*out++ = ' '; | |||
} | |||
out = detail::copy(formatting.delimiter, out); | |||
} | |||
out = format_to(out, | |||
detail::format_str_quoted( | |||
(formatting.add_delimiter_spaces && i > 0), v), | |||
v); | |||
++i; | |||
} | |||
formatting_tuple<Char>& formatting; | |||
size_t& i; | |||
typename std::add_lvalue_reference<decltype( | |||
std::declval<FormatContext>().out())>::type out; | |||
}; | |||
public: | |||
formatting_tuple<Char> formatting; | |||
template <typename ParseContext> | |||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |||
return formatting.parse(ctx); | |||
} | |||
template <typename FormatContext = format_context> | |||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { | |||
auto out = ctx.out(); | |||
size_t i = 0; | |||
detail::copy(formatting.prefix, out); | |||
detail::for_each(values, format_each<FormatContext>{formatting, i, out}); | |||
if (formatting.add_prepostfix_space) { | |||
*out++ = ' '; | |||
} | |||
detail::copy(formatting.postfix, out); | |||
return ctx.out(); | |||
} | |||
}; | |||
template <typename T, typename Char> struct is_range { | |||
static FMT_CONSTEXPR_DECL const bool value = | |||
detail::is_range_<T>::value && !detail::is_like_std_string<T>::value && | |||
!std::is_convertible<T, std::basic_string<Char>>::value && | |||
!std::is_constructible<detail::std_string_view<Char>, T>::value; | |||
}; | |||
template <typename T, typename Char> | |||
struct formatter< | |||
T, Char, | |||
enable_if_t<fmt::is_range<T, Char>::value | |||
// Workaround a bug in MSVC 2017 and earlier. | |||
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927 | |||
&& | |||
(has_formatter<detail::value_type<T>, format_context>::value || | |||
detail::has_fallback_formatter<detail::value_type<T>, | |||
format_context>::value) | |||
#endif | |||
>> { | |||
formatting_range<Char> formatting; | |||
template <typename ParseContext> | |||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |||
return formatting.parse(ctx); | |||
} | |||
template <typename FormatContext> | |||
typename FormatContext::iterator format(const T& values, FormatContext& ctx) { | |||
auto out = detail::copy(formatting.prefix, ctx.out()); | |||
size_t i = 0; | |||
auto it = values.begin(); | |||
auto end = values.end(); | |||
for (; it != end; ++it) { | |||
if (i > 0) { | |||
if (formatting.add_prepostfix_space) *out++ = ' '; | |||
out = detail::copy(formatting.delimiter, out); | |||
} | |||
out = format_to(out, | |||
detail::format_str_quoted( | |||
(formatting.add_delimiter_spaces && i > 0), *it), | |||
*it); | |||
if (++i > formatting.range_length_limit) { | |||
out = format_to(out, " ... <other elements>"); | |||
break; | |||
} | |||
} | |||
if (formatting.add_prepostfix_space) *out++ = ' '; | |||
return detail::copy(formatting.postfix, out); | |||
} | |||
}; | |||
template <typename Char, typename... T> struct tuple_arg_join : detail::view { | |||
const std::tuple<T...>& tuple; | |||
basic_string_view<Char> sep; | |||
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s) | |||
: tuple{t}, sep{s} {} | |||
}; | |||
template <typename Char, typename... T> | |||
struct formatter<tuple_arg_join<Char, T...>, Char> { | |||
template <typename ParseContext> | |||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |||
return ctx.begin(); | |||
} | |||
template <typename FormatContext> | |||
typename FormatContext::iterator format( | |||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) { | |||
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{}); | |||
} | |||
private: | |||
template <typename FormatContext, size_t... N> | |||
typename FormatContext::iterator format( | |||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx, | |||
detail::index_sequence<N...>) { | |||
return format_args(value, ctx, std::get<N>(value.tuple)...); | |||
} | |||
template <typename FormatContext> | |||
typename FormatContext::iterator format_args( | |||
const tuple_arg_join<Char, T...>&, FormatContext& ctx) { | |||
// 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(); | |||
} | |||
template <typename FormatContext, typename Arg, typename... Args> | |||
typename FormatContext::iterator format_args( | |||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx, | |||
const Arg& arg, const Args&... args) { | |||
using base = formatter<typename std::decay<Arg>::type, Char>; | |||
auto out = ctx.out(); | |||
out = base{}.format(arg, ctx); | |||
if (sizeof...(Args) > 0) { | |||
out = std::copy(value.sep.begin(), value.sep.end(), out); | |||
ctx.advance_to(out); | |||
return format_args(value, ctx, args...); | |||
} | |||
return out; | |||
} | |||
}; | |||
/** | |||
\rst | |||
Returns an object that formats `tuple` with elements separated by `sep`. | |||
**Example**:: | |||
std::tuple<int, char> t = {1, 'a'}; | |||
fmt::print("{}", fmt::join(t, ", ")); | |||
// Output: "1, a" | |||
\endrst | |||
*/ | |||
template <typename... T> | |||
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple, | |||
string_view sep) { | |||
return {tuple, sep}; | |||
} | |||
template <typename... T> | |||
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple, | |||
wstring_view sep) { | |||
return {tuple, sep}; | |||
} | |||
/** | |||
\rst | |||
Returns an object that formats `initializer_list` with elements separated by | |||
`sep`. | |||
**Example**:: | |||
fmt::print("{}", fmt::join({1, 2, 3}, ", ")); | |||
// Output: "1, 2, 3" | |||
\endrst | |||
*/ | |||
template <typename T> | |||
arg_join<const T*, const T*, char> join(std::initializer_list<T> list, | |||
string_view sep) { | |||
return join(std::begin(list), std::end(list), sep); | |||
} | |||
template <typename T> | |||
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list, | |||
wstring_view sep) { | |||
return join(std::begin(list), std::end(list), sep); | |||
} | |||
FMT_END_NAMESPACE | |||
#endif // FMT_RANGES_H_ |
@@ -0,0 +1,99 @@ | |||
// Formatting library for C++ | |||
// | |||
// Copyright (c) 2012 - 2016, Victor Zverovich | |||
// All rights reserved. | |||
// | |||
// For the license information refer to format.h. | |||
#include "fmt/format-inl.h" | |||
FMT_BEGIN_NAMESPACE | |||
namespace detail { | |||
template <typename T> | |||
int format_float(char* buf, std::size_t size, const char* format, int precision, | |||
T value) { | |||
#ifdef FMT_FUZZ | |||
if (precision > 100000) | |||
throw std::runtime_error( | |||
"fuzz mode - avoid large allocation inside snprintf"); | |||
#endif | |||
// Suppress the warning about nonliteral format string. | |||
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; | |||
return precision < 0 ? snprintf_ptr(buf, size, format, value) | |||
: snprintf_ptr(buf, size, format, precision, value); | |||
} | |||
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x) | |||
FMT_NOEXCEPT; | |||
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x) | |||
FMT_NOEXCEPT; | |||
// DEPRECATED! This function exists for ABI compatibility. | |||
template <typename Char> | |||
typename basic_format_context<std::back_insert_iterator<buffer<Char>>, | |||
Char>::iterator | |||
vformat_to(buffer<Char>& buf, basic_string_view<Char> format_str, | |||
basic_format_args<basic_format_context< | |||
std::back_insert_iterator<buffer<type_identity_t<Char>>>, | |||
type_identity_t<Char>>> | |||
args) { | |||
using iterator = std::back_insert_iterator<buffer<char>>; | |||
using context = basic_format_context< | |||
std::back_insert_iterator<buffer<type_identity_t<Char>>>, | |||
type_identity_t<Char>>; | |||
auto out = iterator(buf); | |||
format_handler<iterator, Char, context> h(out, format_str, args, {}); | |||
parse_format_string<false>(format_str, h); | |||
return out; | |||
} | |||
template basic_format_context<std::back_insert_iterator<buffer<char>>, | |||
char>::iterator | |||
vformat_to(buffer<char>&, string_view, | |||
basic_format_args<basic_format_context< | |||
std::back_insert_iterator<buffer<type_identity_t<char>>>, | |||
type_identity_t<char>>>); | |||
} // namespace detail | |||
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>; | |||
// Workaround a bug in MSVC2013 that prevents instantiation of format_float. | |||
int (*instantiate_format_float)(double, int, detail::float_specs, | |||
detail::buffer<char>&) = detail::format_float; | |||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR | |||
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc); | |||
template FMT_API std::locale detail::locale_ref::get<std::locale>() const; | |||
#endif | |||
// Explicit instantiations for char. | |||
template FMT_API std::string detail::grouping_impl<char>(locale_ref); | |||
template FMT_API char detail::thousands_sep_impl(locale_ref); | |||
template FMT_API char detail::decimal_point_impl(locale_ref); | |||
template FMT_API void detail::buffer<char>::append(const char*, const char*); | |||
template FMT_API void detail::vformat_to( | |||
detail::buffer<char>&, string_view, | |||
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref); | |||
template FMT_API int detail::snprintf_float(double, int, detail::float_specs, | |||
detail::buffer<char>&); | |||
template FMT_API int detail::snprintf_float(long double, int, | |||
detail::float_specs, | |||
detail::buffer<char>&); | |||
template FMT_API int detail::format_float(double, int, detail::float_specs, | |||
detail::buffer<char>&); | |||
template FMT_API int detail::format_float(long double, int, detail::float_specs, | |||
detail::buffer<char>&); | |||
// Explicit instantiations for wchar_t. | |||
template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref); | |||
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref); | |||
template FMT_API wchar_t detail::decimal_point_impl(locale_ref); | |||
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*, | |||
const wchar_t*); | |||
FMT_END_NAMESPACE |
@@ -0,0 +1,322 @@ | |||
// Formatting library for C++ - optional OS-specific functionality | |||
// | |||
// Copyright (c) 2012 - 2016, Victor Zverovich | |||
// All rights reserved. | |||
// | |||
// For the license information refer to format.h. | |||
// Disable bogus MSVC warnings. | |||
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) | |||
# define _CRT_SECURE_NO_WARNINGS | |||
#endif | |||
#include "fmt/os.h" | |||
#include <climits> | |||
#if FMT_USE_FCNTL | |||
# include <sys/stat.h> | |||
# include <sys/types.h> | |||
# ifndef _WIN32 | |||
# include <unistd.h> | |||
# else | |||
# ifndef WIN32_LEAN_AND_MEAN | |||
# define WIN32_LEAN_AND_MEAN | |||
# endif | |||
# include <io.h> | |||
# include <windows.h> | |||
# define O_CREAT _O_CREAT | |||
# define O_TRUNC _O_TRUNC | |||
# ifndef S_IRUSR | |||
# define S_IRUSR _S_IREAD | |||
# endif | |||
# ifndef S_IWUSR | |||
# define S_IWUSR _S_IWRITE | |||
# endif | |||
# ifdef __MINGW32__ | |||
# define _SH_DENYNO 0x40 | |||
# endif | |||
# endif // _WIN32 | |||
#endif // FMT_USE_FCNTL | |||
#ifdef _WIN32 | |||
# include <windows.h> | |||
#endif | |||
#ifdef fileno | |||
# undef fileno | |||
#endif | |||
namespace { | |||
#ifdef _WIN32 | |||
// Return type of read and write functions. | |||
using RWResult = int; | |||
// On Windows the count argument to read and write is unsigned, so convert | |||
// it from size_t preventing integer overflow. | |||
inline unsigned convert_rwcount(std::size_t count) { | |||
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX; | |||
} | |||
#elif FMT_USE_FCNTL | |||
// Return type of read and write functions. | |||
using RWResult = ssize_t; | |||
inline std::size_t convert_rwcount(std::size_t count) { return count; } | |||
#endif | |||
} // namespace | |||
FMT_BEGIN_NAMESPACE | |||
#ifdef _WIN32 | |||
detail::utf16_to_utf8::utf16_to_utf8(wstring_view s) { | |||
if (int error_code = convert(s)) { | |||
FMT_THROW(windows_error(error_code, | |||
"cannot convert string from UTF-16 to UTF-8")); | |||
} | |||
} | |||
int detail::utf16_to_utf8::convert(wstring_view s) { | |||
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; | |||
int s_size = static_cast<int>(s.size()); | |||
if (s_size == 0) { | |||
// WideCharToMultiByte does not support zero length, handle separately. | |||
buffer_.resize(1); | |||
buffer_[0] = 0; | |||
return 0; | |||
} | |||
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0, | |||
nullptr, nullptr); | |||
if (length == 0) return GetLastError(); | |||
buffer_.resize(length + 1); | |||
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0], | |||
length, nullptr, nullptr); | |||
if (length == 0) return GetLastError(); | |||
buffer_[length] = 0; | |||
return 0; | |||
} | |||
void windows_error::init(int err_code, string_view format_str, | |||
format_args args) { | |||
error_code_ = err_code; | |||
memory_buffer buffer; | |||
detail::format_windows_error(buffer, err_code, vformat(format_str, args)); | |||
std::runtime_error& base = *this; | |||
base = std::runtime_error(to_string(buffer)); | |||
} | |||
void detail::format_windows_error(detail::buffer<char>& out, int error_code, | |||
string_view message) FMT_NOEXCEPT { | |||
FMT_TRY { | |||
wmemory_buffer buf; | |||
buf.resize(inline_buffer_size); | |||
for (;;) { | |||
wchar_t* system_message = &buf[0]; | |||
int result = FormatMessageW( | |||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, | |||
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message, | |||
static_cast<uint32_t>(buf.size()), nullptr); | |||
if (result != 0) { | |||
utf16_to_utf8 utf8_message; | |||
if (utf8_message.convert(system_message) == ERROR_SUCCESS) { | |||
format_to(buffer_appender<char>(out), "{}: {}", message, | |||
utf8_message); | |||
return; | |||
} | |||
break; | |||
} | |||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) | |||
break; // Can't get error message, report error code instead. | |||
buf.resize(buf.size() * 2); | |||
} | |||
} | |||
FMT_CATCH(...) {} | |||
format_error_code(out, error_code, message); | |||
} | |||
void report_windows_error(int error_code, | |||
fmt::string_view message) FMT_NOEXCEPT { | |||
report_error(detail::format_windows_error, error_code, message); | |||
} | |||
#endif // _WIN32 | |||
buffered_file::~buffered_file() FMT_NOEXCEPT { | |||
if (file_ && FMT_SYSTEM(fclose(file_)) != 0) | |||
report_system_error(errno, "cannot close file"); | |||
} | |||
buffered_file::buffered_file(cstring_view filename, cstring_view mode) { | |||
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), | |||
nullptr); | |||
if (!file_) | |||
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str())); | |||
} | |||
void buffered_file::close() { | |||
if (!file_) return; | |||
int result = FMT_SYSTEM(fclose(file_)); | |||
file_ = nullptr; | |||
if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); | |||
} | |||
// A macro used to prevent expansion of fileno on broken versions of MinGW. | |||
#define FMT_ARGS | |||
int buffered_file::fileno() const { | |||
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); | |||
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor")); | |||
return fd; | |||
} | |||
#if FMT_USE_FCNTL | |||
file::file(cstring_view path, int oflag) { | |||
int mode = S_IRUSR | S_IWUSR; | |||
# if defined(_WIN32) && !defined(__MINGW32__) | |||
fd_ = -1; | |||
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); | |||
# else | |||
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); | |||
# endif | |||
if (fd_ == -1) | |||
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); | |||
} | |||
file::~file() FMT_NOEXCEPT { | |||
// Don't retry close in case of EINTR! | |||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html | |||
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) | |||
report_system_error(errno, "cannot close file"); | |||
} | |||
void file::close() { | |||
if (fd_ == -1) return; | |||
// Don't retry close in case of EINTR! | |||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html | |||
int result = FMT_POSIX_CALL(close(fd_)); | |||
fd_ = -1; | |||
if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); | |||
} | |||
long long file::size() const { | |||
# ifdef _WIN32 | |||
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT | |||
// is less than 0x0500 as is the case with some default MinGW builds. | |||
// Both functions support large file sizes. | |||
DWORD size_upper = 0; | |||
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_)); | |||
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); | |||
if (size_lower == INVALID_FILE_SIZE) { | |||
DWORD error = GetLastError(); | |||
if (error != NO_ERROR) | |||
FMT_THROW(windows_error(GetLastError(), "cannot get file size")); | |||
} | |||
unsigned long long long_size = size_upper; | |||
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; | |||
# else | |||
using Stat = struct stat; | |||
Stat file_stat = Stat(); | |||
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) | |||
FMT_THROW(system_error(errno, "cannot get file attributes")); | |||
static_assert(sizeof(long long) >= sizeof(file_stat.st_size), | |||
"return type of file::size is not large enough"); | |||
return file_stat.st_size; | |||
# endif | |||
} | |||
std::size_t file::read(void* buffer, std::size_t count) { | |||
RWResult result = 0; | |||
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); | |||
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file")); | |||
return detail::to_unsigned(result); | |||
} | |||
std::size_t file::write(const void* buffer, std::size_t count) { | |||
RWResult result = 0; | |||
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); | |||
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file")); | |||
return detail::to_unsigned(result); | |||
} | |||
file file::dup(int fd) { | |||
// Don't retry as dup doesn't return EINTR. | |||
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html | |||
int new_fd = FMT_POSIX_CALL(dup(fd)); | |||
if (new_fd == -1) | |||
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd)); | |||
return file(new_fd); | |||
} | |||
void file::dup2(int fd) { | |||
int result = 0; | |||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); | |||
if (result == -1) { | |||
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}", | |||
fd_, fd)); | |||
} | |||
} | |||
void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT { | |||
int result = 0; | |||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); | |||
if (result == -1) ec = error_code(errno); | |||
} | |||
void file::pipe(file& read_end, file& write_end) { | |||
// Close the descriptors first to make sure that assignments don't throw | |||
// and there are no leaks. | |||
read_end.close(); | |||
write_end.close(); | |||
int fds[2] = {}; | |||
# ifdef _WIN32 | |||
// Make the default pipe capacity same as on Linux 2.6.11+. | |||
enum { DEFAULT_CAPACITY = 65536 }; | |||
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); | |||
# else | |||
// Don't retry as the pipe function doesn't return EINTR. | |||
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html | |||
int result = FMT_POSIX_CALL(pipe(fds)); | |||
# endif | |||
if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe")); | |||
// The following assignments don't throw because read_fd and write_fd | |||
// are closed. | |||
read_end = file(fds[0]); | |||
write_end = file(fds[1]); | |||
} | |||
buffered_file file::fdopen(const char* mode) { | |||
// Don't retry as fdopen doesn't return EINTR. | |||
# if defined(__MINGW32__) && defined(_POSIX_) | |||
FILE* f = ::fdopen(fd_, mode); | |||
# else | |||
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode)); | |||
# endif | |||
if (!f) | |||
FMT_THROW( | |||
system_error(errno, "cannot associate stream with file descriptor")); | |||
buffered_file bf(f); | |||
fd_ = -1; | |||
return bf; | |||
} | |||
long getpagesize() { | |||
# ifdef _WIN32 | |||
SYSTEM_INFO si; | |||
GetSystemInfo(&si); | |||
return si.dwPageSize; | |||
# else | |||
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); | |||
if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size")); | |||
return size; | |||
# endif | |||
} | |||
FMT_API void ostream::grow(size_t) { | |||
if (this->size() == this->capacity()) flush(); | |||
} | |||
#endif // FMT_USE_FCNTL | |||
FMT_END_NAMESPACE |
@@ -196,6 +196,7 @@ TARGET_LINK_LIBRARIES(rspamd-server rspamd-lpeg) | |||
TARGET_LINK_LIBRARIES(rspamd-server lcbtrie) | |||
TARGET_LINK_LIBRARIES(rspamd-server rspamd-zstd) | |||
TARGET_LINK_LIBRARIES(rspamd-server rspamd-fastutf8) | |||
TARGET_LINK_LIBRARIES(rspamd-server fmt) | |||
IF (ENABLE_CLANG_PLUGIN MATCHES "ON") | |||
ADD_DEPENDENCIES(rspamd-server rspamd-clang) |