mirror of https://github.com/trapexit/mergerfs.git
Browse Source
Merge pull request #1140 from trapexit/pinthreads
Merge pull request #1140 from trapexit/pinthreads
Add ability to pin read and processing threadspull/1142/head
trapexit
2 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 22298 additions and 21 deletions
-
27README.md
-
3libfuse/Makefile
-
1libfuse/include/fuse.h
-
3libfuse/include/fuse_lowlevel.h
-
163libfuse/lib/cpu.cpp
-
33libfuse/lib/cpu.hpp
-
234libfuse/lib/fmt/args.h
-
2069libfuse/lib/fmt/chrono.h
-
651libfuse/lib/fmt/color.h
-
611libfuse/lib/fmt/compile.h
-
3323libfuse/lib/fmt/core.h
-
1723libfuse/lib/fmt/format-inl.h
-
4217libfuse/lib/fmt/format.h
-
478libfuse/lib/fmt/os.h
-
237libfuse/lib/fmt/ostream.h
-
640libfuse/lib/fmt/printf.h
-
722libfuse/lib/fmt/ranges.h
-
171libfuse/lib/fmt/std.h
-
229libfuse/lib/fmt/xchar.h
-
47libfuse/lib/format.cpp
-
9libfuse/lib/fuse.c
-
235libfuse/lib/fuse_loop_mt.cpp
-
3libfuse/lib/fuse_mt.c
-
6049libfuse/lib/ghc/filesystem.hpp
-
361libfuse/lib/os.cpp
-
12libfuse/lib/thread_pool.hpp
-
68man/mergerfs.1
@ -0,0 +1,163 @@ |
|||
#ifndef _GNU_SOURCE
|
|||
#define _GNU_SOURCE
|
|||
#endif
|
|||
|
|||
#include "cpu.hpp"
|
|||
#include "ghc/filesystem.hpp"
|
|||
#include "fmt/core.h"
|
|||
|
|||
#include <sched.h>
|
|||
|
|||
#include <fstream>
|
|||
|
|||
|
|||
int |
|||
CPU::getaffinity(const pthread_t thread_id_, |
|||
cpu_set_t *cpuset_) |
|||
{ |
|||
CPU_ZERO(cpuset_); |
|||
return sched_getaffinity(thread_id_, |
|||
sizeof(cpu_set_t), |
|||
cpuset_); |
|||
} |
|||
|
|||
int |
|||
CPU::setaffinity(const pthread_t thread_id_, |
|||
cpu_set_t *cpuset_) |
|||
{ |
|||
return pthread_setaffinity_np(thread_id_, |
|||
sizeof(cpu_set_t), |
|||
cpuset_); |
|||
} |
|||
|
|||
int |
|||
CPU::setaffinity(const pthread_t thread_id_, |
|||
const int cpu_) |
|||
{ |
|||
cpu_set_t cpuset; |
|||
|
|||
CPU_ZERO(&cpuset); |
|||
CPU_SET(cpu_,&cpuset); |
|||
|
|||
return CPU::setaffinity(thread_id_,&cpuset); |
|||
} |
|||
|
|||
int |
|||
CPU::setaffinity(const pthread_t thread_id_, |
|||
const std::set<int> cpus_) |
|||
{ |
|||
cpu_set_t cpuset; |
|||
|
|||
CPU_ZERO(&cpuset); |
|||
for(auto const cpu : cpus_) |
|||
CPU_SET(cpu,&cpuset); |
|||
|
|||
return CPU::setaffinity(thread_id_,&cpuset); |
|||
} |
|||
|
|||
static |
|||
ghc::filesystem::path |
|||
generate_cpu_core_id_path(const int cpu_id_) |
|||
{ |
|||
const ghc::filesystem::path basepath{"/sys/devices/system/cpu"}; |
|||
|
|||
return basepath / fmt::format("cpu{}",cpu_id_) / "topology" / "core_id"; |
|||
} |
|||
|
|||
int |
|||
CPU::count() |
|||
{ |
|||
int rv; |
|||
cpu_set_t cpuset; |
|||
|
|||
rv = CPU::getaffinity(0,&cpuset); |
|||
if(rv < 0) |
|||
return rv; |
|||
|
|||
return CPU_COUNT(&cpuset); |
|||
} |
|||
|
|||
CPU::CPUVec |
|||
CPU::cpus() |
|||
{ |
|||
cpu_set_t cpuset; |
|||
CPU::CPUVec cpuvec; |
|||
|
|||
CPU::getaffinity(0,&cpuset); |
|||
|
|||
for(int i = 0; i < CPU_SETSIZE; i++) |
|||
{ |
|||
if(!CPU_ISSET(i,&cpuset)) |
|||
continue; |
|||
|
|||
cpuvec.push_back(i); |
|||
} |
|||
|
|||
return cpuvec; |
|||
} |
|||
|
|||
CPU::CPU2CoreMap |
|||
CPU::cpu2core() |
|||
{ |
|||
cpu_set_t cpuset; |
|||
CPU::CPU2CoreMap c2c; |
|||
|
|||
CPU::getaffinity(0,&cpuset); |
|||
|
|||
for(int i = 0; i < CPU_SETSIZE; i++) |
|||
{ |
|||
int core_id; |
|||
std::ifstream ifs; |
|||
ghc::filesystem::path path; |
|||
|
|||
if(!CPU_ISSET(i,&cpuset)) |
|||
continue; |
|||
|
|||
path = ::generate_cpu_core_id_path(i); |
|||
|
|||
ifs.open(path); |
|||
if(!ifs) |
|||
break; |
|||
|
|||
ifs >> core_id; |
|||
|
|||
c2c[i] = core_id; |
|||
|
|||
ifs.close(); |
|||
} |
|||
|
|||
return c2c; |
|||
} |
|||
|
|||
CPU::Core2CPUsMap |
|||
CPU::core2cpus() |
|||
{ |
|||
cpu_set_t cpuset; |
|||
CPU::Core2CPUsMap c2c; |
|||
|
|||
CPU::getaffinity(0,&cpuset); |
|||
|
|||
for(int i = 0; i < CPU_SETSIZE; i++) |
|||
{ |
|||
int core_id; |
|||
std::ifstream ifs; |
|||
ghc::filesystem::path path; |
|||
|
|||
if(!CPU_ISSET(i,&cpuset)) |
|||
continue; |
|||
|
|||
path = ::generate_cpu_core_id_path(i); |
|||
|
|||
ifs.open(path); |
|||
if(!ifs) |
|||
break; |
|||
|
|||
ifs >> core_id; |
|||
|
|||
c2c[core_id].insert(i); |
|||
|
|||
ifs.close(); |
|||
} |
|||
|
|||
return c2c; |
|||
} |
@ -0,0 +1,33 @@ |
|||
#pragma once
|
|||
|
|||
#ifndef _GNU_SOURCE
|
|||
#define _GNU_SOURCE
|
|||
#endif
|
|||
|
|||
#include <pthread.h>
|
|||
#include <sched.h>
|
|||
|
|||
#include <set>
|
|||
#include <unordered_map>
|
|||
#include <vector>
|
|||
|
|||
|
|||
class CPU |
|||
{ |
|||
public: |
|||
typedef std::vector<pthread_t> ThreadIdVec; |
|||
typedef std::vector<int> CPUVec; |
|||
typedef std::unordered_map<int,int> CPU2CoreMap; |
|||
typedef std::unordered_map<int,std::set<int>> Core2CPUsMap; |
|||
|
|||
public: |
|||
static int count(); |
|||
static int getaffinity(const pthread_t thread_id, cpu_set_t *cpuset); |
|||
static int setaffinity(const pthread_t thread_id, cpu_set_t *cpuset); |
|||
static int setaffinity(const pthread_t thread_id, const int cpu); |
|||
static int setaffinity(const pthread_t thread_id, const std::set<int> cpus); |
|||
|
|||
static CPU::CPUVec cpus(); |
|||
static CPU::CPU2CoreMap cpu2core(); |
|||
static CPU::Core2CPUsMap core2cpus(); |
|||
}; |
@ -0,0 +1,234 @@ |
|||
// Formatting library for C++ - dynamic format arguments |
|||
// |
|||
// Copyright (c) 2012 - present, Victor Zverovich |
|||
// All rights reserved. |
|||
// |
|||
// For the license information refer to format.h. |
|||
|
|||
#ifndef FMT_ARGS_H_ |
|||
#define FMT_ARGS_H_ |
|||
|
|||
#include <functional> // std::reference_wrapper |
|||
#include <memory> // std::unique_ptr |
|||
#include <vector> |
|||
|
|||
#include "core.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
|
|||
namespace detail { |
|||
|
|||
template <typename T> struct is_reference_wrapper : std::false_type {}; |
|||
template <typename T> |
|||
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {}; |
|||
|
|||
template <typename T> const T& unwrap(const T& v) { return v; } |
|||
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) { |
|||
return static_cast<const T&>(v); |
|||
} |
|||
|
|||
class dynamic_arg_list { |
|||
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for |
|||
// templates it doesn't complain about inability to deduce single translation |
|||
// unit for placing vtable. So storage_node_base is made a fake template. |
|||
template <typename = void> struct node { |
|||
virtual ~node() = default; |
|||
std::unique_ptr<node<>> next; |
|||
}; |
|||
|
|||
template <typename T> struct typed_node : node<> { |
|||
T value; |
|||
|
|||
template <typename Arg> |
|||
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} |
|||
|
|||
template <typename Char> |
|||
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg) |
|||
: value(arg.data(), arg.size()) {} |
|||
}; |
|||
|
|||
std::unique_ptr<node<>> head_; |
|||
|
|||
public: |
|||
template <typename T, typename Arg> const T& push(const Arg& arg) { |
|||
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg)); |
|||
auto& value = new_node->value; |
|||
new_node->next = std::move(head_); |
|||
head_ = std::move(new_node); |
|||
return value; |
|||
} |
|||
}; |
|||
} // namespace detail |
|||
|
|||
/** |
|||
\rst |
|||
A dynamic version of `fmt::format_arg_store`. |
|||
It's equipped with a storage to potentially temporary objects which lifetimes |
|||
could be shorter than the format arguments object. |
|||
|
|||
It can be implicitly converted into `~fmt::basic_format_args` for passing |
|||
into type-erased formatting functions such as `~fmt::vformat`. |
|||
\endrst |
|||
*/ |
|||
template <typename Context> |
|||
class dynamic_format_arg_store |
|||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 |
|||
// Workaround a GCC template argument substitution bug. |
|||
: public basic_format_args<Context> |
|||
#endif |
|||
{ |
|||
private: |
|||
using char_type = typename Context::char_type; |
|||
|
|||
template <typename T> struct need_copy { |
|||
static constexpr detail::type mapped_type = |
|||
detail::mapped_type_constant<T, Context>::value; |
|||
|
|||
enum { |
|||
value = !(detail::is_reference_wrapper<T>::value || |
|||
std::is_same<T, basic_string_view<char_type>>::value || |
|||
std::is_same<T, detail::std_string_view<char_type>>::value || |
|||
(mapped_type != detail::type::cstring_type && |
|||
mapped_type != detail::type::string_type && |
|||
mapped_type != detail::type::custom_type)) |
|||
}; |
|||
}; |
|||
|
|||
template <typename T> |
|||
using stored_type = conditional_t< |
|||
std::is_convertible<T, std::basic_string<char_type>>::value && |
|||
!detail::is_reference_wrapper<T>::value, |
|||
std::basic_string<char_type>, T>; |
|||
|
|||
// Storage of basic_format_arg must be contiguous. |
|||
std::vector<basic_format_arg<Context>> data_; |
|||
std::vector<detail::named_arg_info<char_type>> named_info_; |
|||
|
|||
// Storage of arguments not fitting into basic_format_arg must grow |
|||
// without relocation because items in data_ refer to it. |
|||
detail::dynamic_arg_list dynamic_args_; |
|||
|
|||
friend class basic_format_args<Context>; |
|||
|
|||
unsigned long long get_types() const { |
|||
return detail::is_unpacked_bit | data_.size() | |
|||
(named_info_.empty() |
|||
? 0ULL |
|||
: static_cast<unsigned long long>(detail::has_named_args_bit)); |
|||
} |
|||
|
|||
const basic_format_arg<Context>* data() const { |
|||
return named_info_.empty() ? data_.data() : data_.data() + 1; |
|||
} |
|||
|
|||
template <typename T> void emplace_arg(const T& arg) { |
|||
data_.emplace_back(detail::make_arg<Context>(arg)); |
|||
} |
|||
|
|||
template <typename T> |
|||
void emplace_arg(const detail::named_arg<char_type, T>& arg) { |
|||
if (named_info_.empty()) { |
|||
constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr}; |
|||
data_.insert(data_.begin(), {zero_ptr, 0}); |
|||
} |
|||
data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value))); |
|||
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) { |
|||
data->pop_back(); |
|||
}; |
|||
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)> |
|||
guard{&data_, pop_one}; |
|||
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)}); |
|||
data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; |
|||
guard.release(); |
|||
} |
|||
|
|||
public: |
|||
constexpr dynamic_format_arg_store() = default; |
|||
|
|||
/** |
|||
\rst |
|||
Adds an argument into the dynamic store for later passing to a formatting |
|||
function. |
|||
|
|||
Note that custom types and string types (but not string views) are copied |
|||
into the store dynamically allocating memory if necessary. |
|||
|
|||
**Example**:: |
|||
|
|||
fmt::dynamic_format_arg_store<fmt::format_context> store; |
|||
store.push_back(42); |
|||
store.push_back("abc"); |
|||
store.push_back(1.5f); |
|||
std::string result = fmt::vformat("{} and {} and {}", store); |
|||
\endrst |
|||
*/ |
|||
template <typename T> void push_back(const T& arg) { |
|||
if (detail::const_check(need_copy<T>::value)) |
|||
emplace_arg(dynamic_args_.push<stored_type<T>>(arg)); |
|||
else |
|||
emplace_arg(detail::unwrap(arg)); |
|||
} |
|||
|
|||
/** |
|||
\rst |
|||
Adds a reference to the argument into the dynamic store for later passing to |
|||
a formatting function. |
|||
|
|||
**Example**:: |
|||
|
|||
fmt::dynamic_format_arg_store<fmt::format_context> store; |
|||
char band[] = "Rolling Stones"; |
|||
store.push_back(std::cref(band)); |
|||
band[9] = 'c'; // Changing str affects the output. |
|||
std::string result = fmt::vformat("{}", store); |
|||
// result == "Rolling Scones" |
|||
\endrst |
|||
*/ |
|||
template <typename T> void push_back(std::reference_wrapper<T> arg) { |
|||
static_assert( |
|||
need_copy<T>::value, |
|||
"objects of built-in types and string views are always copied"); |
|||
emplace_arg(arg.get()); |
|||
} |
|||
|
|||
/** |
|||
Adds named argument into the dynamic store for later passing to a formatting |
|||
function. ``std::reference_wrapper`` is supported to avoid copying of the |
|||
argument. The name is always copied into the store. |
|||
*/ |
|||
template <typename T> |
|||
void push_back(const detail::named_arg<char_type, T>& arg) { |
|||
const char_type* arg_name = |
|||
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str(); |
|||
if (detail::const_check(need_copy<T>::value)) { |
|||
emplace_arg( |
|||
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value))); |
|||
} else { |
|||
emplace_arg(fmt::arg(arg_name, arg.value)); |
|||
} |
|||
} |
|||
|
|||
/** Erase all elements from the store */ |
|||
void clear() { |
|||
data_.clear(); |
|||
named_info_.clear(); |
|||
dynamic_args_ = detail::dynamic_arg_list(); |
|||
} |
|||
|
|||
/** |
|||
\rst |
|||
Reserves space to store at least *new_cap* arguments including |
|||
*new_cap_named* named arguments. |
|||
\endrst |
|||
*/ |
|||
void reserve(size_t new_cap, size_t new_cap_named) { |
|||
FMT_ASSERT(new_cap >= new_cap_named, |
|||
"Set of arguments includes set of named arguments"); |
|||
data_.reserve(new_cap); |
|||
named_info_.reserve(new_cap_named); |
|||
} |
|||
}; |
|||
|
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_ARGS_H_ |
2069
libfuse/lib/fmt/chrono.h
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,651 @@ |
|||
// 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 |
|||
FMT_MODULE_EXPORT_BEGIN |
|||
|
|||
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, |
|||
faint = 1 << 1, |
|||
italic = 1 << 2, |
|||
underline = 1 << 3, |
|||
blink = 1 << 4, |
|||
reverse = 1 << 5, |
|||
conceal = 1 << 6, |
|||
strikethrough = 1 << 7, |
|||
}; |
|||
|
|||
// rgb is a struct for red, green and blue colors. |
|||
// 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; |
|||
}; |
|||
|
|||
FMT_BEGIN_DETAIL_NAMESPACE |
|||
|
|||
// color is a struct of either a rgb color or a terminal color. |
|||
struct color_type { |
|||
FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {} |
|||
FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} { |
|||
value.rgb_color = static_cast<uint32_t>(rgb_color); |
|||
} |
|||
FMT_CONSTEXPR color_type(rgb rgb_color) 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) 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; |
|||
}; |
|||
|
|||
FMT_END_DETAIL_NAMESPACE |
|||
|
|||
/** A text style consisting of foreground and background colors and emphasis. */ |
|||
class text_style { |
|||
public: |
|||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept |
|||
: set_foreground_color(), set_background_color(), ems(em) {} |
|||
|
|||
FMT_CONSTEXPR text_style& 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 bool has_foreground() const noexcept { |
|||
return set_foreground_color; |
|||
} |
|||
FMT_CONSTEXPR bool has_background() const noexcept { |
|||
return set_background_color; |
|||
} |
|||
FMT_CONSTEXPR bool has_emphasis() const noexcept { |
|||
return static_cast<uint8_t>(ems) != 0; |
|||
} |
|||
FMT_CONSTEXPR detail::color_type get_foreground() const noexcept { |
|||
FMT_ASSERT(has_foreground(), "no foreground specified for this style"); |
|||
return foreground_color; |
|||
} |
|||
FMT_CONSTEXPR detail::color_type get_background() const noexcept { |
|||
FMT_ASSERT(has_background(), "no background specified for this style"); |
|||
return background_color; |
|||
} |
|||
FMT_CONSTEXPR emphasis get_emphasis() const noexcept { |
|||
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); |
|||
return ems; |
|||
} |
|||
|
|||
private: |
|||
FMT_CONSTEXPR text_style(bool is_foreground, |
|||
detail::color_type text_color) noexcept |
|||
: set_foreground_color(), set_background_color(), ems() { |
|||
if (is_foreground) { |
|||
foreground_color = text_color; |
|||
set_foreground_color = true; |
|||
} else { |
|||
background_color = text_color; |
|||
set_background_color = true; |
|||
} |
|||
} |
|||
|
|||
friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept; |
|||
|
|||
friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept; |
|||
|
|||
detail::color_type foreground_color; |
|||
detail::color_type background_color; |
|||
bool set_foreground_color; |
|||
bool set_background_color; |
|||
emphasis ems; |
|||
}; |
|||
|
|||
/** Creates a text style from the foreground (text) color. */ |
|||
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept { |
|||
return text_style(true, foreground); |
|||
} |
|||
|
|||
/** Creates a text style from the background color. */ |
|||
FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept { |
|||
return text_style(false, background); |
|||
} |
|||
|
|||
FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept { |
|||
return text_style(lhs) | rhs; |
|||
} |
|||
|
|||
FMT_BEGIN_DETAIL_NAMESPACE |
|||
|
|||
template <typename Char> struct ansi_color_escape { |
|||
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, |
|||
const char* esc) noexcept { |
|||
// If we have a terminal color, we need to output another escape code |
|||
// sequence. |
|||
if (!text_color.is_rgb) { |
|||
bool is_background = esc == string_view("\x1b[48;2;"); |
|||
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) noexcept { |
|||
uint8_t em_codes[num_emphases] = {}; |
|||
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; |
|||
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2; |
|||
if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3; |
|||
if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4; |
|||
if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5; |
|||
if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7; |
|||
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; |
|||
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; |
|||
|
|||
size_t index = 0; |
|||
for (size_t i = 0; i < num_emphases; ++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 noexcept { return buffer; } |
|||
|
|||
FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; } |
|||
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept { |
|||
return buffer + std::char_traits<Char>::length(buffer); |
|||
} |
|||
|
|||
private: |
|||
static constexpr size_t num_emphases = 8; |
|||
Char buffer[7u + 3u * num_emphases + 1u]; |
|||
|
|||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, |
|||
char delimiter) 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); |
|||
} |
|||
static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept { |
|||
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask); |
|||
} |
|||
}; |
|||
|
|||
template <typename Char> |
|||
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color( |
|||
detail::color_type foreground) noexcept { |
|||
return ansi_color_escape<Char>(foreground, "\x1b[38;2;"); |
|||
} |
|||
|
|||
template <typename Char> |
|||
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color( |
|||
detail::color_type background) noexcept { |
|||
return ansi_color_escape<Char>(background, "\x1b[48;2;"); |
|||
} |
|||
|
|||
template <typename Char> |
|||
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept { |
|||
return ansi_color_escape<Char>(em); |
|||
} |
|||
|
|||
template <typename Char> inline void fputs(const Char* chars, FILE* stream) { |
|||
int result = std::fputs(chars, stream); |
|||
if (result < 0) |
|||
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); |
|||
} |
|||
|
|||
template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) { |
|||
int result = std::fputws(chars, stream); |
|||
if (result < 0) |
|||
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); |
|||
} |
|||
|
|||
template <typename Char> inline void reset_color(FILE* stream) { |
|||
fputs("\x1b[0m", stream); |
|||
} |
|||
|
|||
template <> inline void reset_color<wchar_t>(FILE* stream) { |
|||
fputs(L"\x1b[0m", stream); |
|||
} |
|||
|
|||
template <typename Char> inline void reset_color(buffer<Char>& buffer) { |
|||
auto reset_color = string_view("\x1b[0m"); |
|||
buffer.append(reset_color.begin(), reset_color.end()); |
|||
} |
|||
|
|||
template <typename T> struct styled_arg { |
|||
const T& value; |
|||
text_style style; |
|||
}; |
|||
|
|||
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); |
|||
} |
|||
|
|||
FMT_END_DETAIL_NAMESPACE |
|||
|
|||
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, detail::to_string_view(format), args); |
|||
if (detail::is_utf8()) { |
|||
detail::print(f, basic_string_view<Char>(buf.begin(), buf.size())); |
|||
} else { |
|||
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_format_args<buffer_context<char_t<S>>>(args...)); |
|||
} |
|||
|
|||
/** |
|||
\rst |
|||
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); |
|||
\endrst |
|||
*/ |
|||
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, detail::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 fmt::vformat(ts, detail::to_string_view(format_str), |
|||
fmt::make_format_args<buffer_context<Char>>(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) { |
|||
auto&& buf = detail::get_buffer<Char>(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, detail::to_string_view(format_str), |
|||
fmt::make_format_args<buffer_context<char_t<S>>>(args...)); |
|||
} |
|||
|
|||
template <typename T, typename Char> |
|||
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> { |
|||
template <typename FormatContext> |
|||
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const |
|||
-> decltype(ctx.out()) { |
|||
const auto& ts = arg.style; |
|||
const auto& value = arg.value; |
|||
auto out = ctx.out(); |
|||
|
|||
bool has_style = false; |
|||
if (ts.has_emphasis()) { |
|||
has_style = true; |
|||
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis()); |
|||
out = std::copy(emphasis.begin(), emphasis.end(), out); |
|||
} |
|||
if (ts.has_foreground()) { |
|||
has_style = true; |
|||
auto foreground = |
|||
detail::make_foreground_color<Char>(ts.get_foreground()); |
|||
out = std::copy(foreground.begin(), foreground.end(), out); |
|||
} |
|||
if (ts.has_background()) { |
|||
has_style = true; |
|||
auto background = |
|||
detail::make_background_color<Char>(ts.get_background()); |
|||
out = std::copy(background.begin(), background.end(), out); |
|||
} |
|||
out = formatter<T, Char>::format(value, ctx); |
|||
if (has_style) { |
|||
auto reset_color = string_view("\x1b[0m"); |
|||
out = std::copy(reset_color.begin(), reset_color.end(), out); |
|||
} |
|||
return out; |
|||
} |
|||
}; |
|||
|
|||
/** |
|||
\rst |
|||
Returns an argument that will be formatted using ANSI escape sequences, |
|||
to be used in a formatting function. |
|||
|
|||
**Example**:: |
|||
|
|||
fmt::print("Elapsed time: {0:.2f} seconds", |
|||
fmt::styled(1.23, fmt::fg(fmt::color::green) | |
|||
fmt::bg(fmt::color::blue))); |
|||
\endrst |
|||
*/ |
|||
template <typename T> |
|||
FMT_CONSTEXPR auto styled(const T& value, text_style ts) |
|||
-> detail::styled_arg<remove_cvref_t<T>> { |
|||
return detail::styled_arg<remove_cvref_t<T>>{value, ts}; |
|||
} |
|||
|
|||
FMT_MODULE_EXPORT_END |
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_COLOR_H_ |
@ -0,0 +1,611 @@ |
|||
// 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 "format.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
namespace detail { |
|||
|
|||
template <typename Char, typename InputIt> |
|||
FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end, |
|||
counting_iterator it) { |
|||
return it + (end - begin); |
|||
} |
|||
|
|||
template <typename OutputIt> class truncating_iterator_base { |
|||
protected: |
|||
OutputIt out_; |
|||
size_t limit_; |
|||
size_t count_ = 0; |
|||
|
|||
truncating_iterator_base() : out_(), limit_(0) {} |
|||
|
|||
truncating_iterator_base(OutputIt out, size_t limit) |
|||
: out_(out), limit_(limit) {} |
|||
|
|||
public: |
|||
using iterator_category = std::output_iterator_tag; |
|||
using value_type = typename std::iterator_traits<OutputIt>::value_type; |
|||
using difference_type = std::ptrdiff_t; |
|||
using pointer = void; |
|||
using reference = void; |
|||
FMT_UNCHECKED_ITERATOR(truncating_iterator_base); |
|||
|
|||
OutputIt base() const { return out_; } |
|||
size_t count() const { return count_; } |
|||
}; |
|||
|
|||
// An output iterator that truncates the output and counts the number of objects |
|||
// written to it. |
|||
template <typename OutputIt, |
|||
typename Enable = typename std::is_void< |
|||
typename std::iterator_traits<OutputIt>::value_type>::type> |
|||
class truncating_iterator; |
|||
|
|||
template <typename OutputIt> |
|||
class truncating_iterator<OutputIt, std::false_type> |
|||
: public truncating_iterator_base<OutputIt> { |
|||
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_; |
|||
|
|||
public: |
|||
using value_type = typename truncating_iterator_base<OutputIt>::value_type; |
|||
|
|||
truncating_iterator() = default; |
|||
|
|||
truncating_iterator(OutputIt out, size_t limit) |
|||
: truncating_iterator_base<OutputIt>(out, limit) {} |
|||
|
|||
truncating_iterator& operator++() { |
|||
if (this->count_++ < this->limit_) ++this->out_; |
|||
return *this; |
|||
} |
|||
|
|||
truncating_iterator operator++(int) { |
|||
auto it = *this; |
|||
++*this; |
|||
return it; |
|||
} |
|||
|
|||
value_type& operator*() const { |
|||
return this->count_ < this->limit_ ? *this->out_ : blackhole_; |
|||
} |
|||
}; |
|||
|
|||
template <typename OutputIt> |
|||
class truncating_iterator<OutputIt, std::true_type> |
|||
: public truncating_iterator_base<OutputIt> { |
|||
public: |
|||
truncating_iterator() = default; |
|||
|
|||
truncating_iterator(OutputIt out, size_t limit) |
|||
: truncating_iterator_base<OutputIt>(out, limit) {} |
|||
|
|||
template <typename T> truncating_iterator& operator=(T val) { |
|||
if (this->count_++ < this->limit_) *this->out_++ = val; |
|||
return *this; |
|||
} |
|||
|
|||
truncating_iterator& operator++() { return *this; } |
|||
truncating_iterator& operator++(int) { return *this; } |
|||
truncating_iterator& operator*() { return *this; } |
|||
}; |
|||
|
|||
// 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 |
|||
*/ |
|||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) |
|||
# define FMT_COMPILE(s) \ |
|||
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) |
|||
#else |
|||
# define FMT_COMPILE(s) FMT_STRING(s) |
|||
#endif |
|||
|
|||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS |
|||
template <typename Char, size_t N, |
|||
fmt::detail_exported::fixed_string<Char, N> Str> |
|||
struct udl_compiled_string : compiled_string { |
|||
using char_type = Char; |
|||
explicit constexpr operator basic_string_view<char_type>() const { |
|||
return {Str.data, N - 1}; |
|||
} |
|||
}; |
|||
#endif |
|||
|
|||
template <typename T, typename... Tail> |
|||
const T& first(const T& value, const Tail&...) { |
|||
return value; |
|||
} |
|||
|
|||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) |
|||
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 detail::get<N - 1>(rest...); |
|||
} |
|||
|
|||
template <typename Char, typename... Args> |
|||
constexpr int get_arg_index_by_name(basic_string_view<Char> name, |
|||
type_list<Args...>) { |
|||
return get_arg_index_by_name<Args...>(name); |
|||
} |
|||
|
|||
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(detail::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> |
|||
constexpr 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> |
|||
constexpr OutputIt format(OutputIt out, const Args&...) const { |
|||
return write<Char>(out, value); |
|||
} |
|||
}; |
|||
|
|||
// This ensures that the argument type is convertible to `const T&`. |
|||
template <typename T, int N, typename... Args> |
|||
constexpr const T& get_arg_checked(const Args&... args) { |
|||
const auto& arg = detail::get<N>(args...); |
|||
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) { |
|||
return arg.value; |
|||
} else { |
|||
return arg; |
|||
} |
|||
} |
|||
|
|||
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> |
|||
constexpr OutputIt format(OutputIt out, const Args&... args) const { |
|||
return write<Char>(out, get_arg_checked<T, N>(args...)); |
|||
} |
|||
}; |
|||
|
|||
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 with name. |
|||
template <typename Char> struct runtime_named_field { |
|||
using char_type = Char; |
|||
basic_string_view<Char> name; |
|||
|
|||
template <typename OutputIt, typename T> |
|||
constexpr static bool try_format_argument( |
|||
OutputIt& out, |
|||
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9 |
|||
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) { |
|||
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) { |
|||
if (arg_name == arg.name) { |
|||
out = write<Char>(out, arg.value); |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
template <typename OutputIt, typename... Args> |
|||
constexpr OutputIt format(OutputIt out, const Args&... args) const { |
|||
bool found = (try_format_argument(out, name, args) || ...); |
|||
if (!found) { |
|||
FMT_THROW(format_error("argument with specified name is not found")); |
|||
} |
|||
return out; |
|||
} |
|||
}; |
|||
|
|||
template <typename Char> |
|||
struct is_compiled_format<runtime_named_field<Char>> : 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; |
|||
formatter<T, Char> fmt; |
|||
|
|||
template <typename OutputIt, typename... Args> |
|||
constexpr FMT_INLINE OutputIt format(OutputIt out, |
|||
const Args&... args) const { |
|||
const auto& vargs = |
|||
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...); |
|||
basic_format_context<OutputIt, Char> ctx(out, vargs); |
|||
return fmt.format(get_arg_checked<T, N>(args...), 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> |
|||
constexpr 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; |
|||
}; |
|||
|
|||
constexpr int manual_indexing_id = -1; |
|||
|
|||
template <typename T, typename Char> |
|||
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, |
|||
size_t pos, int next_arg_id) { |
|||
str.remove_prefix(pos); |
|||
auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, {}, |
|||
next_arg_id); |
|||
auto f = formatter<T, Char>(); |
|||
auto end = f.parse(ctx); |
|||
return {f, pos + fmt::detail::to_unsigned(end - str.data()), |
|||
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; |
|||
} |
|||
|
|||
template <typename Char> struct arg_id_handler { |
|||
arg_ref<Char> arg_id; |
|||
|
|||
constexpr int operator()() { |
|||
FMT_ASSERT(false, "handler cannot be used with automatic indexing"); |
|||
return 0; |
|||
} |
|||
constexpr int operator()(int id) { |
|||
arg_id = arg_ref<Char>(id); |
|||
return 0; |
|||
} |
|||
constexpr int operator()(basic_string_view<Char> id) { |
|||
arg_id = arg_ref<Char>(id); |
|||
return 0; |
|||
} |
|||
|
|||
constexpr void on_error(const char* message) { |
|||
FMT_THROW(format_error(message)); |
|||
} |
|||
}; |
|||
|
|||
template <typename Char> struct parse_arg_id_result { |
|||
arg_ref<Char> arg_id; |
|||
const Char* arg_id_end; |
|||
}; |
|||
|
|||
template <int ID, typename Char> |
|||
constexpr auto parse_arg_id(const Char* begin, const Char* end) { |
|||
auto handler = arg_id_handler<Char>{arg_ref<Char>{}}; |
|||
auto arg_id_end = parse_arg_id(begin, end, handler); |
|||
return parse_arg_id_result<Char>{handler.arg_id, arg_id_end}; |
|||
} |
|||
|
|||
template <typename T, typename Enable = void> struct field_type { |
|||
using type = remove_cvref_t<T>; |
|||
}; |
|||
|
|||
template <typename T> |
|||
struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> { |
|||
using type = remove_cvref_t<decltype(T::value)>; |
|||
}; |
|||
|
|||
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID, |
|||
typename S> |
|||
constexpr auto parse_replacement_field_then_tail(S format_str) { |
|||
using char_type = typename S::char_type; |
|||
constexpr auto str = basic_string_view<char_type>(format_str); |
|||
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); |
|||
if constexpr (c == '}') { |
|||
return parse_tail<Args, END_POS + 1, NEXT_ID>( |
|||
field<char_type, typename field_type<T>::type, ARG_INDEX>(), |
|||
format_str); |
|||
} else if constexpr (c != ':') { |
|||
FMT_THROW(format_error("expected ':'")); |
|||
} else { |
|||
constexpr auto result = parse_specs<typename field_type<T>::type>( |
|||
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); |
|||
if constexpr (result.end >= str.size() || str[result.end] != '}') { |
|||
FMT_THROW(format_error("expected '}'")); |
|||
return 0; |
|||
} else { |
|||
return parse_tail<Args, result.end + 1, result.next_arg_id>( |
|||
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{ |
|||
result.fmt}, |
|||
format_str); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 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 auto str = basic_string_view<char_type>(format_str); |
|||
if constexpr (str[POS] == '{') { |
|||
if constexpr (POS + 1 == str.size()) |
|||
FMT_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] == '}' || str[POS + 1] == ':') { |
|||
static_assert(ID != manual_indexing_id, |
|||
"cannot switch from manual to automatic argument indexing"); |
|||
constexpr auto next_id = |
|||
ID != manual_indexing_id ? ID + 1 : manual_indexing_id; |
|||
return parse_replacement_field_then_tail<get_type<ID, Args>, Args, |
|||
POS + 1, ID, next_id>( |
|||
format_str); |
|||
} else { |
|||
constexpr auto arg_id_result = |
|||
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size()); |
|||
constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data(); |
|||
constexpr char_type c = |
|||
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); |
|||
static_assert(c == '}' || c == ':', "missing '}' in format string"); |
|||
if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) { |
|||
static_assert( |
|||
ID == manual_indexing_id || ID == 0, |
|||
"cannot switch from automatic to manual argument indexing"); |
|||
constexpr auto arg_index = arg_id_result.arg_id.val.index; |
|||
return parse_replacement_field_then_tail<get_type<arg_index, Args>, |
|||
Args, arg_id_end_pos, |
|||
arg_index, manual_indexing_id>( |
|||
format_str); |
|||
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { |
|||
constexpr auto arg_index = |
|||
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); |
|||
if constexpr (arg_index != invalid_arg_index) { |
|||
constexpr auto next_id = |
|||
ID != manual_indexing_id ? ID + 1 : manual_indexing_id; |
|||
return parse_replacement_field_then_tail< |
|||
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos, |
|||
arg_index, next_id>(format_str); |
|||
} else { |
|||
if constexpr (c == '}') { |
|||
return parse_tail<Args, arg_id_end_pos + 1, ID>( |
|||
runtime_named_field<char_type>{arg_id_result.arg_id.val.name}, |
|||
format_str); |
|||
} else if constexpr (c == ':') { |
|||
return unknown_format(); // no type info for specs parsing |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} else if constexpr (str[POS] == '}') { |
|||
if constexpr (POS + 1 == str.size()) |
|||
FMT_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(detail::is_compiled_string<S>::value)> |
|||
constexpr auto compile(S format_str) { |
|||
constexpr auto str = basic_string_view<typename S::char_type>(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); |
|||
return result; |
|||
} |
|||
} |
|||
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) |
|||
} // namespace detail |
|||
|
|||
FMT_MODULE_EXPORT_BEGIN |
|||
|
|||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) |
|||
|
|||
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) { |
|||
auto s = std::basic_string<Char>(); |
|||
cf.format(std::back_inserter(s), args...); |
|||
return s; |
|||
} |
|||
|
|||
template <typename OutputIt, typename CompiledFormat, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> |
|||
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, |
|||
const Args&... args) { |
|||
return cf.format(out, args...); |
|||
} |
|||
|
|||
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) { |
|||
if constexpr (std::is_same<typename S::char_type, char>::value) { |
|||
constexpr auto str = basic_string_view<typename S::char_type>(S()); |
|||
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { |
|||
const auto& first = detail::first(args...); |
|||
if constexpr (detail::is_named_arg< |
|||
remove_cvref_t<decltype(first)>>::value) { |
|||
return fmt::to_string(first.value); |
|||
} else { |
|||
return fmt::to_string(first); |
|||
} |
|||
} |
|||
} |
|||
constexpr auto compiled = detail::compile<Args...>(S()); |
|||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, |
|||
detail::unknown_format>()) { |
|||
return fmt::format( |
|||
static_cast<basic_string_view<typename S::char_type>>(S()), |
|||
std::forward<Args>(args)...); |
|||
} else { |
|||
return fmt::format(compiled, std::forward<Args>(args)...); |
|||
} |
|||
} |
|||
|
|||
template <typename OutputIt, typename S, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> |
|||
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { |
|||
constexpr auto compiled = detail::compile<Args...>(S()); |
|||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, |
|||
detail::unknown_format>()) { |
|||
return fmt::format_to( |
|||
out, static_cast<basic_string_view<typename S::char_type>>(S()), |
|||
std::forward<Args>(args)...); |
|||
} else { |
|||
return fmt::format_to(out, compiled, std::forward<Args>(args)...); |
|||
} |
|||
} |
|||
#endif |
|||
|
|||
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& format_str, Args&&... args) { |
|||
auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n), |
|||
format_str, std::forward<Args>(args)...); |
|||
return {it.base(), it.count()}; |
|||
} |
|||
|
|||
template <typename S, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> |
|||
FMT_CONSTEXPR20 size_t formatted_size(const S& format_str, |
|||
const Args&... args) { |
|||
return fmt::format_to(detail::counting_iterator(), format_str, args...) |
|||
.count(); |
|||
} |
|||
|
|||
template <typename S, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> |
|||
void print(std::FILE* f, const S& format_str, const Args&... args) { |
|||
memory_buffer buffer; |
|||
fmt::format_to(std::back_inserter(buffer), format_str, args...); |
|||
detail::print(f, {buffer.data(), buffer.size()}); |
|||
} |
|||
|
|||
template <typename S, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> |
|||
void print(const S& format_str, const Args&... args) { |
|||
print(stdout, format_str, args...); |
|||
} |
|||
|
|||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS |
|||
inline namespace literals { |
|||
template <detail_exported::fixed_string Str> constexpr auto operator""_cf() { |
|||
using char_t = remove_cvref_t<decltype(Str.data[0])>; |
|||
return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t), |
|||
Str>(); |
|||
} |
|||
} // namespace literals |
|||
#endif |
|||
|
|||
FMT_MODULE_EXPORT_END |
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_COMPILE_H_ |
3323
libfuse/lib/fmt/core.h
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1723
libfuse/lib/fmt/format-inl.h
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
4217
libfuse/lib/fmt/format.h
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,478 @@ |
|||
// 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_ |
|||
|
|||
#include <cerrno> |
|||
#include <cstddef> |
|||
#include <cstdio> |
|||
#include <system_error> // std::system_error |
|||
|
|||
#if defined __APPLE__ || defined(__FreeBSD__) |
|||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X |
|||
#endif |
|||
|
|||
#include "format.h" |
|||
|
|||
#ifndef FMT_USE_FCNTL |
|||
// 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 |
|||
#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 |
|||
FMT_MODULE_EXPORT_BEGIN |
|||
|
|||
/** |
|||
\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>; |
|||
|
|||
template <typename Char> struct formatter<std::error_code, Char> { |
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
return ctx.begin(); |
|||
} |
|||
|
|||
template <typename FormatContext> |
|||
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const |
|||
-> decltype(ctx.out()) { |
|||
auto out = ctx.out(); |
|||
out = detail::write_bytes(out, ec.category().name(), |
|||
basic_format_specs<Char>()); |
|||
out = detail::write<Char>(out, Char(':')); |
|||
out = detail::write<Char>(out, ec.value()); |
|||
return out; |
|||
} |
|||
}; |
|||
|
|||
#ifdef _WIN32 |
|||
FMT_API const std::error_category& system_category() noexcept; |
|||
|
|||
FMT_BEGIN_DETAIL_NAMESPACE |
|||
// 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(basic_string_view<wchar_t> 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(basic_string_view<wchar_t> s); |
|||
}; |
|||
|
|||
FMT_API void format_windows_error(buffer<char>& out, int error_code, |
|||
const char* message) noexcept; |
|||
FMT_END_DETAIL_NAMESPACE |
|||
|
|||
FMT_API std::system_error vwindows_error(int error_code, string_view format_str, |
|||
format_args args); |
|||
|
|||
/** |
|||
\rst |
|||
Constructs a :class:`std::system_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 system_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> |
|||
std::system_error windows_error(int error_code, string_view message, |
|||
const Args&... args) { |
|||
return vwindows_error(error_code, message, fmt::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, const char* message) noexcept; |
|||
#else |
|||
inline const std::error_category& system_category() noexcept { |
|||
return std::system_category(); |
|||
} |
|||
#endif // _WIN32 |
|||
|
|||
// std::system is not available on some platforms such as iOS (#2248). |
|||
#ifdef __OSX__ |
|||
template <typename S, typename... Args, typename Char = char_t<S>> |
|||
void say(const S& format_str, Args&&... args) { |
|||
std::system(format("say \"{}\"", format(format_str, args...)).c_str()); |
|||
} |
|||
#endif |
|||
|
|||
// 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() noexcept : file_(nullptr) {} |
|||
|
|||
// Destroys the object closing the file it represents if any. |
|||
FMT_API ~buffered_file() noexcept; |
|||
|
|||
public: |
|||
buffered_file(buffered_file&& other) 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 noexcept { return file_; } |
|||
|
|||
FMT_API int descriptor() 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, fmt::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 noexcept may throw |
|||
// fmt::system_error in case of failure. Note that some errors such as |
|||
// closing the file multiple times will cause a crash on Windows rather |
|||
// than an exception. You can get standard behavior by overriding the |
|||
// invalid parameter handler with _set_invalid_parameter_handler. |
|||
class FMT_API file { |
|||
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. |
|||
TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file. |
|||
}; |
|||
|
|||
// Constructs a file object which doesn't represent any file. |
|||
file() noexcept : fd_(-1) {} |
|||
|
|||
// Opens a file and constructs a file object representing this file. |
|||
file(cstring_view path, int oflag); |
|||
|
|||
public: |
|||
file(const file&) = delete; |
|||
void operator=(const file&) = delete; |
|||
|
|||
file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } |
|||
|
|||
// Move assignment is not noexcept because close may throw. |
|||
file& operator=(file&& other) { |
|||
close(); |
|||
fd_ = other.fd_; |
|||
other.fd_ = -1; |
|||
return *this; |
|||
} |
|||
|
|||
// Destroys the object closing the file it represents if any. |
|||
~file() noexcept; |
|||
|
|||
// Returns the file descriptor. |
|||
int descriptor() const noexcept { return fd_; } |
|||
|
|||
// Closes the file. |
|||
void close(); |
|||
|
|||
// Returns the file size. The size has signed type for consistency with |
|||
// stat::st_size. |
|||
long long size() const; |
|||
|
|||
// Attempts to read count bytes from the file into the specified buffer. |
|||
size_t read(void* buffer, size_t count); |
|||
|
|||
// Attempts to write count bytes from the specified buffer to the file. |
|||
size_t write(const void* buffer, size_t count); |
|||
|
|||
// Duplicates a file descriptor with the dup function and returns |
|||
// the duplicate as a file object. |
|||
static file dup(int fd); |
|||
|
|||
// Makes fd be the copy of this file descriptor, closing fd first if |
|||
// necessary. |
|||
void dup2(int fd); |
|||
|
|||
// Makes fd be the copy of this file descriptor, closing fd first if |
|||
// necessary. |
|||
void dup2(int fd, std::error_code& ec) noexcept; |
|||
|
|||
// Creates a pipe setting up read_end and write_end file objects for reading |
|||
// and writing respectively. |
|||
static void pipe(file& read_end, file& write_end); |
|||
|
|||
// Creates a buffered_file object associated with this file and detaches |
|||
// this file object from the file. |
|||
buffered_file fdopen(const char* mode); |
|||
}; |
|||
|
|||
// Returns the memory page size. |
|||
long getpagesize(); |
|||
|
|||
FMT_BEGIN_DETAIL_NAMESPACE |
|||
|
|||
struct buffer_size { |
|||
buffer_size() = default; |
|||
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 | file::TRUNC; |
|||
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; |
|||
|
|||
ostream_params() {} |
|||
|
|||
template <typename... T> |
|||
ostream_params(T... params, int new_oflag) : ostream_params(params...) { |
|||
oflag = new_oflag; |
|||
} |
|||
|
|||
template <typename... T> |
|||
ostream_params(T... params, detail::buffer_size bs) |
|||
: ostream_params(params...) { |
|||
this->buffer_size = bs.value; |
|||
} |
|||
|
|||
// Intel has a bug that results in failure to deduce a constructor |
|||
// for empty parameter packs. |
|||
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000 |
|||
ostream_params(int new_oflag) : oflag(new_oflag) {} |
|||
ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {} |
|||
# endif |
|||
}; |
|||
|
|||
FMT_END_DETAIL_NAMESPACE |
|||
|
|||
// Added {} below to work around default constructor error known to |
|||
// occur in Xcode versions 7.2.1 and 8.2.1. |
|||
constexpr detail::buffer_size buffer_size{}; |
|||
|
|||
/** A fast output stream which is not thread-safe. */ |
|||
class FMT_API ostream final : private detail::buffer<char> { |
|||
private: |
|||
file file_; |
|||
|
|||
void grow(size_t) override; |
|||
|
|||
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.clear(); |
|||
other.set(nullptr, 0); |
|||
} |
|||
~ostream() { |
|||
flush(); |
|||
delete[] data(); |
|||
} |
|||
|
|||
void flush() { |
|||
if (size() == 0) return; |
|||
file_.write(data(), size()); |
|||
clear(); |
|||
} |
|||
|
|||
template <typename... T> |
|||
friend ostream output_file(cstring_view path, T... params); |
|||
|
|||
void close() { |
|||
flush(); |
|||
file_.close(); |
|||
} |
|||
|
|||
/** |
|||
Formats ``args`` according to specifications in ``fmt`` and writes the |
|||
output to the file. |
|||
*/ |
|||
template <typename... T> void print(format_string<T...> fmt, T&&... args) { |
|||
vformat_to(detail::buffer_appender<char>(*this), fmt, |
|||
fmt::make_format_args(args...)); |
|||
} |
|||
}; |
|||
|
|||
/** |
|||
\rst |
|||
Opens a file for writing. Supported parameters passed in *params*: |
|||
|
|||
* ``<integer>``: Flags passed to `open |
|||
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_ |
|||
(``file::WRONLY | file::CREATE | file::TRUNC`` by default) |
|||
* ``buffer_size=<integer>``: Output buffer size |
|||
|
|||
**Example**:: |
|||
|
|||
auto out = fmt::output_file("guide.txt"); |
|||
out.print("Don't {}", "Panic"); |
|||
\endrst |
|||
*/ |
|||
template <typename... T> |
|||
inline ostream output_file(cstring_view path, T... params) { |
|||
return {path, detail::ostream_params(params...)}; |
|||
} |
|||
#endif // FMT_USE_FCNTL |
|||
|
|||
FMT_MODULE_EXPORT_END |
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_OS_H_ |
@ -0,0 +1,237 @@ |
|||
// 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 <fstream> |
|||
#include <ostream> |
|||
#if defined(_WIN32) && defined(__GLIBCXX__) |
|||
# include <ext/stdio_filebuf.h> |
|||
# include <ext/stdio_sync_filebuf.h> |
|||
#elif defined(_WIN32) && defined(_LIBCPP_VERSION) |
|||
# include <__std_stream> |
|||
#endif |
|||
|
|||
#include "format.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
|
|||
template <typename OutputIt, typename Char> class basic_printf_context; |
|||
|
|||
namespace detail { |
|||
|
|||
// Checks if T has a user-defined operator<<. |
|||
template <typename T, typename Char, typename Enable = void> |
|||
class is_streamable { |
|||
private: |
|||
template <typename U> |
|||
static auto test(int) |
|||
-> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>() |
|||
<< std::declval<U>()) != 0>; |
|||
|
|||
template <typename> static auto test(...) -> std::false_type; |
|||
|
|||
using result = decltype(test<T>(0)); |
|||
|
|||
public: |
|||
is_streamable() = default; |
|||
|
|||
static const bool value = result::value; |
|||
}; |
|||
|
|||
// Formatting of built-in types and arrays is intentionally disabled because |
|||
// it's handled by standard (non-ostream) formatters. |
|||
template <typename T, typename Char> |
|||
struct is_streamable< |
|||
T, Char, |
|||
enable_if_t< |
|||
std::is_arithmetic<T>::value || std::is_array<T>::value || |
|||
std::is_pointer<T>::value || std::is_same<T, char8_type>::value || |
|||
std::is_convertible<T, fmt::basic_string_view<Char>>::value || |
|||
std::is_same<T, std_string_view<Char>>::value || |
|||
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>> |
|||
: std::false_type {}; |
|||
|
|||
// Generate a unique explicit instantion in every translation unit using a tag |
|||
// type in an anonymous namespace. |
|||
namespace { |
|||
struct file_access_tag {}; |
|||
} // namespace |
|||
template <class Tag, class BufType, FILE* BufType::*FileMemberPtr> |
|||
class file_access { |
|||
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } |
|||
}; |
|||
|
|||
#if FMT_MSC_VERSION |
|||
template class file_access<file_access_tag, std::filebuf, |
|||
&std::filebuf::_Myfile>; |
|||
auto get_file(std::filebuf&) -> FILE*; |
|||
#elif defined(_WIN32) && defined(_LIBCPP_VERSION) |
|||
template class file_access<file_access_tag, std::__stdoutbuf<char>, |
|||
&std::__stdoutbuf<char>::__file_>; |
|||
auto get_file(std::__stdoutbuf<char>&) -> FILE*; |
|||
#endif |
|||
|
|||
inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) { |
|||
#if FMT_MSC_VERSION |
|||
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf())) |
|||
if (FILE* f = get_file(*buf)) return write_console(f, data); |
|||
#elif defined(_WIN32) && defined(__GLIBCXX__) |
|||
auto* rdbuf = os.rdbuf(); |
|||
FILE* c_file; |
|||
if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf)) |
|||
c_file = fbuf->file(); |
|||
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf)) |
|||
c_file = fbuf->file(); |
|||
else |
|||
return false; |
|||
if (c_file) return write_console(c_file, data); |
|||
#elif defined(_WIN32) && defined(_LIBCPP_VERSION) |
|||
if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf())) |
|||
if (FILE* f = get_file(*buf)) return write_console(f, data); |
|||
#else |
|||
ignore_unused(os, data); |
|||
#endif |
|||
return false; |
|||
} |
|||
inline bool write_ostream_unicode(std::wostream&, |
|||
fmt::basic_string_view<wchar_t>) { |
|||
return false; |
|||
} |
|||
|
|||
// Write the content of buf to os. |
|||
// It is a separate function rather than a part of vprint to simplify testing. |
|||
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()) { |
|||
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf); |
|||
auto&& output = std::basic_ostream<Char>(&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); |
|||
} |
|||
|
|||
template <typename T> struct streamed_view { const T& value; }; |
|||
|
|||
} // namespace detail |
|||
|
|||
// Formats an object of type T that has an overloaded ostream operator<<. |
|||
template <typename Char> |
|||
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> { |
|||
void set_debug_format() = delete; |
|||
|
|||
template <typename T, typename OutputIt> |
|||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const |
|||
-> OutputIt { |
|||
auto buffer = basic_memory_buffer<Char>(); |
|||
format_value(buffer, value, ctx.locale()); |
|||
return formatter<basic_string_view<Char>, Char>::format( |
|||
{buffer.data(), buffer.size()}, ctx); |
|||
} |
|||
}; |
|||
|
|||
using ostream_formatter = basic_ostream_formatter<char>; |
|||
|
|||
template <typename T, typename Char> |
|||
struct formatter<detail::streamed_view<T>, Char> |
|||
: basic_ostream_formatter<Char> { |
|||
template <typename OutputIt> |
|||
auto format(detail::streamed_view<T> view, |
|||
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt { |
|||
return basic_ostream_formatter<Char>::format(view.value, ctx); |
|||
} |
|||
}; |
|||
|
|||
/** |
|||
\rst |
|||
Returns a view that formats `value` via an ostream ``operator<<``. |
|||
|
|||
**Example**:: |
|||
|
|||
fmt::print("Current thread id: {}\n", |
|||
fmt::streamed(std::this_thread::get_id())); |
|||
\endrst |
|||
*/ |
|||
template <typename T> |
|||
auto streamed(const T& value) -> detail::streamed_view<T> { |
|||
return {value}; |
|||
} |
|||
|
|||
namespace detail { |
|||
|
|||
// 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>> |
|||
: basic_ostream_formatter<Char> { |
|||
using basic_ostream_formatter<Char>::format; |
|||
}; |
|||
|
|||
inline void vprint_directly(std::ostream& os, string_view format_str, |
|||
format_args args) { |
|||
auto buffer = memory_buffer(); |
|||
detail::vformat_to(buffer, format_str, args); |
|||
detail::write_buffer(os, buffer); |
|||
} |
|||
|
|||
} // namespace detail |
|||
|
|||
FMT_MODULE_EXPORT template <typename Char> |
|||
void vprint(std::basic_ostream<Char>& os, |
|||
basic_string_view<type_identity_t<Char>> format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { |
|||
auto buffer = basic_memory_buffer<Char>(); |
|||
detail::vformat_to(buffer, format_str, args); |
|||
if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return; |
|||
detail::write_buffer(os, buffer); |
|||
} |
|||
|
|||
/** |
|||
\rst |
|||
Prints formatted data to the stream *os*. |
|||
|
|||
**Example**:: |
|||
|
|||
fmt::print(cerr, "Don't {}!", "panic"); |
|||
\endrst |
|||
*/ |
|||
FMT_MODULE_EXPORT template <typename... T> |
|||
void print(std::ostream& os, format_string<T...> fmt, T&&... args) { |
|||
const auto& vargs = fmt::make_format_args(args...); |
|||
if (detail::is_utf8()) |
|||
vprint(os, fmt, vargs); |
|||
else |
|||
detail::vprint_directly(os, fmt, vargs); |
|||
} |
|||
|
|||
FMT_MODULE_EXPORT |
|||
template <typename... Args> |
|||
void print(std::wostream& os, |
|||
basic_format_string<wchar_t, type_identity_t<Args>...> fmt, |
|||
Args&&... args) { |
|||
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...)); |
|||
} |
|||
|
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_OSTREAM_H_ |
@ -0,0 +1,640 @@ |
|||
// 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 "format.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
FMT_MODULE_EXPORT_BEGIN |
|||
|
|||
template <typename T> struct printf_formatter { printf_formatter() = delete; }; |
|||
|
|||
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 { |
|||
private: |
|||
OutputIt out_; |
|||
basic_format_args<basic_printf_context> args_; |
|||
|
|||
public: |
|||
using char_type = Char; |
|||
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>; |
|||
|
|||
/** |
|||
\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_format_args<basic_printf_context> args) |
|||
: out_(out), args_(args) {} |
|||
|
|||
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); } |
|||
|
|||
FMT_CONSTEXPR void on_error(const char* message) { |
|||
detail::error_handler().on_error(message); |
|||
} |
|||
}; |
|||
|
|||
FMT_BEGIN_DETAIL_NAMESPACE |
|||
|
|||
// 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; |
|||
} |
|||
}; |
|||
|
|||
// The ``printf`` argument formatter. |
|||
template <typename OutputIt, typename Char> |
|||
class printf_arg_formatter : public arg_formatter<Char> { |
|||
private: |
|||
using base = arg_formatter<Char>; |
|||
using context_type = basic_printf_context<OutputIt, Char>; |
|||
using format_specs = basic_format_specs<Char>; |
|||
|
|||
context_type& context_; |
|||
|
|||
OutputIt write_null_pointer(bool is_string = false) { |
|||
auto s = this->specs; |
|||
s.type = presentation_type::none; |
|||
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s); |
|||
} |
|||
|
|||
public: |
|||
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx) |
|||
: base{iter, s, locale_ref()}, context_(ctx) {} |
|||
|
|||
OutputIt operator()(monostate value) { return base::operator()(value); } |
|||
|
|||
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)> |
|||
OutputIt operator()(T value) { |
|||
// MSVC2013 fails to compile separate overloads for bool and Char so use |
|||
// std::is_same instead. |
|||
if (std::is_same<T, Char>::value) { |
|||
format_specs fmt_specs = this->specs; |
|||
if (fmt_specs.type != presentation_type::none && |
|||
fmt_specs.type != presentation_type::chr) { |
|||
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 write<Char>(this->out, static_cast<Char>(value), fmt_specs); |
|||
} |
|||
return base::operator()(value); |
|||
} |
|||
|
|||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> |
|||
OutputIt operator()(T value) { |
|||
return base::operator()(value); |
|||
} |
|||
|
|||
/** Formats a null-terminated C string. */ |
|||
OutputIt operator()(const char* value) { |
|||
if (value) return base::operator()(value); |
|||
return write_null_pointer(this->specs.type != presentation_type::pointer); |
|||
} |
|||
|
|||
/** Formats a null-terminated wide C string. */ |
|||
OutputIt operator()(const wchar_t* value) { |
|||
if (value) return base::operator()(value); |
|||
return write_null_pointer(this->specs.type != presentation_type::pointer); |
|||
} |
|||
|
|||
OutputIt operator()(basic_string_view<Char> value) { |
|||
return base::operator()(value); |
|||
} |
|||
|
|||
/** Formats a pointer. */ |
|||
OutputIt operator()(const void* value) { |
|||
return value ? base::operator()(value) : write_null_pointer(); |
|||
} |
|||
|
|||
/** Formats an argument of a custom (user-defined) type. */ |
|||
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) { |
|||
auto parse_ctx = |
|||
basic_printf_parse_context<Char>(basic_string_view<Char>()); |
|||
handle.format(parse_ctx, context_); |
|||
return this->out; |
|||
} |
|||
}; |
|||
|
|||
template <typename Char> |
|||
void parse_flags(basic_format_specs<Char>& 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 Char, typename GetArg> |
|||
int parse_header(const Char*& it, const Char* end, |
|||
basic_format_specs<Char>& specs, GetArg get_arg) { |
|||
int arg_index = -1; |
|||
Char c = *it; |
|||
if (c >= '0' && c <= '9') { |
|||
// Parse an argument index (if followed by '$') or a width possibly |
|||
// preceded with '0' flag(s). |
|||
int value = parse_nonnegative_int(it, end, -1); |
|||
if (it != end && *it == '$') { // value is an argument index |
|||
++it; |
|||
arg_index = value != -1 ? value : max_value<int>(); |
|||
} 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. |
|||
if (value == -1) FMT_THROW(format_error("number is too big")); |
|||
specs.width = value; |
|||
return arg_index; |
|||
} |
|||
} |
|||
} |
|||
parse_flags(specs, it, end); |
|||
// Parse width. |
|||
if (it != end) { |
|||
if (*it >= '0' && *it <= '9') { |
|||
specs.width = parse_nonnegative_int(it, end, -1); |
|||
if (specs.width == -1) FMT_THROW(format_error("number is too big")); |
|||
} else if (*it == '*') { |
|||
++it; |
|||
specs.width = static_cast<int>(visit_format_arg( |
|||
detail::printf_width_handler<Char>(specs), get_arg(-1))); |
|||
} |
|||
} |
|||
return arg_index; |
|||
} |
|||
|
|||
template <typename Char, typename Context> |
|||
void vprintf(buffer<Char>& buf, basic_string_view<Char> format, |
|||
basic_format_args<Context> args) { |
|||
using OutputIt = buffer_appender<Char>; |
|||
auto out = OutputIt(buf); |
|||
auto context = basic_printf_context<OutputIt, Char>(out, args); |
|||
auto parse_ctx = basic_printf_parse_context<Char>(format); |
|||
|
|||
// Returns the argument with specified index or, if arg_index is -1, the next |
|||
// argument. |
|||
auto 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(context, arg_index); |
|||
}; |
|||
|
|||
const Char* start = parse_ctx.begin(); |
|||
const Char* end = parse_ctx.end(); |
|||
auto it = start; |
|||
while (it != end) { |
|||
if (!detail::find<false, Char>(it, end, '%', it)) { |
|||
it = end; // detail::find leaves it == nullptr if it doesn't find '%' |
|||
break; |
|||
} |
|||
Char c = *it++; |
|||
if (it != end && *it == c) { |
|||
out = detail::write( |
|||
out, basic_string_view<Char>(start, detail::to_unsigned(it - start))); |
|||
start = ++it; |
|||
continue; |
|||
} |
|||
out = detail::write(out, basic_string_view<Char>( |
|||
start, detail::to_unsigned(it - 1 - start))); |
|||
|
|||
basic_format_specs<Char> specs; |
|||
specs.align = align::right; |
|||
|
|||
// Parse argument index, flags and width. |
|||
int arg_index = parse_header(it, end, specs, get_arg); |
|||
if (arg_index == 0) parse_ctx.on_error("argument not found"); |
|||
|
|||
// Parse precision. |
|||
if (it != end && *it == '.') { |
|||
++it; |
|||
c = it != end ? *it : 0; |
|||
if ('0' <= c && c <= '9') { |
|||
specs.precision = parse_nonnegative_int(it, end, 0); |
|||
} else if (c == '*') { |
|||
++it; |
|||
specs.precision = static_cast<int>( |
|||
visit_format_arg(detail::printf_precision_handler(), get_arg(-1))); |
|||
} else { |
|||
specs.precision = 0; |
|||
} |
|||
} |
|||
|
|||
auto 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<OutputIt, Char>>( |
|||
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 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")); |
|||
char type = static_cast<char>(*it++); |
|||
if (arg.is_integral()) { |
|||
// Normalize type. |
|||
switch (type) { |
|||
case 'i': |
|||
case 'u': |
|||
type = 'd'; |
|||
break; |
|||
case 'c': |
|||
visit_format_arg( |
|||
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg), |
|||
arg); |
|||
break; |
|||
} |
|||
} |
|||
specs.type = parse_presentation_type(type); |
|||
if (specs.type == presentation_type::none) |
|||
parse_ctx.on_error("invalid type specifier"); |
|||
|
|||
start = it; |
|||
|
|||
// Format argument. |
|||
out = visit_format_arg( |
|||
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg); |
|||
} |
|||
detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start))); |
|||
} |
|||
FMT_END_DETAIL_NAMESPACE |
|||
|
|||
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... T> |
|||
inline auto make_printf_args(const T&... args) |
|||
-> format_arg_store<printf_context, T...> { |
|||
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... T> |
|||
inline auto make_wprintf_args(const T&... args) |
|||
-> format_arg_store<wprintf_context, T...> { |
|||
return {args...}; |
|||
} |
|||
|
|||
template <typename S, typename Char = char_t<S>> |
|||
inline auto vsprintf( |
|||
const S& fmt, |
|||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) |
|||
-> std::basic_string<Char> { |
|||
basic_memory_buffer<Char> buffer; |
|||
vprintf(buffer, detail::to_string_view(fmt), 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... T, |
|||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> |
|||
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> { |
|||
using context = basic_printf_context_t<Char>; |
|||
return vsprintf(detail::to_string_view(fmt), |
|||
fmt::make_format_args<context>(args...)); |
|||
} |
|||
|
|||
template <typename S, typename Char = char_t<S>> |
|||
inline auto vfprintf( |
|||
std::FILE* f, const S& fmt, |
|||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) |
|||
-> int { |
|||
basic_memory_buffer<Char> buffer; |
|||
vprintf(buffer, detail::to_string_view(fmt), 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... T, typename Char = char_t<S>> |
|||
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { |
|||
using context = basic_printf_context_t<Char>; |
|||
return vfprintf(f, detail::to_string_view(fmt), |
|||
fmt::make_format_args<context>(args...)); |
|||
} |
|||
|
|||
template <typename S, typename Char = char_t<S>> |
|||
inline auto vprintf( |
|||
const S& fmt, |
|||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) |
|||
-> int { |
|||
return vfprintf(stdout, detail::to_string_view(fmt), args); |
|||
} |
|||
|
|||
/** |
|||
\rst |
|||
Prints formatted data to ``stdout``. |
|||
|
|||
**Example**:: |
|||
|
|||
fmt::printf("Elapsed time: %.2f seconds", 1.23); |
|||
\endrst |
|||
*/ |
|||
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)> |
|||
inline auto printf(const S& fmt, const T&... args) -> int { |
|||
return vprintf( |
|||
detail::to_string_view(fmt), |
|||
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...)); |
|||
} |
|||
|
|||
FMT_MODULE_EXPORT_END |
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_PRINTF_H_ |
@ -0,0 +1,722 @@ |
|||
// 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 <tuple> |
|||
#include <type_traits> |
|||
|
|||
#include "format.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
|
|||
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; |
|||
} |
|||
|
|||
template <typename OutputIterator> |
|||
OutputIterator copy(wchar_t ch, OutputIterator out) { |
|||
*out++ = ch; |
|||
return out; |
|||
} |
|||
|
|||
// Returns true if T has a std::string-like interface, like std::string_view. |
|||
template <typename T> class is_std_string_like { |
|||
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 constexpr const bool value = |
|||
is_string<T>::value || |
|||
std::is_convertible<T, std_string_view<char>>::value || |
|||
!std::is_void<decltype(check<T>(nullptr))>::value; |
|||
}; |
|||
|
|||
template <typename Char> |
|||
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {}; |
|||
|
|||
template <typename T> class is_map { |
|||
template <typename U> static auto check(U*) -> typename U::mapped_type; |
|||
template <typename> static void check(...); |
|||
|
|||
public: |
|||
#ifdef FMT_FORMAT_MAP_AS_LIST |
|||
static constexpr const bool value = false; |
|||
#else |
|||
static constexpr const bool value = |
|||
!std::is_void<decltype(check<T>(nullptr))>::value; |
|||
#endif |
|||
}; |
|||
|
|||
template <typename T> class is_set { |
|||
template <typename U> static auto check(U*) -> typename U::key_type; |
|||
template <typename> static void check(...); |
|||
|
|||
public: |
|||
#ifdef FMT_FORMAT_SET_AS_LIST |
|||
static constexpr const bool value = false; |
|||
#else |
|||
static constexpr const bool value = |
|||
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value; |
|||
#endif |
|||
}; |
|||
|
|||
template <typename... Ts> struct conditional_helper {}; |
|||
|
|||
template <typename T, typename _ = void> struct is_range_ : std::false_type {}; |
|||
|
|||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800 |
|||
|
|||
# define FMT_DECLTYPE_RETURN(val) \ |
|||
->decltype(val) { return val; } \ |
|||
static_assert( \ |
|||
true, "") // This makes it so that a semicolon is required after the |
|||
// macro, which helps clang-format handle the formatting. |
|||
|
|||
// C array overload |
|||
template <typename T, std::size_t N> |
|||
auto range_begin(const T (&arr)[N]) -> const T* { |
|||
return arr; |
|||
} |
|||
template <typename T, std::size_t N> |
|||
auto range_end(const T (&arr)[N]) -> const T* { |
|||
return arr + N; |
|||
} |
|||
|
|||
template <typename T, typename Enable = void> |
|||
struct has_member_fn_begin_end_t : std::false_type {}; |
|||
|
|||
template <typename T> |
|||
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()), |
|||
decltype(std::declval<T>().end())>> |
|||
: std::true_type {}; |
|||
|
|||
// Member function overload |
|||
template <typename T> |
|||
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin()); |
|||
template <typename T> |
|||
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end()); |
|||
|
|||
// ADL overload. Only participates in overload resolution if member functions |
|||
// are not found. |
|||
template <typename T> |
|||
auto range_begin(T&& rng) |
|||
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value, |
|||
decltype(begin(static_cast<T&&>(rng)))> { |
|||
return begin(static_cast<T&&>(rng)); |
|||
} |
|||
template <typename T> |
|||
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value, |
|||
decltype(end(static_cast<T&&>(rng)))> { |
|||
return end(static_cast<T&&>(rng)); |
|||
} |
|||
|
|||
template <typename T, typename Enable = void> |
|||
struct has_const_begin_end : std::false_type {}; |
|||
template <typename T, typename Enable = void> |
|||
struct has_mutable_begin_end : std::false_type {}; |
|||
|
|||
template <typename T> |
|||
struct has_const_begin_end< |
|||
T, |
|||
void_t< |
|||
decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())), |
|||
decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>> |
|||
: std::true_type {}; |
|||
|
|||
template <typename T> |
|||
struct has_mutable_begin_end< |
|||
T, void_t<decltype(detail::range_begin(std::declval<T>())), |
|||
decltype(detail::range_end(std::declval<T>())), |
|||
enable_if_t<std::is_copy_constructible<T>::value>>> |
|||
: std::true_type {}; |
|||
|
|||
template <typename T> |
|||
struct is_range_<T, void> |
|||
: std::integral_constant<bool, (has_const_begin_end<T>::value || |
|||
has_mutable_begin_end<T>::value)> {}; |
|||
# undef FMT_DECLTYPE_RETURN |
|||
#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 constexpr const bool value = |
|||
!std::is_void<decltype(check<T>(nullptr))>::value; |
|||
}; |
|||
|
|||
// Check for integer_sequence |
|||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 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 <typename T> |
|||
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>; |
|||
|
|||
template <typename T, typename C, bool = is_tuple_like_<T>::value> |
|||
class is_tuple_formattable_ { |
|||
public: |
|||
static constexpr const bool value = false; |
|||
}; |
|||
template <typename T, typename C> class is_tuple_formattable_<T, C, true> { |
|||
template <std::size_t... I> |
|||
static std::true_type check2(index_sequence<I...>, |
|||
integer_sequence<bool, (I == I)...>); |
|||
static std::false_type check2(...); |
|||
template <std::size_t... I> |
|||
static decltype(check2( |
|||
index_sequence<I...>{}, |
|||
integer_sequence< |
|||
bool, (is_formattable<typename std::tuple_element<I, T>::type, |
|||
C>::value)...>{})) check(index_sequence<I...>); |
|||
|
|||
public: |
|||
static constexpr const bool value = |
|||
decltype(check(tuple_index_sequence<T>{}))::value; |
|||
}; |
|||
|
|||
template <class Tuple, class F, size_t... Is> |
|||
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) 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)); |
|||
} |
|||
|
|||
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920 |
|||
// Older MSVC doesn't get the reference type correctly for arrays. |
|||
template <typename R> struct range_reference_type_impl { |
|||
using type = decltype(*detail::range_begin(std::declval<R&>())); |
|||
}; |
|||
|
|||
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> { |
|||
using type = T&; |
|||
}; |
|||
|
|||
template <typename T> |
|||
using range_reference_type = typename range_reference_type_impl<T>::type; |
|||
#else |
|||
template <typename Range> |
|||
using range_reference_type = |
|||
decltype(*detail::range_begin(std::declval<Range&>())); |
|||
#endif |
|||
|
|||
// We don't use the Range's value_type for anything, but we do need the Range's |
|||
// reference type, with cv-ref stripped. |
|||
template <typename Range> |
|||
using uncvref_type = remove_cvref_t<range_reference_type<Range>>; |
|||
|
|||
template <typename Range> |
|||
using uncvref_first_type = |
|||
remove_cvref_t<decltype(std::declval<range_reference_type<Range>>().first)>; |
|||
|
|||
template <typename Range> |
|||
using uncvref_second_type = remove_cvref_t< |
|||
decltype(std::declval<range_reference_type<Range>>().second)>; |
|||
|
|||
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) { |
|||
*out++ = ','; |
|||
*out++ = ' '; |
|||
return out; |
|||
} |
|||
|
|||
template <typename Char, typename OutputIt> |
|||
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt { |
|||
return write_escaped_string(out, str); |
|||
} |
|||
|
|||
template <typename Char, typename OutputIt, typename T, |
|||
FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)> |
|||
inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt { |
|||
auto sv = std_string_view<Char>(str); |
|||
return write_range_entry<Char>(out, basic_string_view<Char>(sv)); |
|||
} |
|||
|
|||
template <typename Char, typename OutputIt, typename Arg, |
|||
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)> |
|||
OutputIt write_range_entry(OutputIt out, const Arg v) { |
|||
return write_escaped_char(out, v); |
|||
} |
|||
|
|||
template < |
|||
typename Char, typename OutputIt, typename Arg, |
|||
FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value && |
|||
!std::is_same<Arg, Char>::value)> |
|||
OutputIt write_range_entry(OutputIt out, const Arg& v) { |
|||
return write<Char>(out, v); |
|||
} |
|||
|
|||
} // namespace detail |
|||
|
|||
template <typename T> struct is_tuple_like { |
|||
static constexpr const bool value = |
|||
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value; |
|||
}; |
|||
|
|||
template <typename T, typename C> struct is_tuple_formattable { |
|||
static constexpr const bool value = |
|||
detail::is_tuple_formattable_<T, C>::value; |
|||
}; |
|||
|
|||
template <typename TupleT, typename Char> |
|||
struct formatter<TupleT, Char, |
|||
enable_if_t<fmt::is_tuple_like<TupleT>::value && |
|||
fmt::is_tuple_formattable<TupleT, Char>::value>> { |
|||
private: |
|||
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; |
|||
basic_string_view<Char> opening_bracket_ = |
|||
detail::string_literal<Char, '('>{}; |
|||
basic_string_view<Char> closing_bracket_ = |
|||
detail::string_literal<Char, ')'>{}; |
|||
|
|||
// C++11 generic lambda for format(). |
|||
template <typename FormatContext> struct format_each { |
|||
template <typename T> void operator()(const T& v) { |
|||
if (i > 0) out = detail::copy_str<Char>(separator, out); |
|||
out = detail::write_range_entry<Char>(out, v); |
|||
++i; |
|||
} |
|||
int i; |
|||
typename FormatContext::iterator& out; |
|||
basic_string_view<Char> separator; |
|||
}; |
|||
|
|||
public: |
|||
FMT_CONSTEXPR formatter() {} |
|||
|
|||
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { |
|||
separator_ = sep; |
|||
} |
|||
|
|||
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, |
|||
basic_string_view<Char> close) { |
|||
opening_bracket_ = open; |
|||
closing_bracket_ = close; |
|||
} |
|||
|
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
return ctx.begin(); |
|||
} |
|||
|
|||
template <typename FormatContext = format_context> |
|||
auto format(const TupleT& values, FormatContext& ctx) const |
|||
-> decltype(ctx.out()) { |
|||
auto out = ctx.out(); |
|||
out = detail::copy_str<Char>(opening_bracket_, out); |
|||
detail::for_each(values, format_each<FormatContext>{0, out, separator_}); |
|||
out = detail::copy_str<Char>(closing_bracket_, out); |
|||
return out; |
|||
} |
|||
}; |
|||
|
|||
template <typename T, typename Char> struct is_range { |
|||
static constexpr const bool value = |
|||
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value && |
|||
!std::is_convertible<T, std::basic_string<Char>>::value && |
|||
!std::is_convertible<T, detail::std_string_view<Char>>::value; |
|||
}; |
|||
|
|||
namespace detail { |
|||
template <typename Context> struct range_mapper { |
|||
using mapper = arg_mapper<Context>; |
|||
|
|||
template <typename T, |
|||
FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)> |
|||
static auto map(T&& value) -> T&& { |
|||
return static_cast<T&&>(value); |
|||
} |
|||
template <typename T, |
|||
FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)> |
|||
static auto map(T&& value) |
|||
-> decltype(mapper().map(static_cast<T&&>(value))) { |
|||
return mapper().map(static_cast<T&&>(value)); |
|||
} |
|||
}; |
|||
|
|||
template <typename Char, typename Element> |
|||
using range_formatter_type = conditional_t< |
|||
is_formattable<Element, Char>::value, |
|||
formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map( |
|||
std::declval<Element>()))>, |
|||
Char>, |
|||
fallback_formatter<Element, Char>>; |
|||
|
|||
template <typename R> |
|||
using maybe_const_range = |
|||
conditional_t<has_const_begin_end<R>::value, const R, R>; |
|||
|
|||
// Workaround a bug in MSVC 2015 and earlier. |
|||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 |
|||
template <typename R, typename Char> |
|||
struct is_formattable_delayed |
|||
: disjunction< |
|||
is_formattable<uncvref_type<maybe_const_range<R>>, Char>, |
|||
has_fallback_formatter<uncvref_type<maybe_const_range<R>>, Char>> {}; |
|||
#endif |
|||
|
|||
} // namespace detail |
|||
|
|||
template <typename T, typename Char, typename Enable = void> |
|||
struct range_formatter; |
|||
|
|||
template <typename T, typename Char> |
|||
struct range_formatter< |
|||
T, Char, |
|||
enable_if_t<conjunction< |
|||
std::is_same<T, remove_cvref_t<T>>, |
|||
disjunction<is_formattable<T, Char>, |
|||
detail::has_fallback_formatter<T, Char>>>::value>> { |
|||
private: |
|||
detail::range_formatter_type<Char, T> underlying_; |
|||
bool custom_specs_ = false; |
|||
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; |
|||
basic_string_view<Char> opening_bracket_ = |
|||
detail::string_literal<Char, '['>{}; |
|||
basic_string_view<Char> closing_bracket_ = |
|||
detail::string_literal<Char, ']'>{}; |
|||
|
|||
template <class U> |
|||
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, int) |
|||
-> decltype(u.set_debug_format()) { |
|||
u.set_debug_format(); |
|||
} |
|||
|
|||
template <class U> |
|||
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {} |
|||
|
|||
FMT_CONSTEXPR void maybe_set_debug_format() { |
|||
maybe_set_debug_format(underlying_, 0); |
|||
} |
|||
|
|||
public: |
|||
FMT_CONSTEXPR range_formatter() {} |
|||
|
|||
FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& { |
|||
return underlying_; |
|||
} |
|||
|
|||
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { |
|||
separator_ = sep; |
|||
} |
|||
|
|||
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, |
|||
basic_string_view<Char> close) { |
|||
opening_bracket_ = open; |
|||
closing_bracket_ = close; |
|||
} |
|||
|
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
auto it = ctx.begin(); |
|||
auto end = ctx.end(); |
|||
if (it == end || *it == '}') { |
|||
maybe_set_debug_format(); |
|||
return it; |
|||
} |
|||
|
|||
if (*it == 'n') { |
|||
set_brackets({}, {}); |
|||
++it; |
|||
} |
|||
|
|||
if (*it == '}') { |
|||
maybe_set_debug_format(); |
|||
return it; |
|||
} |
|||
|
|||
if (*it != ':') |
|||
FMT_THROW(format_error("no other top-level range formatters supported")); |
|||
|
|||
custom_specs_ = true; |
|||
++it; |
|||
ctx.advance_to(it); |
|||
return underlying_.parse(ctx); |
|||
} |
|||
|
|||
template <typename R, class FormatContext> |
|||
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { |
|||
detail::range_mapper<buffer_context<Char>> mapper; |
|||
auto out = ctx.out(); |
|||
out = detail::copy_str<Char>(opening_bracket_, out); |
|||
int i = 0; |
|||
auto it = detail::range_begin(range); |
|||
auto end = detail::range_end(range); |
|||
for (; it != end; ++it) { |
|||
if (i > 0) out = detail::copy_str<Char>(separator_, out); |
|||
; |
|||
ctx.advance_to(out); |
|||
out = underlying_.format(mapper.map(*it), ctx); |
|||
++i; |
|||
} |
|||
out = detail::copy_str<Char>(closing_bracket_, out); |
|||
return out; |
|||
} |
|||
}; |
|||
|
|||
enum class range_format { disabled, map, set, sequence, string, debug_string }; |
|||
|
|||
namespace detail { |
|||
template <typename T> struct range_format_kind_ { |
|||
static constexpr auto value = std::is_same<range_reference_type<T>, T>::value |
|||
? range_format::disabled |
|||
: is_map<T>::value ? range_format::map |
|||
: is_set<T>::value ? range_format::set |
|||
: range_format::sequence; |
|||
}; |
|||
|
|||
template <range_format K, typename R, typename Char, typename Enable = void> |
|||
struct range_default_formatter; |
|||
|
|||
template <range_format K> |
|||
using range_format_constant = std::integral_constant<range_format, K>; |
|||
|
|||
template <range_format K, typename R, typename Char> |
|||
struct range_default_formatter< |
|||
K, R, Char, |
|||
enable_if_t<(K == range_format::sequence || K == range_format::map || |
|||
K == range_format::set)>> { |
|||
using range_type = detail::maybe_const_range<R>; |
|||
range_formatter<detail::uncvref_type<range_type>, Char> underlying_; |
|||
|
|||
FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); } |
|||
|
|||
FMT_CONSTEXPR void init(range_format_constant<range_format::set>) { |
|||
underlying_.set_brackets(detail::string_literal<Char, '{'>{}, |
|||
detail::string_literal<Char, '}'>{}); |
|||
} |
|||
|
|||
FMT_CONSTEXPR void init(range_format_constant<range_format::map>) { |
|||
underlying_.set_brackets(detail::string_literal<Char, '{'>{}, |
|||
detail::string_literal<Char, '}'>{}); |
|||
underlying_.underlying().set_brackets({}, {}); |
|||
underlying_.underlying().set_separator( |
|||
detail::string_literal<Char, ':', ' '>{}); |
|||
} |
|||
|
|||
FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {} |
|||
|
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
return underlying_.parse(ctx); |
|||
} |
|||
|
|||
template <typename FormatContext> |
|||
auto format(range_type& range, FormatContext& ctx) const |
|||
-> decltype(ctx.out()) { |
|||
return underlying_.format(range, ctx); |
|||
} |
|||
}; |
|||
} // namespace detail |
|||
|
|||
template <typename T, typename Char, typename Enable = void> |
|||
struct range_format_kind |
|||
: conditional_t< |
|||
is_range<T, Char>::value, detail::range_format_kind_<T>, |
|||
std::integral_constant<range_format, range_format::disabled>> {}; |
|||
|
|||
template <typename R, typename Char> |
|||
struct formatter< |
|||
R, Char, |
|||
enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value != |
|||
range_format::disabled> |
|||
// Workaround a bug in MSVC 2015 and earlier. |
|||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 |
|||
, |
|||
detail::is_formattable_delayed<R, Char> |
|||
#endif |
|||
>::value>> |
|||
: detail::range_default_formatter<range_format_kind<R, Char>::value, R, |
|||
Char> { |
|||
}; |
|||
|
|||
template <typename Char, typename... T> struct tuple_join_view : detail::view { |
|||
const std::tuple<T...>& tuple; |
|||
basic_string_view<Char> sep; |
|||
|
|||
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s) |
|||
: tuple(t), sep{s} {} |
|||
}; |
|||
|
|||
template <typename Char, typename... T> |
|||
using tuple_arg_join = tuple_join_view<Char, T...>; |
|||
|
|||
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers |
|||
// support in tuple_join. It is disabled by default because of issues with |
|||
// the dynamic width and precision. |
|||
#ifndef FMT_TUPLE_JOIN_SPECIFIERS |
|||
# define FMT_TUPLE_JOIN_SPECIFIERS 0 |
|||
#endif |
|||
|
|||
template <typename Char, typename... T> |
|||
struct formatter<tuple_join_view<Char, T...>, Char> { |
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>()); |
|||
} |
|||
|
|||
template <typename FormatContext> |
|||
auto format(const tuple_join_view<Char, T...>& value, |
|||
FormatContext& ctx) const -> typename FormatContext::iterator { |
|||
return do_format(value, ctx, |
|||
std::integral_constant<size_t, sizeof...(T)>()); |
|||
} |
|||
|
|||
private: |
|||
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_; |
|||
|
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto do_parse(ParseContext& ctx, |
|||
std::integral_constant<size_t, 0>) |
|||
-> decltype(ctx.begin()) { |
|||
return ctx.begin(); |
|||
} |
|||
|
|||
template <typename ParseContext, size_t N> |
|||
FMT_CONSTEXPR auto do_parse(ParseContext& ctx, |
|||
std::integral_constant<size_t, N>) |
|||
-> decltype(ctx.begin()) { |
|||
auto end = ctx.begin(); |
|||
#if FMT_TUPLE_JOIN_SPECIFIERS |
|||
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx); |
|||
if (N > 1) { |
|||
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>()); |
|||
if (end != end1) |
|||
FMT_THROW(format_error("incompatible format specs for tuple elements")); |
|||
} |
|||
#endif |
|||
return end; |
|||
} |
|||
|
|||
template <typename FormatContext> |
|||
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx, |
|||
std::integral_constant<size_t, 0>) const -> |
|||
typename FormatContext::iterator { |
|||
return ctx.out(); |
|||
} |
|||
|
|||
template <typename FormatContext, size_t N> |
|||
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx, |
|||
std::integral_constant<size_t, N>) const -> |
|||
typename FormatContext::iterator { |
|||
auto out = std::get<sizeof...(T) - N>(formatters_) |
|||
.format(std::get<sizeof...(T) - N>(value.tuple), ctx); |
|||
if (N > 1) { |
|||
out = std::copy(value.sep.begin(), value.sep.end(), out); |
|||
ctx.advance_to(out); |
|||
return do_format(value, ctx, std::integral_constant<size_t, N - 1>()); |
|||
} |
|||
return out; |
|||
} |
|||
}; |
|||
|
|||
FMT_MODULE_EXPORT_BEGIN |
|||
|
|||
/** |
|||
\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 auto join(const std::tuple<T...>& tuple, string_view sep) |
|||
-> tuple_join_view<char, T...> { |
|||
return {tuple, sep}; |
|||
} |
|||
|
|||
template <typename... T> |
|||
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, |
|||
basic_string_view<wchar_t> sep) |
|||
-> tuple_join_view<wchar_t, T...> { |
|||
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> |
|||
auto join(std::initializer_list<T> list, string_view sep) |
|||
-> join_view<const T*, const T*> { |
|||
return join(std::begin(list), std::end(list), sep); |
|||
} |
|||
|
|||
FMT_MODULE_EXPORT_END |
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_RANGES_H_ |
@ -0,0 +1,171 @@ |
|||
// Formatting library for C++ - formatters for standard library types |
|||
// |
|||
// Copyright (c) 2012 - present, Victor Zverovich |
|||
// All rights reserved. |
|||
// |
|||
// For the license information refer to format.h. |
|||
|
|||
#ifndef FMT_STD_H_ |
|||
#define FMT_STD_H_ |
|||
|
|||
#include <thread> |
|||
#include <type_traits> |
|||
#include <utility> |
|||
|
|||
#include "ostream.h" |
|||
|
|||
#if FMT_HAS_INCLUDE(<version>) |
|||
# include <version> |
|||
#endif |
|||
// Checking FMT_CPLUSPLUS for warning suppression in MSVC. |
|||
#if FMT_CPLUSPLUS >= 201703L |
|||
# if FMT_HAS_INCLUDE(<filesystem>) |
|||
# include <filesystem> |
|||
# endif |
|||
# if FMT_HAS_INCLUDE(<variant>) |
|||
# include <variant> |
|||
# endif |
|||
#endif |
|||
|
|||
#ifdef __cpp_lib_filesystem |
|||
FMT_BEGIN_NAMESPACE |
|||
|
|||
namespace detail { |
|||
|
|||
template <typename Char> |
|||
void write_escaped_path(basic_memory_buffer<Char>& quoted, |
|||
const std::filesystem::path& p) { |
|||
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>()); |
|||
} |
|||
# ifdef _WIN32 |
|||
template <> |
|||
inline void write_escaped_path<char>(basic_memory_buffer<char>& quoted, |
|||
const std::filesystem::path& p) { |
|||
auto s = p.u8string(); |
|||
write_escaped_string<char>( |
|||
std::back_inserter(quoted), |
|||
string_view(reinterpret_cast<const char*>(s.c_str()), s.size())); |
|||
} |
|||
# endif |
|||
template <> |
|||
inline void write_escaped_path<std::filesystem::path::value_type>( |
|||
basic_memory_buffer<std::filesystem::path::value_type>& quoted, |
|||
const std::filesystem::path& p) { |
|||
write_escaped_string<std::filesystem::path::value_type>( |
|||
std::back_inserter(quoted), p.native()); |
|||
} |
|||
|
|||
} // namespace detail |
|||
|
|||
template <typename Char> |
|||
struct formatter<std::filesystem::path, Char> |
|||
: formatter<basic_string_view<Char>> { |
|||
template <typename FormatContext> |
|||
auto format(const std::filesystem::path& p, FormatContext& ctx) const -> |
|||
typename FormatContext::iterator { |
|||
basic_memory_buffer<Char> quoted; |
|||
detail::write_escaped_path(quoted, p); |
|||
return formatter<basic_string_view<Char>>::format( |
|||
basic_string_view<Char>(quoted.data(), quoted.size()), ctx); |
|||
} |
|||
}; |
|||
FMT_END_NAMESPACE |
|||
#endif |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
template <typename Char> |
|||
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {}; |
|||
FMT_END_NAMESPACE |
|||
|
|||
#ifdef __cpp_lib_variant |
|||
FMT_BEGIN_NAMESPACE |
|||
template <typename Char> struct formatter<std::monostate, Char> { |
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
return ctx.begin(); |
|||
} |
|||
|
|||
template <typename FormatContext> |
|||
auto format(const std::monostate&, FormatContext& ctx) const |
|||
-> decltype(ctx.out()) { |
|||
auto out = ctx.out(); |
|||
out = detail::write<Char>(out, "monostate"); |
|||
return out; |
|||
} |
|||
}; |
|||
|
|||
namespace detail { |
|||
|
|||
template <typename T> |
|||
using variant_index_sequence = |
|||
std::make_index_sequence<std::variant_size<T>::value>; |
|||
|
|||
// variant_size and variant_alternative check. |
|||
template <typename T, typename U = void> |
|||
struct is_variant_like_ : std::false_type {}; |
|||
template <typename T> |
|||
struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>> |
|||
: std::true_type {}; |
|||
|
|||
// formattable element check |
|||
template <typename T, typename C> class is_variant_formattable_ { |
|||
template <std::size_t... I> |
|||
static std::conjunction< |
|||
is_formattable<std::variant_alternative_t<I, T>, C>...> |
|||
check(std::index_sequence<I...>); |
|||
|
|||
public: |
|||
static constexpr const bool value = |
|||
decltype(check(variant_index_sequence<T>{}))::value; |
|||
}; |
|||
|
|||
template <typename Char, typename OutputIt, typename T> |
|||
auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt { |
|||
if constexpr (is_string<T>::value) |
|||
return write_escaped_string<Char>(out, detail::to_string_view(v)); |
|||
else if constexpr (std::is_same_v<T, Char>) |
|||
return write_escaped_char(out, v); |
|||
else |
|||
return write<Char>(out, v); |
|||
} |
|||
|
|||
} // namespace detail |
|||
|
|||
template <typename T> struct is_variant_like { |
|||
static constexpr const bool value = detail::is_variant_like_<T>::value; |
|||
}; |
|||
|
|||
template <typename T, typename C> struct is_variant_formattable { |
|||
static constexpr const bool value = |
|||
detail::is_variant_formattable_<T, C>::value; |
|||
}; |
|||
|
|||
template <typename Variant, typename Char> |
|||
struct formatter< |
|||
Variant, Char, |
|||
std::enable_if_t<std::conjunction_v< |
|||
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> { |
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
return ctx.begin(); |
|||
} |
|||
|
|||
template <typename FormatContext> |
|||
auto format(const Variant& value, FormatContext& ctx) const |
|||
-> decltype(ctx.out()) { |
|||
auto out = ctx.out(); |
|||
|
|||
out = detail::write<Char>(out, "variant("); |
|||
std::visit( |
|||
[&](const auto& v) { |
|||
out = detail::write_variant_alternative<Char>(out, v); |
|||
}, |
|||
value); |
|||
*out++ = ')'; |
|||
return out; |
|||
} |
|||
}; |
|||
FMT_END_NAMESPACE |
|||
#endif |
|||
|
|||
#endif // FMT_STD_H_ |
@ -0,0 +1,229 @@ |
|||
// Formatting library for C++ - optional wchar_t and exotic character support |
|||
// |
|||
// Copyright (c) 2012 - present, Victor Zverovich |
|||
// All rights reserved. |
|||
// |
|||
// For the license information refer to format.h. |
|||
|
|||
#ifndef FMT_XCHAR_H_ |
|||
#define FMT_XCHAR_H_ |
|||
|
|||
#include <cwchar> |
|||
|
|||
#include "format.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
namespace detail { |
|||
template <typename T> |
|||
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>; |
|||
} |
|||
|
|||
FMT_MODULE_EXPORT_BEGIN |
|||
|
|||
using wstring_view = basic_string_view<wchar_t>; |
|||
using wformat_parse_context = basic_format_parse_context<wchar_t>; |
|||
using wformat_context = buffer_context<wchar_t>; |
|||
using wformat_args = basic_format_args<wformat_context>; |
|||
using wmemory_buffer = basic_memory_buffer<wchar_t>; |
|||
|
|||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 |
|||
// Workaround broken conversion on older gcc. |
|||
template <typename... Args> using wformat_string = wstring_view; |
|||
inline auto runtime(wstring_view s) -> wstring_view { return s; } |
|||
#else |
|||
template <typename... Args> |
|||
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>; |
|||
inline auto runtime(wstring_view s) -> basic_runtime<wchar_t> { return {{s}}; } |
|||
#endif |
|||
|
|||
template <> struct is_char<wchar_t> : std::true_type {}; |
|||
template <> struct is_char<detail::char8_type> : std::true_type {}; |
|||
template <> struct is_char<char16_t> : std::true_type {}; |
|||
template <> struct is_char<char32_t> : std::true_type {}; |
|||
|
|||
template <typename... Args> |
|||
constexpr format_arg_store<wformat_context, Args...> make_wformat_args( |
|||
const Args&... args) { |
|||
return {args...}; |
|||
} |
|||
|
|||
inline namespace literals { |
|||
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS |
|||
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) { |
|||
return {s}; |
|||
} |
|||
#endif |
|||
} // namespace literals |
|||
|
|||
template <typename It, typename Sentinel> |
|||
auto join(It begin, Sentinel end, wstring_view sep) |
|||
-> join_view<It, Sentinel, wchar_t> { |
|||
return {begin, end, sep}; |
|||
} |
|||
|
|||
template <typename Range> |
|||
auto join(Range&& range, wstring_view sep) |
|||
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>, |
|||
wchar_t> { |
|||
return join(std::begin(range), std::end(range), sep); |
|||
} |
|||
|
|||
template <typename T> |
|||
auto join(std::initializer_list<T> list, wstring_view sep) |
|||
-> join_view<const T*, const T*, wchar_t> { |
|||
return join(std::begin(list), std::end(list), sep); |
|||
} |
|||
|
|||
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)> |
|||
auto vformat(basic_string_view<Char> format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) |
|||
-> std::basic_string<Char> { |
|||
basic_memory_buffer<Char> buffer; |
|||
detail::vformat_to(buffer, format_str, args); |
|||
return to_string(buffer); |
|||
} |
|||
|
|||
template <typename... T> |
|||
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring { |
|||
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...)); |
|||
} |
|||
|
|||
// Pass char_t as a default template parameter instead of using |
|||
// std::basic_string<char_t<S>> to reduce the symbol size. |
|||
template <typename S, typename... Args, typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(!std::is_same<Char, char>::value && |
|||
!std::is_same<Char, wchar_t>::value)> |
|||
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> { |
|||
return vformat(detail::to_string_view(format_str), |
|||
fmt::make_format_args<buffer_context<Char>>(args...)); |
|||
} |
|||
|
|||
template <typename Locale, typename S, typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&& |
|||
detail::is_exotic_char<Char>::value)> |
|||
inline auto vformat( |
|||
const Locale& loc, const S& format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) |
|||
-> std::basic_string<Char> { |
|||
return detail::vformat(loc, detail::to_string_view(format_str), args); |
|||
} |
|||
|
|||
template <typename Locale, typename S, typename... Args, |
|||
typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&& |
|||
detail::is_exotic_char<Char>::value)> |
|||
inline auto format(const Locale& loc, const S& format_str, Args&&... args) |
|||
-> std::basic_string<Char> { |
|||
return detail::vformat(loc, detail::to_string_view(format_str), |
|||
fmt::make_format_args<buffer_context<Char>>(args...)); |
|||
} |
|||
|
|||
template <typename OutputIt, typename S, typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& |
|||
detail::is_exotic_char<Char>::value)> |
|||
auto vformat_to(OutputIt out, const S& format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) |
|||
-> OutputIt { |
|||
auto&& buf = detail::get_buffer<Char>(out); |
|||
detail::vformat_to(buf, detail::to_string_view(format_str), args); |
|||
return detail::get_iterator(buf); |
|||
} |
|||
|
|||
template <typename OutputIt, typename S, typename... Args, |
|||
typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& |
|||
detail::is_exotic_char<Char>::value)> |
|||
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { |
|||
return vformat_to(out, detail::to_string_view(fmt), |
|||
fmt::make_format_args<buffer_context<Char>>(args...)); |
|||
} |
|||
|
|||
template <typename Locale, typename S, typename OutputIt, typename... Args, |
|||
typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& |
|||
detail::is_locale<Locale>::value&& |
|||
detail::is_exotic_char<Char>::value)> |
|||
inline auto vformat_to( |
|||
OutputIt out, const Locale& loc, const S& format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt { |
|||
auto&& buf = detail::get_buffer<Char>(out); |
|||
vformat_to(buf, detail::to_string_view(format_str), args, |
|||
detail::locale_ref(loc)); |
|||
return detail::get_iterator(buf); |
|||
} |
|||
|
|||
template < |
|||
typename OutputIt, typename Locale, typename S, typename... Args, |
|||
typename Char = char_t<S>, |
|||
bool enable = detail::is_output_iterator<OutputIt, Char>::value&& |
|||
detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value> |
|||
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, |
|||
Args&&... args) -> |
|||
typename std::enable_if<enable, OutputIt>::type { |
|||
return vformat_to(out, loc, to_string_view(format_str), |
|||
fmt::make_format_args<buffer_context<Char>>(args...)); |
|||
} |
|||
|
|||
template <typename OutputIt, typename Char, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& |
|||
detail::is_exotic_char<Char>::value)> |
|||
inline auto vformat_to_n( |
|||
OutputIt out, size_t n, basic_string_view<Char> format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) |
|||
-> format_to_n_result<OutputIt> { |
|||
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out, |
|||
n); |
|||
detail::vformat_to(buf, format_str, args); |
|||
return {buf.out(), buf.count()}; |
|||
} |
|||
|
|||
template <typename OutputIt, typename S, typename... Args, |
|||
typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& |
|||
detail::is_exotic_char<Char>::value)> |
|||
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, |
|||
const Args&... args) -> format_to_n_result<OutputIt> { |
|||
return vformat_to_n(out, n, detail::to_string_view(fmt), |
|||
fmt::make_format_args<buffer_context<Char>>(args...)); |
|||
} |
|||
|
|||
template <typename S, typename... Args, typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> |
|||
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t { |
|||
detail::counting_buffer<Char> buf; |
|||
detail::vformat_to(buf, detail::to_string_view(fmt), |
|||
fmt::make_format_args<buffer_context<Char>>(args...)); |
|||
return buf.count(); |
|||
} |
|||
|
|||
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { |
|||
wmemory_buffer buffer; |
|||
detail::vformat_to(buffer, fmt, args); |
|||
buffer.push_back(L'\0'); |
|||
if (std::fputws(buffer.data(), f) == -1) |
|||
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); |
|||
} |
|||
|
|||
inline void vprint(wstring_view fmt, wformat_args args) { |
|||
vprint(stdout, fmt, args); |
|||
} |
|||
|
|||
template <typename... T> |
|||
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) { |
|||
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...)); |
|||
} |
|||
|
|||
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) { |
|||
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...)); |
|||
} |
|||
|
|||
/** |
|||
Converts *value* to ``std::wstring`` using the default format for type *T*. |
|||
*/ |
|||
template <typename T> inline auto to_wstring(const T& value) -> std::wstring { |
|||
return format(FMT_STRING(L"{}"), value); |
|||
} |
|||
FMT_MODULE_EXPORT_END |
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_XCHAR_H_ |
@ -0,0 +1,47 @@ |
|||
// 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 FMT_API auto dragonbox::to_decimal(float x) noexcept |
|||
-> dragonbox::decimal_fp<float>; |
|||
template FMT_API auto dragonbox::to_decimal(double x) noexcept |
|||
-> dragonbox::decimal_fp<double>; |
|||
|
|||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
|||
template FMT_API locale_ref::locale_ref(const std::locale& loc); |
|||
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale; |
|||
#endif
|
|||
|
|||
// Explicit instantiations for char.
|
|||
|
|||
template FMT_API auto thousands_sep_impl(locale_ref) |
|||
-> thousands_sep_result<char>; |
|||
template FMT_API auto decimal_point_impl(locale_ref) -> char; |
|||
|
|||
template FMT_API void buffer<char>::append(const char*, const char*); |
|||
|
|||
// DEPRECATED!
|
|||
// There is no correspondent extern template in format.h because of
|
|||
// incompatibility between clang and gcc (#2377).
|
|||
template FMT_API void vformat_to(buffer<char>&, string_view, |
|||
basic_format_args<FMT_BUFFER_CONTEXT(char)>, |
|||
locale_ref); |
|||
|
|||
// Explicit instantiations for wchar_t.
|
|||
|
|||
template FMT_API auto thousands_sep_impl(locale_ref) |
|||
-> thousands_sep_result<wchar_t>; |
|||
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; |
|||
|
|||
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*); |
|||
|
|||
} // namespace detail
|
|||
FMT_END_NAMESPACE |
6049
libfuse/lib/ghc/filesystem.hpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,361 @@ |
|||
// 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>
|
|||
|
|||
# ifndef S_IRUSR
|
|||
# define S_IRUSR _S_IREAD
|
|||
# endif
|
|||
# ifndef S_IWUSR
|
|||
# define S_IWUSR _S_IWRITE
|
|||
# endif
|
|||
# ifndef S_IRGRP
|
|||
# define S_IRGRP 0
|
|||
# endif
|
|||
# ifndef S_IWGRP
|
|||
# define S_IWGRP 0
|
|||
# endif
|
|||
# ifndef S_IROTH
|
|||
# define S_IROTH 0
|
|||
# endif
|
|||
# ifndef S_IWOTH
|
|||
# define S_IWOTH 0
|
|||
# endif
|
|||
# endif // _WIN32
|
|||
#endif // FMT_USE_FCNTL
|
|||
|
|||
#ifdef _WIN32
|
|||
# include <windows.h>
|
|||
#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(basic_string_view<wchar_t> 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(basic_string_view<wchar_t> 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; |
|||
} |
|||
|
|||
namespace detail { |
|||
|
|||
class system_message { |
|||
system_message(const system_message&) = delete; |
|||
void operator=(const system_message&) = delete; |
|||
|
|||
unsigned long result_; |
|||
wchar_t* message_; |
|||
|
|||
static bool is_whitespace(wchar_t c) noexcept { |
|||
return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0'; |
|||
} |
|||
|
|||
public: |
|||
explicit system_message(unsigned long error_code) |
|||
: result_(0), message_(nullptr) { |
|||
result_ = FormatMessageW( |
|||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | |
|||
FORMAT_MESSAGE_IGNORE_INSERTS, |
|||
nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
|||
reinterpret_cast<wchar_t*>(&message_), 0, nullptr); |
|||
if (result_ != 0) { |
|||
while (result_ != 0 && is_whitespace(message_[result_ - 1])) { |
|||
--result_; |
|||
} |
|||
} |
|||
} |
|||
~system_message() { LocalFree(message_); } |
|||
explicit operator bool() const noexcept { return result_ != 0; } |
|||
operator basic_string_view<wchar_t>() const noexcept { |
|||
return basic_string_view<wchar_t>(message_, result_); |
|||
} |
|||
}; |
|||
|
|||
class utf8_system_category final : public std::error_category { |
|||
public: |
|||
const char* name() const noexcept override { return "system"; } |
|||
std::string message(int error_code) const override { |
|||
system_message msg(error_code); |
|||
if (msg) { |
|||
utf16_to_utf8 utf8_message; |
|||
if (utf8_message.convert(msg) == ERROR_SUCCESS) { |
|||
return utf8_message.str(); |
|||
} |
|||
} |
|||
return "unknown error"; |
|||
} |
|||
}; |
|||
|
|||
} // namespace detail
|
|||
|
|||
FMT_API const std::error_category& system_category() noexcept { |
|||
static const detail::utf8_system_category category; |
|||
return category; |
|||
} |
|||
|
|||
std::system_error vwindows_error(int err_code, string_view format_str, |
|||
format_args args) { |
|||
auto ec = std::error_code(err_code, system_category()); |
|||
return std::system_error(ec, vformat(format_str, args)); |
|||
} |
|||
|
|||
void detail::format_windows_error(detail::buffer<char>& out, int error_code, |
|||
const char* message) noexcept { |
|||
FMT_TRY { |
|||
system_message msg(error_code); |
|||
if (msg) { |
|||
utf16_to_utf8 utf8_message; |
|||
if (utf8_message.convert(msg) == ERROR_SUCCESS) { |
|||
fmt::format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message); |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
FMT_CATCH(...) {} |
|||
format_error_code(out, error_code, message); |
|||
} |
|||
|
|||
void report_windows_error(int error_code, const char* message) noexcept { |
|||
report_error(detail::format_windows_error, error_code, message); |
|||
} |
|||
#endif // _WIN32
|
|||
|
|||
buffered_file::~buffered_file() 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")); |
|||
} |
|||
|
|||
int buffered_file::descriptor() const { |
|||
int fd = FMT_POSIX_CALL(fileno(file_)); |
|||
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor")); |
|||
return fd; |
|||
} |
|||
|
|||
#if FMT_USE_FCNTL
|
|||
file::file(cstring_view path, int oflag) { |
|||
# ifdef _WIN32
|
|||
using mode_t = int; |
|||
# endif
|
|||
constexpr mode_t mode = |
|||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; |
|||
# 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() 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, std::error_code& ec) noexcept { |
|||
int result = 0; |
|||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); |
|||
if (result == -1) ec = std::error_code(errno, std::generic_category()); |
|||
} |
|||
|
|||
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 |
Write
Preview
Loading…
Cancel
Save
Reference in new issue