mirror of
https://github.com/gabime/spdlog.git
synced 2024-12-26 02:21:34 +08:00
Bump fmt version to 6.2.0
This commit is contained in:
parent
c89a5148b2
commit
7698bb0ae1
@ -8,14 +8,14 @@
|
|||||||
#ifndef FMT_CHRONO_H_
|
#ifndef FMT_CHRONO_H_
|
||||||
#define FMT_CHRONO_H_
|
#define FMT_CHRONO_H_
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
#include "locale.h"
|
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include "locale.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
// Enable safe chrono durations, unless explicitly disabled.
|
// Enable safe chrono durations, unless explicitly disabled.
|
||||||
@ -495,12 +495,12 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
|
|||||||
handler.on_text(ptr - 1, ptr);
|
handler.on_text(ptr - 1, ptr);
|
||||||
break;
|
break;
|
||||||
case 'n': {
|
case 'n': {
|
||||||
const char newline[] = "\n";
|
const Char newline[] = {'\n'};
|
||||||
handler.on_text(newline, newline + 1);
|
handler.on_text(newline, newline + 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 't': {
|
case 't': {
|
||||||
const char tab[] = "\t";
|
const Char tab[] = {'\t'};
|
||||||
handler.on_text(tab, tab + 1);
|
handler.on_text(tab, tab + 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -759,18 +759,30 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
|
|||||||
return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms));
|
return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Rep, typename OutputIt>
|
template <typename Char, typename Rep, typename OutputIt>
|
||||||
OutputIt format_chrono_duration_value(OutputIt out, Rep val, int precision) {
|
OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
|
||||||
if (precision >= 0) return format_to(out, "{:.{}f}", val, precision);
|
const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0};
|
||||||
return format_to(out, std::is_floating_point<Rep>::value ? "{:g}" : "{}",
|
if (precision >= 0) return format_to(out, pr_f, val, precision);
|
||||||
|
const Char fp_f[] = {'{', ':', 'g', '}', 0};
|
||||||
|
const Char format[] = {'{', '}', 0};
|
||||||
|
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
|
||||||
val);
|
val);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Period, typename OutputIt>
|
template <typename Char, typename Period, typename OutputIt>
|
||||||
static OutputIt format_chrono_duration_unit(OutputIt out) {
|
OutputIt format_duration_unit(OutputIt out) {
|
||||||
if (const char* unit = get_units<Period>()) return format_to(out, "{}", unit);
|
if (const char* unit = get_units<Period>()) {
|
||||||
if (Period::den == 1) return format_to(out, "[{}]s", Period::num);
|
string_view s(unit);
|
||||||
return format_to(out, "[{}/{}]s", Period::num, Period::den);
|
if (const_check(std::is_same<Char, wchar_t>())) {
|
||||||
|
utf8_to_utf16 u(s);
|
||||||
|
return std::copy(u.c_str(), u.c_str() + u.size(), out);
|
||||||
|
}
|
||||||
|
return std::copy(s.begin(), s.end(), out);
|
||||||
|
}
|
||||||
|
const Char num_f[] = {'[', '{', '}', ']', 's', 0};
|
||||||
|
if (Period::den == 1) return format_to(out, num_f, Period::num);
|
||||||
|
const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0};
|
||||||
|
return format_to(out, num_def_f, Period::num, Period::den);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext, typename OutputIt, typename Rep,
|
template <typename FormatContext, typename OutputIt, typename Rep,
|
||||||
@ -871,13 +883,13 @@ struct chrono_formatter {
|
|||||||
void write_pinf() { std::copy_n("inf", 3, out); }
|
void write_pinf() { std::copy_n("inf", 3, out); }
|
||||||
void write_ninf() { std::copy_n("-inf", 4, out); }
|
void write_ninf() { std::copy_n("-inf", 4, out); }
|
||||||
|
|
||||||
void format_localized(const tm& time, const char* format) {
|
void format_localized(const tm& time, char format, char modifier = 0) {
|
||||||
if (isnan(val)) return write_nan();
|
if (isnan(val)) return write_nan();
|
||||||
auto locale = context.locale().template get<std::locale>();
|
auto locale = context.locale().template get<std::locale>();
|
||||||
auto& facet = std::use_facet<std::time_put<char_type>>(locale);
|
auto& facet = std::use_facet<std::time_put<char_type>>(locale);
|
||||||
std::basic_ostringstream<char_type> os;
|
std::basic_ostringstream<char_type> os;
|
||||||
os.imbue(locale);
|
os.imbue(locale);
|
||||||
facet.put(os, os, ' ', &time, format, format + std::strlen(format));
|
facet.put(os, os, ' ', &time, format, modifier);
|
||||||
auto str = os.str();
|
auto str = os.str();
|
||||||
std::copy(str.begin(), str.end(), out);
|
std::copy(str.begin(), str.end(), out);
|
||||||
}
|
}
|
||||||
@ -907,7 +919,7 @@ struct chrono_formatter {
|
|||||||
if (ns == numeric_system::standard) return write(hour(), 2);
|
if (ns == numeric_system::standard) return write(hour(), 2);
|
||||||
auto time = tm();
|
auto time = tm();
|
||||||
time.tm_hour = to_nonnegative_int(hour(), 24);
|
time.tm_hour = to_nonnegative_int(hour(), 24);
|
||||||
format_localized(time, "%OH");
|
format_localized(time, 'H', 'O');
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_12_hour(numeric_system ns) {
|
void on_12_hour(numeric_system ns) {
|
||||||
@ -916,7 +928,7 @@ struct chrono_formatter {
|
|||||||
if (ns == numeric_system::standard) return write(hour12(), 2);
|
if (ns == numeric_system::standard) return write(hour12(), 2);
|
||||||
auto time = tm();
|
auto time = tm();
|
||||||
time.tm_hour = to_nonnegative_int(hour12(), 12);
|
time.tm_hour = to_nonnegative_int(hour12(), 12);
|
||||||
format_localized(time, "%OI");
|
format_localized(time, 'I', 'O');
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_minute(numeric_system ns) {
|
void on_minute(numeric_system ns) {
|
||||||
@ -925,7 +937,7 @@ struct chrono_formatter {
|
|||||||
if (ns == numeric_system::standard) return write(minute(), 2);
|
if (ns == numeric_system::standard) return write(minute(), 2);
|
||||||
auto time = tm();
|
auto time = tm();
|
||||||
time.tm_min = to_nonnegative_int(minute(), 60);
|
time.tm_min = to_nonnegative_int(minute(), 60);
|
||||||
format_localized(time, "%OM");
|
format_localized(time, 'M', 'O');
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_second(numeric_system ns) {
|
void on_second(numeric_system ns) {
|
||||||
@ -950,13 +962,12 @@ struct chrono_formatter {
|
|||||||
}
|
}
|
||||||
auto time = tm();
|
auto time = tm();
|
||||||
time.tm_sec = to_nonnegative_int(second(), 60);
|
time.tm_sec = to_nonnegative_int(second(), 60);
|
||||||
format_localized(time, "%OS");
|
format_localized(time, 'S', 'O');
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_12_hour_time() {
|
void on_12_hour_time() {
|
||||||
if (handle_nan_inf()) return;
|
if (handle_nan_inf()) return;
|
||||||
|
format_localized(time(), 'r');
|
||||||
format_localized(time(), "%r");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_24_hour_time() {
|
void on_24_hour_time() {
|
||||||
@ -980,16 +991,18 @@ struct chrono_formatter {
|
|||||||
|
|
||||||
void on_am_pm() {
|
void on_am_pm() {
|
||||||
if (handle_nan_inf()) return;
|
if (handle_nan_inf()) return;
|
||||||
format_localized(time(), "%p");
|
format_localized(time(), 'p');
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_duration_value() {
|
void on_duration_value() {
|
||||||
if (handle_nan_inf()) return;
|
if (handle_nan_inf()) return;
|
||||||
write_sign();
|
write_sign();
|
||||||
out = format_chrono_duration_value(out, val, precision);
|
out = format_duration_value<char_type>(out, val, precision);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_duration_unit() { out = format_chrono_duration_unit<Period>(out); }
|
void on_duration_unit() {
|
||||||
|
out = format_duration_unit<char_type, Period>(out);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
@ -1024,7 +1037,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
|
void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
|
||||||
void on_fill(Char fill) { f.specs.fill[0] = fill; }
|
void on_fill(basic_string_view<Char> fill) { f.specs.fill = fill; }
|
||||||
void on_align(align_t align) { f.specs.align = align; }
|
void on_align(align_t align) { f.specs.align = align; }
|
||||||
void on_width(int width) { f.specs.width = width; }
|
void on_width(int width) { f.specs.width = width; }
|
||||||
void on_precision(int _precision) { f.precision = _precision; }
|
void on_precision(int _precision) { f.precision = _precision; }
|
||||||
@ -1088,8 +1101,8 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
|||||||
internal::handle_dynamic_spec<internal::precision_checker>(
|
internal::handle_dynamic_spec<internal::precision_checker>(
|
||||||
precision, precision_ref, ctx);
|
precision, precision_ref, ctx);
|
||||||
if (begin == end || *begin == '}') {
|
if (begin == end || *begin == '}') {
|
||||||
out = internal::format_chrono_duration_value(out, d.count(), precision);
|
out = internal::format_duration_value<Char>(out, d.count(), precision);
|
||||||
internal::format_chrono_duration_unit<Period>(out);
|
internal::format_duration_unit<Char, Period>(out);
|
||||||
} else {
|
} else {
|
||||||
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
|
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
|
||||||
ctx, out, d);
|
ctx, out, d);
|
||||||
|
@ -412,7 +412,7 @@ template <typename Char> struct ansi_color_escape {
|
|||||||
|
|
||||||
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
|
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
|
||||||
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
|
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
|
||||||
return buffer + std::strlen(buffer);
|
return buffer + std::char_traits<Char>::length(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -491,10 +491,8 @@ void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
|
|||||||
internal::make_background_color<Char>(ts.get_background());
|
internal::make_background_color<Char>(ts.get_background());
|
||||||
buf.append(background.begin(), background.end());
|
buf.append(background.begin(), background.end());
|
||||||
}
|
}
|
||||||
vformat_to(buf, format_str, args);
|
internal::vformat_to(buf, format_str, args);
|
||||||
if (has_style) {
|
if (has_style) internal::reset_color<Char>(buf);
|
||||||
internal::reset_color<Char>(buf);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
@ -540,7 +538,7 @@ void print(const text_style& ts, const S& format_str, const Args&... args) {
|
|||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline std::basic_string<Char> vformat(
|
inline std::basic_string<Char> vformat(
|
||||||
const text_style& ts, const S& format_str,
|
const text_style& ts, const S& format_str,
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buf;
|
basic_memory_buffer<Char> buf;
|
||||||
internal::vformat_to(buf, ts, to_string_view(format_str), args);
|
internal::vformat_to(buf, ts, to_string_view(format_str), args);
|
||||||
return fmt::to_string(buf);
|
return fmt::to_string(buf);
|
||||||
@ -562,7 +560,7 @@ template <typename S, typename... Args, typename Char = char_t<S>>
|
|||||||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return vformat(ts, to_string_view(format_str),
|
return vformat(ts, to_string_view(format_str),
|
||||||
{internal::make_args_checked<Args...>(format_str, args...)});
|
internal::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#define FMT_COMPILE_H_
|
#define FMT_COMPILE_H_
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
@ -350,6 +351,8 @@ template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
|||||||
template <int N, typename T>
|
template <int N, typename T>
|
||||||
using get_type = typename get_type_impl<N, T>::type;
|
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 {
|
template <typename Char> struct text {
|
||||||
basic_string_view<Char> data;
|
basic_string_view<Char> data;
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
@ -361,6 +364,9 @@ template <typename Char> struct text {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct is_compiled_format<text<Char>> : std::true_type {};
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
||||||
size_t size) {
|
size_t size) {
|
||||||
@ -406,6 +412,9 @@ template <typename Char, typename T, int N> struct field {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename T, int N>
|
||||||
|
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
||||||
|
|
||||||
template <typename L, typename R> struct concat {
|
template <typename L, typename R> struct concat {
|
||||||
L lhs;
|
L lhs;
|
||||||
R rhs;
|
R rhs;
|
||||||
@ -418,6 +427,9 @@ template <typename L, typename R> struct concat {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
struct is_compiled_format<concat<L, R>> : std::true_type {};
|
||||||
|
|
||||||
template <typename L, typename R>
|
template <typename L, typename R>
|
||||||
constexpr concat<L, R> make_concat(L lhs, R rhs) {
|
constexpr concat<L, R> make_concat(L lhs, R rhs) {
|
||||||
return {lhs, rhs};
|
return {lhs, rhs};
|
||||||
@ -508,8 +520,7 @@ constexpr auto compile(S format_str) {
|
|||||||
|
|
||||||
template <typename CompiledFormat, typename... Args,
|
template <typename CompiledFormat, typename... Args,
|
||||||
typename Char = typename CompiledFormat::char_type,
|
typename Char = typename CompiledFormat::char_type,
|
||||||
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
|
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
|
||||||
CompiledFormat>::value)>
|
|
||||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
cf.format(std::back_inserter(buffer), args...);
|
cf.format(std::back_inserter(buffer), args...);
|
||||||
@ -517,8 +528,7 @@ std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||||
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
|
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
|
||||||
CompiledFormat>::value)>
|
|
||||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return cf.format(out, args...);
|
return cf.format(out, args...);
|
||||||
@ -549,7 +559,7 @@ std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
|||||||
using range = buffer_range<Char>;
|
using range = buffer_range<Char>;
|
||||||
using context = buffer_context<Char>;
|
using context = buffer_context<Char>;
|
||||||
internal::cf::vformat_to<context>(range(buffer), cf,
|
internal::cf::vformat_to<context>(range(buffer), cf,
|
||||||
{make_format_args<context>(args...)});
|
make_format_args<context>(args...));
|
||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,8 +571,8 @@ OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
|||||||
using char_type = typename CompiledFormat::char_type;
|
using char_type = typename CompiledFormat::char_type;
|
||||||
using range = internal::output_range<OutputIt, char_type>;
|
using range = internal::output_range<OutputIt, char_type>;
|
||||||
using context = format_context_t<OutputIt, char_type>;
|
using context = format_context_t<OutputIt, char_type>;
|
||||||
return internal::cf::vformat_to<context>(
|
return internal::cf::vformat_to<context>(range(out), cf,
|
||||||
range(out), cf, {make_format_args<context>(args...)});
|
make_format_args<context>(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||||
|
@ -10,12 +10,15 @@
|
|||||||
|
|
||||||
#include <cstdio> // std::FILE
|
#include <cstdio> // std::FILE
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <functional>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
||||||
#define FMT_VERSION 60102
|
#define FMT_VERSION 60200
|
||||||
|
|
||||||
#ifdef __has_feature
|
#ifdef __has_feature
|
||||||
# define FMT_HAS_FEATURE(x) __has_feature(x)
|
# define FMT_HAS_FEATURE(x) __has_feature(x)
|
||||||
@ -36,6 +39,18 @@
|
|||||||
# define FMT_HAS_CPP_ATTRIBUTE(x) 0
|
# define FMT_HAS_CPP_ATTRIBUTE(x) 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
|
||||||
|
(__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
|
||||||
|
|
||||||
|
#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
|
||||||
|
(__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
|
||||||
|
#else
|
||||||
|
# define FMT_CLANG_VERSION 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(__GNUC__) && !defined(__clang__)
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||||
#else
|
#else
|
||||||
@ -117,16 +132,25 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// [[noreturn]] is disabled on MSVC because of bogus unreachable code warnings.
|
// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code
|
||||||
#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER
|
// warnings.
|
||||||
|
#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \
|
||||||
|
!FMT_NVCC
|
||||||
# define FMT_NORETURN [[noreturn]]
|
# define FMT_NORETURN [[noreturn]]
|
||||||
#else
|
#else
|
||||||
# define FMT_NORETURN
|
# define FMT_NORETURN
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef FMT_MAYBE_UNUSED
|
||||||
|
# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
|
||||||
|
# define FMT_MAYBE_UNUSED [[maybe_unused]]
|
||||||
|
# else
|
||||||
|
# define FMT_MAYBE_UNUSED
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_DEPRECATED
|
#ifndef FMT_DEPRECATED
|
||||||
# if (FMT_HAS_CPP_ATTRIBUTE(deprecated) && __cplusplus >= 201402L) || \
|
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
|
||||||
FMT_MSC_VER >= 1900
|
|
||||||
# define FMT_DEPRECATED [[deprecated]]
|
# define FMT_DEPRECATED [[deprecated]]
|
||||||
# else
|
# else
|
||||||
# if defined(__GNUC__) || defined(__clang__)
|
# if defined(__GNUC__) || defined(__clang__)
|
||||||
@ -139,8 +163,8 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Workaround broken [[deprecated]] in the Intel compiler and NVCC.
|
// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers.
|
||||||
#if defined(__INTEL_COMPILER) || FMT_NVCC
|
#if defined(__INTEL_COMPILER) || defined(__PGI) || FMT_NVCC
|
||||||
# define FMT_DEPRECATED_ALIAS
|
# define FMT_DEPRECATED_ALIAS
|
||||||
#else
|
#else
|
||||||
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
|
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
|
||||||
@ -166,6 +190,12 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
|
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
|
||||||
|
# if FMT_MSC_VER
|
||||||
|
# define FMT_NO_W4275 __pragma(warning(suppress : 4275))
|
||||||
|
# else
|
||||||
|
# define FMT_NO_W4275
|
||||||
|
# endif
|
||||||
|
# define FMT_CLASS_API FMT_NO_W4275
|
||||||
# ifdef FMT_EXPORT
|
# ifdef FMT_EXPORT
|
||||||
# define FMT_API __declspec(dllexport)
|
# define FMT_API __declspec(dllexport)
|
||||||
# elif defined(FMT_SHARED)
|
# elif defined(FMT_SHARED)
|
||||||
@ -173,12 +203,24 @@
|
|||||||
# define FMT_EXTERN_TEMPLATE_API FMT_API
|
# define FMT_EXTERN_TEMPLATE_API FMT_API
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef FMT_CLASS_API
|
||||||
|
# define FMT_CLASS_API
|
||||||
|
#endif
|
||||||
#ifndef FMT_API
|
#ifndef FMT_API
|
||||||
# define FMT_API
|
# if FMT_GCC_VERSION || FMT_CLANG_VERSION
|
||||||
|
# define FMT_API __attribute__((visibility("default")))
|
||||||
|
# define FMT_EXTERN_TEMPLATE_API FMT_API
|
||||||
|
# define FMT_INSTANTIATION_DEF_API
|
||||||
|
# else
|
||||||
|
# define FMT_API
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
#ifndef FMT_EXTERN_TEMPLATE_API
|
#ifndef FMT_EXTERN_TEMPLATE_API
|
||||||
# define FMT_EXTERN_TEMPLATE_API
|
# define FMT_EXTERN_TEMPLATE_API
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef FMT_INSTANTIATION_DEF_API
|
||||||
|
# define FMT_INSTANTIATION_DEF_API FMT_API
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_HEADER_ONLY
|
#ifndef FMT_HEADER_ONLY
|
||||||
# define FMT_EXTERN extern
|
# define FMT_EXTERN extern
|
||||||
@ -197,9 +239,16 @@
|
|||||||
# define FMT_USE_EXPERIMENTAL_STRING_VIEW
|
# define FMT_USE_EXPERIMENTAL_STRING_VIEW
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef FMT_UNICODE
|
||||||
|
# define FMT_UNICODE !FMT_MSC_VER
|
||||||
|
#endif
|
||||||
|
#if FMT_UNICODE && FMT_MSC_VER
|
||||||
|
# pragma execution_character_set("utf-8")
|
||||||
|
#endif
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
// Implementations of enable_if_t and other types for pre-C++14 systems.
|
// Implementations of enable_if_t and other metafunctions for older systems.
|
||||||
template <bool B, class T = void>
|
template <bool B, class T = void>
|
||||||
using enable_if_t = typename std::enable_if<B, T>::type;
|
using enable_if_t = typename std::enable_if<B, T>::type;
|
||||||
template <bool B, class T, class F>
|
template <bool B, class T, class F>
|
||||||
@ -211,6 +260,8 @@ template <typename T>
|
|||||||
using remove_const_t = typename std::remove_const<T>::type;
|
using remove_const_t = typename std::remove_const<T>::type;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
|
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
|
||||||
|
template <typename T> struct type_identity { using type = T; };
|
||||||
|
template <typename T> using type_identity_t = typename type_identity<T>::type;
|
||||||
|
|
||||||
struct monostate {};
|
struct monostate {};
|
||||||
|
|
||||||
@ -221,19 +272,25 @@ struct monostate {};
|
|||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
// A helper function to suppress bogus "conditional expression is constant"
|
||||||
|
// warnings.
|
||||||
|
template <typename T> FMT_CONSTEXPR T const_check(T value) { return value; }
|
||||||
|
|
||||||
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
|
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
|
||||||
template <typename... Ts> struct void_t_impl { using type = void; };
|
template <typename... Ts> struct void_t_impl { using type = void; };
|
||||||
|
|
||||||
FMT_API void assert_fail(const char* file, int line, const char* message);
|
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
|
||||||
|
const char* message);
|
||||||
|
|
||||||
#ifndef FMT_ASSERT
|
#ifndef FMT_ASSERT
|
||||||
# ifdef NDEBUG
|
# ifdef NDEBUG
|
||||||
# define FMT_ASSERT(condition, message)
|
// FMT_ASSERT is not empty to avoid -Werror=empty-body.
|
||||||
|
# define FMT_ASSERT(condition, message) ((void)0)
|
||||||
# else
|
# else
|
||||||
# define FMT_ASSERT(condition, message) \
|
# define FMT_ASSERT(condition, message) \
|
||||||
((condition) \
|
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
|
||||||
? void() \
|
? (void)0 \
|
||||||
: fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
|
: ::fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -248,7 +305,7 @@ template <typename T> struct std_string_view {};
|
|||||||
|
|
||||||
#ifdef FMT_USE_INT128
|
#ifdef FMT_USE_INT128
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
#elif defined(__SIZEOF_INT128__)
|
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC
|
||||||
# define FMT_USE_INT128 1
|
# define FMT_USE_INT128 1
|
||||||
using int128_t = __int128_t;
|
using int128_t = __int128_t;
|
||||||
using uint128_t = __uint128_t;
|
using uint128_t = __uint128_t;
|
||||||
@ -266,6 +323,19 @@ FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
|
|||||||
FMT_ASSERT(value >= 0, "negative value");
|
FMT_ASSERT(value >= 0, "negative value");
|
||||||
return static_cast<typename std::make_unsigned<Int>::type>(value);
|
return static_cast<typename std::make_unsigned<Int>::type>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr unsigned char micro[] = "\u00B5";
|
||||||
|
|
||||||
|
template <typename Char> constexpr bool is_unicode() {
|
||||||
|
return FMT_UNICODE || sizeof(Char) != 1 ||
|
||||||
|
(sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cpp_char8_t
|
||||||
|
using char8_type = char8_t;
|
||||||
|
#else
|
||||||
|
enum char8_type : unsigned char {};
|
||||||
|
#endif
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
template <typename... Ts>
|
template <typename... Ts>
|
||||||
@ -284,7 +354,8 @@ template <typename Char> class basic_string_view {
|
|||||||
size_t size_;
|
size_t size_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using char_type = Char;
|
using char_type FMT_DEPRECATED_ALIAS = Char;
|
||||||
|
using value_type = Char;
|
||||||
using iterator = const Char*;
|
using iterator = const Char*;
|
||||||
|
|
||||||
FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
|
FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
|
||||||
@ -300,6 +371,9 @@ template <typename Char> class basic_string_view {
|
|||||||
the size with ``std::char_traits<Char>::length``.
|
the size with ``std::char_traits<Char>::length``.
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
|
#if __cplusplus >= 201703L // C++17's char_traits::length() is constexpr.
|
||||||
|
FMT_CONSTEXPR
|
||||||
|
#endif
|
||||||
basic_string_view(const Char* s)
|
basic_string_view(const Char* s)
|
||||||
: data_(s), size_(std::char_traits<Char>::length(s)) {}
|
: data_(s), size_(std::char_traits<Char>::length(s)) {}
|
||||||
|
|
||||||
@ -365,15 +439,15 @@ using string_view = basic_string_view<char>;
|
|||||||
using wstring_view = basic_string_view<wchar_t>;
|
using wstring_view = basic_string_view<wchar_t>;
|
||||||
|
|
||||||
#ifndef __cpp_char8_t
|
#ifndef __cpp_char8_t
|
||||||
// A UTF-8 code unit type.
|
// char8_t is deprecated; use char instead.
|
||||||
enum char8_t : unsigned char {};
|
using char8_t FMT_DEPRECATED_ALIAS = internal::char8_type;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Specifies if ``T`` is a character type. Can be specialized by users. */
|
/** Specifies if ``T`` is a character type. Can be specialized by users. */
|
||||||
template <typename T> struct is_char : std::false_type {};
|
template <typename T> struct is_char : std::false_type {};
|
||||||
template <> struct is_char<char> : std::true_type {};
|
template <> struct is_char<char> : std::true_type {};
|
||||||
template <> struct is_char<wchar_t> : std::true_type {};
|
template <> struct is_char<wchar_t> : std::true_type {};
|
||||||
template <> struct is_char<char8_t> : std::true_type {};
|
template <> struct is_char<internal::char8_type> : std::true_type {};
|
||||||
template <> struct is_char<char16_t> : std::true_type {};
|
template <> struct is_char<char16_t> : std::true_type {};
|
||||||
template <> struct is_char<char32_t> : std::true_type {};
|
template <> struct is_char<char32_t> : std::true_type {};
|
||||||
|
|
||||||
@ -442,7 +516,7 @@ struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))> {
|
|||||||
template <typename S, typename = void> struct char_t_impl {};
|
template <typename S, typename = void> struct char_t_impl {};
|
||||||
template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
|
template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
|
||||||
using result = decltype(to_string_view(std::declval<S>()));
|
using result = decltype(to_string_view(std::declval<S>()));
|
||||||
using type = typename result::char_type;
|
using type = typename result::value_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct error_handler {
|
struct error_handler {
|
||||||
@ -603,6 +677,9 @@ template <typename T> class buffer {
|
|||||||
T* begin() FMT_NOEXCEPT { return ptr_; }
|
T* begin() FMT_NOEXCEPT { return ptr_; }
|
||||||
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
|
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
|
||||||
|
|
||||||
|
const T* begin() const FMT_NOEXCEPT { return ptr_; }
|
||||||
|
const T* end() const FMT_NOEXCEPT { return ptr_ + size_; }
|
||||||
|
|
||||||
/** Returns the size of this buffer. */
|
/** Returns the size of this buffer. */
|
||||||
std::size_t size() const FMT_NOEXCEPT { return size_; }
|
std::size_t size() const FMT_NOEXCEPT { return size_; }
|
||||||
|
|
||||||
@ -639,8 +716,10 @@ template <typename T> class buffer {
|
|||||||
/** Appends data to the end of the buffer. */
|
/** Appends data to the end of the buffer. */
|
||||||
template <typename U> void append(const U* begin, const U* end);
|
template <typename U> void append(const U* begin, const U* end);
|
||||||
|
|
||||||
T& operator[](std::size_t index) { return ptr_[index]; }
|
template <typename I> T& operator[](I index) { return ptr_[index]; }
|
||||||
const T& operator[](std::size_t index) const { return ptr_[index]; }
|
template <typename I> const T& operator[](I index) const {
|
||||||
|
return ptr_[index];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// A container-backed buffer.
|
// A container-backed buffer.
|
||||||
@ -684,7 +763,7 @@ using has_fallback_formatter =
|
|||||||
template <typename Char> struct named_arg_base;
|
template <typename Char> struct named_arg_base;
|
||||||
template <typename T, typename Char> struct named_arg;
|
template <typename T, typename Char> struct named_arg;
|
||||||
|
|
||||||
enum type {
|
enum class type {
|
||||||
none_type,
|
none_type,
|
||||||
named_arg_type,
|
named_arg_type,
|
||||||
// Integer types should go first,
|
// Integer types should go first,
|
||||||
@ -710,11 +789,12 @@ enum type {
|
|||||||
|
|
||||||
// Maps core type T to the corresponding type enum constant.
|
// Maps core type T to the corresponding type enum constant.
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct type_constant : std::integral_constant<type, custom_type> {};
|
struct type_constant : std::integral_constant<type, type::custom_type> {};
|
||||||
|
|
||||||
#define FMT_TYPE_CONSTANT(Type, constant) \
|
#define FMT_TYPE_CONSTANT(Type, constant) \
|
||||||
template <typename Char> \
|
template <typename Char> \
|
||||||
struct type_constant<Type, Char> : std::integral_constant<type, constant> {}
|
struct type_constant<Type, Char> \
|
||||||
|
: std::integral_constant<type, type::constant> {}
|
||||||
|
|
||||||
FMT_TYPE_CONSTANT(const named_arg_base<Char>&, named_arg_type);
|
FMT_TYPE_CONSTANT(const named_arg_base<Char>&, named_arg_type);
|
||||||
FMT_TYPE_CONSTANT(int, int_type);
|
FMT_TYPE_CONSTANT(int, int_type);
|
||||||
@ -733,13 +813,13 @@ FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
|
|||||||
FMT_TYPE_CONSTANT(const void*, pointer_type);
|
FMT_TYPE_CONSTANT(const void*, pointer_type);
|
||||||
|
|
||||||
FMT_CONSTEXPR bool is_integral_type(type t) {
|
FMT_CONSTEXPR bool is_integral_type(type t) {
|
||||||
FMT_ASSERT(t != named_arg_type, "invalid argument type");
|
FMT_ASSERT(t != type::named_arg_type, "invalid argument type");
|
||||||
return t > none_type && t <= last_integer_type;
|
return t > type::none_type && t <= type::last_integer_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR bool is_arithmetic_type(type t) {
|
FMT_CONSTEXPR bool is_arithmetic_type(type t) {
|
||||||
FMT_ASSERT(t != named_arg_type, "invalid argument type");
|
FMT_ASSERT(t != type::named_arg_type, "invalid argument type");
|
||||||
return t > none_type && t <= last_numeric_type;
|
return t > type::none_type && t <= type::last_numeric_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char> struct string_value {
|
template <typename Char> struct string_value {
|
||||||
@ -869,7 +949,8 @@ template <typename Context> struct arg_mapper {
|
|||||||
template <typename T,
|
template <typename T,
|
||||||
FMT_ENABLE_IF(
|
FMT_ENABLE_IF(
|
||||||
std::is_constructible<basic_string_view<char_type>, T>::value &&
|
std::is_constructible<basic_string_view<char_type>, T>::value &&
|
||||||
!is_string<T>::value)>
|
!is_string<T>::value && !has_formatter<T, Context>::value &&
|
||||||
|
!has_fallback_formatter<T, Context>::value)>
|
||||||
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
|
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
|
||||||
return basic_string_view<char_type>(val);
|
return basic_string_view<char_type>(val);
|
||||||
}
|
}
|
||||||
@ -878,7 +959,8 @@ template <typename Context> struct arg_mapper {
|
|||||||
FMT_ENABLE_IF(
|
FMT_ENABLE_IF(
|
||||||
std::is_constructible<std_string_view<char_type>, T>::value &&
|
std::is_constructible<std_string_view<char_type>, T>::value &&
|
||||||
!std::is_constructible<basic_string_view<char_type>, T>::value &&
|
!std::is_constructible<basic_string_view<char_type>, T>::value &&
|
||||||
!is_string<T>::value && !has_formatter<T, Context>::value)>
|
!is_string<T>::value && !has_formatter<T, Context>::value &&
|
||||||
|
!has_fallback_formatter<T, Context>::value)>
|
||||||
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
|
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
|
||||||
return std_string_view<char_type>(val);
|
return std_string_view<char_type>(val);
|
||||||
}
|
}
|
||||||
@ -907,18 +989,15 @@ template <typename Context> struct arg_mapper {
|
|||||||
FMT_ENABLE_IF(std::is_enum<T>::value &&
|
FMT_ENABLE_IF(std::is_enum<T>::value &&
|
||||||
!has_formatter<T, Context>::value &&
|
!has_formatter<T, Context>::value &&
|
||||||
!has_fallback_formatter<T, Context>::value)>
|
!has_fallback_formatter<T, Context>::value)>
|
||||||
FMT_CONSTEXPR auto map(const T& val) -> decltype(
|
FMT_CONSTEXPR auto map(const T& val)
|
||||||
map(static_cast<typename std::underlying_type<T>::type>(val))) {
|
-> decltype(std::declval<arg_mapper>().map(
|
||||||
|
static_cast<typename std::underlying_type<T>::type>(val))) {
|
||||||
return map(static_cast<typename std::underlying_type<T>::type>(val));
|
return map(static_cast<typename std::underlying_type<T>::type>(val));
|
||||||
}
|
}
|
||||||
template <
|
template <typename T,
|
||||||
typename T,
|
FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
|
||||||
FMT_ENABLE_IF(
|
(has_formatter<T, Context>::value ||
|
||||||
!is_string<T>::value && !is_char<T>::value &&
|
has_fallback_formatter<T, Context>::value))>
|
||||||
!std::is_constructible<basic_string_view<char_type>, T>::value &&
|
|
||||||
(has_formatter<T, Context>::value ||
|
|
||||||
(has_fallback_formatter<T, Context>::value &&
|
|
||||||
!std::is_constructible<std_string_view<char_type>, T>::value)))>
|
|
||||||
FMT_CONSTEXPR const T& map(const T& val) {
|
FMT_CONSTEXPR const T& map(const T& val) {
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
@ -930,6 +1009,16 @@ template <typename Context> struct arg_mapper {
|
|||||||
std::memcpy(val.data, &arg, sizeof(arg));
|
std::memcpy(val.data, &arg, sizeof(arg));
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int map(...) {
|
||||||
|
constexpr bool formattable = sizeof(Context) == 0;
|
||||||
|
static_assert(
|
||||||
|
formattable,
|
||||||
|
"Cannot format argument. To make type T formattable provide a "
|
||||||
|
"formatter<T> specialization: "
|
||||||
|
"https://fmt.dev/latest/api.html#formatting-user-defined-types");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// A type constant after applying arg_mapper<Context>.
|
// A type constant after applying arg_mapper<Context>.
|
||||||
@ -981,10 +1070,10 @@ template <typename Context> class basic_format_arg {
|
|||||||
internal::custom_value<Context> custom_;
|
internal::custom_value<Context> custom_;
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_CONSTEXPR basic_format_arg() : type_(internal::none_type) {}
|
FMT_CONSTEXPR basic_format_arg() : type_(internal::type::none_type) {}
|
||||||
|
|
||||||
FMT_CONSTEXPR explicit operator bool() const FMT_NOEXCEPT {
|
FMT_CONSTEXPR explicit operator bool() const FMT_NOEXCEPT {
|
||||||
return type_ != internal::none_type;
|
return type_ != internal::type::none_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal::type type() const { return type_; }
|
internal::type type() const { return type_; }
|
||||||
@ -1006,47 +1095,47 @@ FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
|
|||||||
-> decltype(vis(0)) {
|
-> decltype(vis(0)) {
|
||||||
using char_type = typename Context::char_type;
|
using char_type = typename Context::char_type;
|
||||||
switch (arg.type_) {
|
switch (arg.type_) {
|
||||||
case internal::none_type:
|
case internal::type::none_type:
|
||||||
break;
|
break;
|
||||||
case internal::named_arg_type:
|
case internal::type::named_arg_type:
|
||||||
FMT_ASSERT(false, "invalid argument type");
|
FMT_ASSERT(false, "invalid argument type");
|
||||||
break;
|
break;
|
||||||
case internal::int_type:
|
case internal::type::int_type:
|
||||||
return vis(arg.value_.int_value);
|
return vis(arg.value_.int_value);
|
||||||
case internal::uint_type:
|
case internal::type::uint_type:
|
||||||
return vis(arg.value_.uint_value);
|
return vis(arg.value_.uint_value);
|
||||||
case internal::long_long_type:
|
case internal::type::long_long_type:
|
||||||
return vis(arg.value_.long_long_value);
|
return vis(arg.value_.long_long_value);
|
||||||
case internal::ulong_long_type:
|
case internal::type::ulong_long_type:
|
||||||
return vis(arg.value_.ulong_long_value);
|
return vis(arg.value_.ulong_long_value);
|
||||||
#if FMT_USE_INT128
|
#if FMT_USE_INT128
|
||||||
case internal::int128_type:
|
case internal::type::int128_type:
|
||||||
return vis(arg.value_.int128_value);
|
return vis(arg.value_.int128_value);
|
||||||
case internal::uint128_type:
|
case internal::type::uint128_type:
|
||||||
return vis(arg.value_.uint128_value);
|
return vis(arg.value_.uint128_value);
|
||||||
#else
|
#else
|
||||||
case internal::int128_type:
|
case internal::type::int128_type:
|
||||||
case internal::uint128_type:
|
case internal::type::uint128_type:
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case internal::bool_type:
|
case internal::type::bool_type:
|
||||||
return vis(arg.value_.bool_value);
|
return vis(arg.value_.bool_value);
|
||||||
case internal::char_type:
|
case internal::type::char_type:
|
||||||
return vis(arg.value_.char_value);
|
return vis(arg.value_.char_value);
|
||||||
case internal::float_type:
|
case internal::type::float_type:
|
||||||
return vis(arg.value_.float_value);
|
return vis(arg.value_.float_value);
|
||||||
case internal::double_type:
|
case internal::type::double_type:
|
||||||
return vis(arg.value_.double_value);
|
return vis(arg.value_.double_value);
|
||||||
case internal::long_double_type:
|
case internal::type::long_double_type:
|
||||||
return vis(arg.value_.long_double_value);
|
return vis(arg.value_.long_double_value);
|
||||||
case internal::cstring_type:
|
case internal::type::cstring_type:
|
||||||
return vis(arg.value_.string.data);
|
return vis(arg.value_.string.data);
|
||||||
case internal::string_type:
|
case internal::type::string_type:
|
||||||
return vis(basic_string_view<char_type>(arg.value_.string.data,
|
return vis(basic_string_view<char_type>(arg.value_.string.data,
|
||||||
arg.value_.string.size));
|
arg.value_.string.size));
|
||||||
case internal::pointer_type:
|
case internal::type::pointer_type:
|
||||||
return vis(arg.value_.pointer);
|
return vis(arg.value_.pointer);
|
||||||
case internal::custom_type:
|
case internal::type::custom_type:
|
||||||
return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
|
return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
|
||||||
}
|
}
|
||||||
return vis(monostate());
|
return vis(monostate());
|
||||||
@ -1106,7 +1195,7 @@ template <typename> constexpr unsigned long long encode_types() { return 0; }
|
|||||||
|
|
||||||
template <typename Context, typename Arg, typename... Args>
|
template <typename Context, typename Arg, typename... Args>
|
||||||
constexpr unsigned long long encode_types() {
|
constexpr unsigned long long encode_types() {
|
||||||
return mapped_type_constant<Arg, Context>::value |
|
return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
|
||||||
(encode_types<Context, Args...>() << packed_arg_bits);
|
(encode_types<Context, Args...>() << packed_arg_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1129,6 +1218,43 @@ template <bool IS_PACKED, typename Context, typename T,
|
|||||||
inline basic_format_arg<Context> make_arg(const T& value) {
|
inline basic_format_arg<Context> make_arg(const T& value) {
|
||||||
return make_arg<Context>(value);
|
return make_arg<Context>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T> struct is_reference_wrapper : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
|
||||||
|
|
||||||
|
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 node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
|
||||||
|
auto& value = node->value;
|
||||||
|
node->next = std::move(head_);
|
||||||
|
head_ = std::move(node);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
// Formatting context.
|
// Formatting context.
|
||||||
@ -1191,7 +1317,13 @@ using wformat_context = buffer_context<wchar_t>;
|
|||||||
such as `~fmt::vformat`.
|
such as `~fmt::vformat`.
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename Context, typename... Args> class format_arg_store {
|
template <typename Context, typename... Args>
|
||||||
|
class 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:
|
private:
|
||||||
static const size_t num_args = sizeof...(Args);
|
static const size_t num_args = sizeof...(Args);
|
||||||
static const bool is_packed = num_args < internal::max_packed_args;
|
static const bool is_packed = num_args < internal::max_packed_args;
|
||||||
@ -1210,7 +1342,12 @@ template <typename Context, typename... Args> class format_arg_store {
|
|||||||
: internal::is_unpacked_bit | num_args;
|
: internal::is_unpacked_bit | num_args;
|
||||||
|
|
||||||
format_arg_store(const Args&... args)
|
format_arg_store(const Args&... args)
|
||||||
: data_{internal::make_arg<is_packed, Context>(args)...} {}
|
:
|
||||||
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||||
|
basic_format_args<Context>(*this),
|
||||||
|
#endif
|
||||||
|
data_{internal::make_arg<is_packed, Context>(args)...} {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1227,7 +1364,112 @@ inline format_arg_store<Context, Args...> make_format_args(
|
|||||||
return {args...};
|
return {args...};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Formatting arguments. */
|
/**
|
||||||
|
\rst
|
||||||
|
A dynamic version of `fmt::format_arg_store<>`.
|
||||||
|
It's equipped with a storage to potentially temporary objects which lifetime
|
||||||
|
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 internal::type mapped_type =
|
||||||
|
internal::mapped_type_constant<T, Context>::value;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
value = !(internal::is_reference_wrapper<T>::value ||
|
||||||
|
std::is_same<T, basic_string_view<char_type>>::value ||
|
||||||
|
std::is_same<T, internal::std_string_view<char_type>>::value ||
|
||||||
|
(mapped_type != internal::type::cstring_type &&
|
||||||
|
mapped_type != internal::type::string_type &&
|
||||||
|
mapped_type != internal::type::custom_type &&
|
||||||
|
mapped_type != internal::type::named_arg_type))
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using stored_type = conditional_t<internal::is_string<T>::value,
|
||||||
|
std::basic_string<char_type>, T>;
|
||||||
|
|
||||||
|
// Storage of basic_format_arg must be contiguous.
|
||||||
|
std::vector<basic_format_arg<Context>> data_;
|
||||||
|
|
||||||
|
// Storage of arguments not fitting into basic_format_arg must grow
|
||||||
|
// without relocation because items in data_ refer to it.
|
||||||
|
internal::dynamic_arg_list dynamic_args_;
|
||||||
|
|
||||||
|
friend class basic_format_args<Context>;
|
||||||
|
|
||||||
|
unsigned long long get_types() const {
|
||||||
|
return internal::is_unpacked_bit | data_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> void emplace_arg(const T& arg) {
|
||||||
|
data_.emplace_back(internal::make_arg<Context>(arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Adds an argument into the dynamic store for later passing to a formating
|
||||||
|
function.
|
||||||
|
|
||||||
|
Note that custom types and string types (but not string views!) are copied
|
||||||
|
into the store with dynamic memory (in addition to resizing vector).
|
||||||
|
|
||||||
|
**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) {
|
||||||
|
static_assert(
|
||||||
|
!std::is_base_of<internal::named_arg_base<char_type>, T>::value,
|
||||||
|
"named arguments are not supported yet");
|
||||||
|
if (internal::const_check(need_copy<T>::value))
|
||||||
|
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
|
||||||
|
else
|
||||||
|
emplace_arg(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Adds a reference to the argument into the dynamic store for later passing to
|
||||||
|
a formating function.
|
||||||
|
*/
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
A view of a collection of formatting arguments. To avoid lifetime issues it
|
||||||
|
should only be used as a parameter type in type-erased functions such as
|
||||||
|
``vformat``::
|
||||||
|
|
||||||
|
void vlog(string_view format_str, format_args args); // OK
|
||||||
|
format_args args = make_format_args(42); // Error: dangling reference
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
template <typename Context> class basic_format_args {
|
template <typename Context> class basic_format_args {
|
||||||
public:
|
public:
|
||||||
using size_type = int;
|
using size_type = int;
|
||||||
@ -1269,7 +1511,7 @@ template <typename Context> class basic_format_args {
|
|||||||
}
|
}
|
||||||
if (index > internal::max_packed_args) return arg;
|
if (index > internal::max_packed_args) return arg;
|
||||||
arg.type_ = type(index);
|
arg.type_ = type(index);
|
||||||
if (arg.type_ == internal::none_type) return arg;
|
if (arg.type_ == internal::type::none_type) return arg;
|
||||||
internal::value<Context>& val = arg.value_;
|
internal::value<Context>& val = arg.value_;
|
||||||
val = values_[index];
|
val = values_[index];
|
||||||
return arg;
|
return arg;
|
||||||
@ -1289,6 +1531,17 @@ template <typename Context> class basic_format_args {
|
|||||||
set_data(store.data_);
|
set_data(store.data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs a `basic_format_args` object from
|
||||||
|
`~fmt::dynamic_format_arg_store`.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
basic_format_args(const dynamic_format_arg_store<Context>& store)
|
||||||
|
: types_(store.get_types()) {
|
||||||
|
set_data(store.data_.data());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Constructs a `basic_format_args` object from a dynamic set of arguments.
|
Constructs a `basic_format_args` object from a dynamic set of arguments.
|
||||||
@ -1302,7 +1555,7 @@ template <typename Context> class basic_format_args {
|
|||||||
/** Returns the argument at specified index. */
|
/** Returns the argument at specified index. */
|
||||||
format_arg get(int index) const {
|
format_arg get(int index) const {
|
||||||
format_arg arg = do_get(index);
|
format_arg arg = do_get(index);
|
||||||
if (arg.type_ == internal::named_arg_type)
|
if (arg.type_ == internal::type::named_arg_type)
|
||||||
arg = arg.value_.named_arg->template deserialize<Context>();
|
arg = arg.value_.named_arg->template deserialize<Context>();
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
@ -1319,12 +1572,12 @@ template <typename Context> class basic_format_args {
|
|||||||
struct format_args : basic_format_args<format_context> {
|
struct format_args : basic_format_args<format_context> {
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
format_args(Args&&... args)
|
format_args(Args&&... args)
|
||||||
: basic_format_args<format_context>(std::forward<Args>(args)...) {}
|
: basic_format_args<format_context>(static_cast<Args&&>(args)...) {}
|
||||||
};
|
};
|
||||||
struct wformat_args : basic_format_args<wformat_context> {
|
struct wformat_args : basic_format_args<wformat_context> {
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
wformat_args(Args&&... args)
|
wformat_args(Args&&... args)
|
||||||
: basic_format_args<wformat_context>(std::forward<Args>(args)...) {}
|
: basic_format_args<wformat_context>(static_cast<Args&&>(args)...) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Container> struct is_contiguous : std::false_type {};
|
template <typename Container> struct is_contiguous : std::false_type {};
|
||||||
@ -1358,7 +1611,10 @@ template <typename Char> struct named_arg_base {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename Char> struct named_arg : named_arg_base<Char> {
|
struct view {};
|
||||||
|
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct named_arg : view, named_arg_base<Char> {
|
||||||
const T& value;
|
const T& value;
|
||||||
|
|
||||||
named_arg(basic_string_view<Char> name, const T& val)
|
named_arg(basic_string_view<Char> name, const T& val)
|
||||||
@ -1376,7 +1632,6 @@ inline void check_format_string(const S&) {
|
|||||||
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
|
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||||
void check_format_string(S);
|
void check_format_string(S);
|
||||||
|
|
||||||
struct view {};
|
|
||||||
template <bool...> struct bool_pack;
|
template <bool...> struct bool_pack;
|
||||||
template <bool... Args>
|
template <bool... Args>
|
||||||
using all_true =
|
using all_true =
|
||||||
@ -1386,32 +1641,38 @@ template <typename... Args, typename S, typename Char = char_t<S>>
|
|||||||
inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
|
inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
|
||||||
make_args_checked(const S& format_str,
|
make_args_checked(const S& format_str,
|
||||||
const remove_reference_t<Args>&... args) {
|
const remove_reference_t<Args>&... args) {
|
||||||
static_assert(all_true<(!std::is_base_of<view, remove_reference_t<Args>>() ||
|
static_assert(
|
||||||
!std::is_reference<Args>())...>::value,
|
all_true<(!std::is_base_of<view, remove_reference_t<Args>>::value ||
|
||||||
"passing views as lvalues is disallowed");
|
!std::is_reference<Args>::value)...>::value,
|
||||||
check_format_string<remove_const_t<remove_reference_t<Args>>...>(format_str);
|
"passing views as lvalues is disallowed");
|
||||||
|
check_format_string<Args...>(format_str);
|
||||||
return {args...};
|
return {args...};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
std::basic_string<Char> vformat(basic_string_view<Char> format_str,
|
std::basic_string<Char> vformat(
|
||||||
basic_format_args<buffer_context<Char>> args);
|
basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args);
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
typename buffer_context<Char>::iterator vformat_to(
|
typename buffer_context<Char>::iterator vformat_to(
|
||||||
buffer<Char>& buf, basic_string_view<Char> format_str,
|
buffer<Char>& buf, basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<Char>> args);
|
basic_format_args<buffer_context<type_identity_t<Char>>> args);
|
||||||
|
|
||||||
|
template <typename Char, typename Args,
|
||||||
|
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||||
|
inline void vprint_mojibake(std::FILE*, basic_string_view<Char>, const Args&) {}
|
||||||
|
|
||||||
|
FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
|
||||||
|
#ifndef _WIN32
|
||||||
|
inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
|
||||||
|
#endif
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Returns a named argument to be used in a formatting function.
|
Returns a named argument to be used in a formatting function. It should only
|
||||||
|
be used in a call to a formatting function.
|
||||||
The named argument holds a reference and does not extend the lifetime
|
|
||||||
of its arguments.
|
|
||||||
Consequently, a dangling reference can accidentally be created.
|
|
||||||
The user should take care to only pass this function temporaries when
|
|
||||||
the named argument is itself a temporary, as per the following example.
|
|
||||||
|
|
||||||
**Example**::
|
**Example**::
|
||||||
|
|
||||||
@ -1434,8 +1695,9 @@ void arg(S, internal::named_arg<T, Char>) = delete;
|
|||||||
template <typename OutputIt, typename S, typename Char = char_t<S>,
|
template <typename OutputIt, typename S, typename Char = char_t<S>,
|
||||||
FMT_ENABLE_IF(
|
FMT_ENABLE_IF(
|
||||||
internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
|
internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
|
||||||
OutputIt vformat_to(OutputIt out, const S& format_str,
|
OutputIt vformat_to(
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
OutputIt out, const S& format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
using container = remove_reference_t<decltype(internal::get_container(out))>;
|
using container = remove_reference_t<decltype(internal::get_container(out))>;
|
||||||
internal::container_buffer<container> buf((internal::get_container(out)));
|
internal::container_buffer<container> buf((internal::get_container(out)));
|
||||||
internal::vformat_to(buf, to_string_view(format_str), args);
|
internal::vformat_to(buf, to_string_view(format_str), args);
|
||||||
@ -1448,14 +1710,14 @@ template <typename Container, typename S, typename... Args,
|
|||||||
inline std::back_insert_iterator<Container> format_to(
|
inline std::back_insert_iterator<Container> format_to(
|
||||||
std::back_insert_iterator<Container> out, const S& format_str,
|
std::back_insert_iterator<Container> out, const S& format_str,
|
||||||
Args&&... args) {
|
Args&&... args) {
|
||||||
return vformat_to(
|
return vformat_to(out, to_string_view(format_str),
|
||||||
out, to_string_view(format_str),
|
internal::make_args_checked<Args...>(format_str, args...));
|
||||||
{internal::make_args_checked<Args...>(format_str, args...)});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline std::basic_string<Char> vformat(
|
inline std::basic_string<Char> vformat(
|
||||||
const S& format_str, basic_format_args<buffer_context<Char>> args) {
|
const S& format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
return internal::vformat(to_string_view(format_str), args);
|
return internal::vformat(to_string_view(format_str), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1475,44 +1737,52 @@ template <typename S, typename... Args, typename Char = char_t<S>>
|
|||||||
inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
|
inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
|
||||||
return internal::vformat(
|
return internal::vformat(
|
||||||
to_string_view(format_str),
|
to_string_view(format_str),
|
||||||
{internal::make_args_checked<Args...>(format_str, args...)});
|
internal::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_API void vprint(std::FILE* f, string_view format_str, format_args args);
|
FMT_API void vprint(string_view, format_args);
|
||||||
FMT_API void vprint(string_view format_str, format_args args);
|
FMT_API void vprint(std::FILE*, string_view, format_args);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Prints formatted data to the file *f*. For wide format strings,
|
Formats ``args`` according to specifications in ``format_str`` and writes the
|
||||||
*f* should be in wide-oriented mode set via ``fwide(f, 1)`` or
|
output to the file ``f``. Strings are assumed to be Unicode-encoded unless the
|
||||||
``_setmode(_fileno(f), _O_U8TEXT)`` on Windows.
|
``FMT_UNICODE`` macro is set to 0.
|
||||||
|
|
||||||
**Example**::
|
**Example**::
|
||||||
|
|
||||||
fmt::print(stderr, "Don't {}!", "panic");
|
fmt::print(stderr, "Don't {}!", "panic");
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
|
||||||
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
|
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
|
||||||
vprint(f, to_string_view(format_str),
|
return internal::is_unicode<Char>()
|
||||||
internal::make_args_checked<Args...>(format_str, args...));
|
? vprint(f, to_string_view(format_str),
|
||||||
|
internal::make_args_checked<Args...>(format_str, args...))
|
||||||
|
: internal::vprint_mojibake(
|
||||||
|
f, to_string_view(format_str),
|
||||||
|
internal::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Prints formatted data to ``stdout``.
|
Formats ``args`` according to specifications in ``format_str`` and writes
|
||||||
|
the output to ``stdout``. Strings are assumed to be Unicode-encoded unless
|
||||||
|
the ``FMT_UNICODE`` macro is set to 0.
|
||||||
|
|
||||||
**Example**::
|
**Example**::
|
||||||
|
|
||||||
fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
|
fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
|
||||||
inline void print(const S& format_str, Args&&... args) {
|
inline void print(const S& format_str, Args&&... args) {
|
||||||
vprint(to_string_view(format_str),
|
return internal::is_unicode<Char>()
|
||||||
internal::make_args_checked<Args...>(format_str, args...));
|
? vprint(to_string_view(format_str),
|
||||||
|
internal::make_args_checked<Args...>(format_str, args...))
|
||||||
|
: internal::vprint_mojibake(
|
||||||
|
stdout, to_string_view(format_str),
|
||||||
|
internal::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
@ -8,8 +8,6 @@
|
|||||||
#ifndef FMT_FORMAT_INL_H_
|
#ifndef FMT_FORMAT_INL_H_
|
||||||
#define FMT_FORMAT_INL_H_
|
#define FMT_FORMAT_INL_H_
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
@ -17,29 +15,15 @@
|
|||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstring> // for std::memmove
|
#include <cstring> // for std::memmove
|
||||||
#include <cwchar>
|
#include <cwchar>
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
# include <locale>
|
# include <locale>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FMT_USE_WINDOWS_H
|
#ifdef _WIN32
|
||||||
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
|
# include <io.h>
|
||||||
# define WIN32_LEAN_AND_MEAN
|
# include <windows.h>
|
||||||
# endif
|
|
||||||
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
|
|
||||||
# include <windows.h>
|
|
||||||
# else
|
|
||||||
# define NOMINMAX
|
|
||||||
# include <windows.h>
|
|
||||||
# undef NOMINMAX
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if FMT_EXCEPTIONS
|
|
||||||
# define FMT_TRY try
|
|
||||||
# define FMT_CATCH(x) catch (x)
|
|
||||||
#else
|
|
||||||
# define FMT_TRY if (true)
|
|
||||||
# define FMT_CATCH(x) if (false)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
@ -73,8 +57,6 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
|
|||||||
# define FMT_SNPRINTF fmt_snprintf
|
# define FMT_SNPRINTF fmt_snprintf
|
||||||
#endif // _MSC_VER
|
#endif // _MSC_VER
|
||||||
|
|
||||||
using format_func = void (*)(internal::buffer<char>&, int, string_view);
|
|
||||||
|
|
||||||
// A portable thread-safe version of strerror.
|
// A portable thread-safe version of strerror.
|
||||||
// Sets buffer to point to a string describing the error code.
|
// Sets buffer to point to a string describing the error code.
|
||||||
// This can be either a pointer to a string stored in buffer,
|
// This can be either a pointer to a string stored in buffer,
|
||||||
@ -104,6 +86,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle the result of GNU-specific version of strerror_r.
|
// Handle the result of GNU-specific version of strerror_r.
|
||||||
|
FMT_MAYBE_UNUSED
|
||||||
int handle(char* message) {
|
int handle(char* message) {
|
||||||
// If the buffer is full then the message is probably truncated.
|
// If the buffer is full then the message is probably truncated.
|
||||||
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
|
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
|
||||||
@ -113,11 +96,13 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle the case when strerror_r is not available.
|
// Handle the case when strerror_r is not available.
|
||||||
|
FMT_MAYBE_UNUSED
|
||||||
int handle(internal::null<>) {
|
int handle(internal::null<>) {
|
||||||
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
|
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to strerror_s when strerror_r is not available.
|
// Fallback to strerror_s when strerror_r is not available.
|
||||||
|
FMT_MAYBE_UNUSED
|
||||||
int fallback(int result) {
|
int fallback(int result) {
|
||||||
// If the buffer is full then the message is probably truncated.
|
// If the buffer is full then the message is probably truncated.
|
||||||
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE
|
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE
|
||||||
@ -168,15 +153,6 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
|
|||||||
assert(out.size() <= inline_buffer_size);
|
assert(out.size() <= inline_buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A wrapper around fwrite that throws on error.
|
|
||||||
FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
|
|
||||||
FILE* stream) {
|
|
||||||
size_t written = std::fwrite(ptr, size, count, stream);
|
|
||||||
if (written < count) {
|
|
||||||
FMT_THROW(system_error(errno, "cannot write to file"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC void report_error(format_func func, int error_code,
|
FMT_FUNC void report_error(format_func func, int error_code,
|
||||||
string_view message) FMT_NOEXCEPT {
|
string_view message) FMT_NOEXCEPT {
|
||||||
memory_buffer full_message;
|
memory_buffer full_message;
|
||||||
@ -185,6 +161,13 @@ FMT_FUNC void report_error(format_func func, int error_code,
|
|||||||
(void)std::fwrite(full_message.data(), full_message.size(), 1, stderr);
|
(void)std::fwrite(full_message.data(), full_message.size(), 1, stderr);
|
||||||
std::fputc('\n', stderr);
|
std::fputc('\n', stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A wrapper around fwrite that throws on error.
|
||||||
|
FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
|
||||||
|
FILE* stream) {
|
||||||
|
size_t written = std::fwrite(ptr, size, count, stream);
|
||||||
|
if (written < count) FMT_THROW(system_error(errno, "cannot write to file"));
|
||||||
|
}
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
@ -356,6 +339,10 @@ class fp {
|
|||||||
private:
|
private:
|
||||||
using significand_type = uint64_t;
|
using significand_type = uint64_t;
|
||||||
|
|
||||||
|
public:
|
||||||
|
significand_type f;
|
||||||
|
int e;
|
||||||
|
|
||||||
// All sizes are in bits.
|
// All sizes are in bits.
|
||||||
// Subtract 1 to account for an implicit most significant bit in the
|
// Subtract 1 to account for an implicit most significant bit in the
|
||||||
// normalized form.
|
// normalized form.
|
||||||
@ -363,11 +350,6 @@ class fp {
|
|||||||
std::numeric_limits<double>::digits - 1;
|
std::numeric_limits<double>::digits - 1;
|
||||||
static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
|
static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
|
||||||
1ULL << double_significand_size;
|
1ULL << double_significand_size;
|
||||||
|
|
||||||
public:
|
|
||||||
significand_type f;
|
|
||||||
int e;
|
|
||||||
|
|
||||||
static FMT_CONSTEXPR_DECL const int significand_size =
|
static FMT_CONSTEXPR_DECL const int significand_size =
|
||||||
bits<significand_type>::value;
|
bits<significand_type>::value;
|
||||||
|
|
||||||
@ -378,22 +360,6 @@ class fp {
|
|||||||
// errors on platforms where double is not IEEE754.
|
// errors on platforms where double is not IEEE754.
|
||||||
template <typename Double> explicit fp(Double d) { assign(d); }
|
template <typename Double> explicit fp(Double d) { assign(d); }
|
||||||
|
|
||||||
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
|
|
||||||
template <int SHIFT> friend fp normalize(fp value) {
|
|
||||||
// Handle subnormals.
|
|
||||||
const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
|
|
||||||
while ((value.f & shifted_implicit_bit) == 0) {
|
|
||||||
value.f <<= 1;
|
|
||||||
--value.e;
|
|
||||||
}
|
|
||||||
// Subtract 1 to account for hidden bit.
|
|
||||||
const auto offset =
|
|
||||||
fp::significand_size - fp::double_significand_size - SHIFT - 1;
|
|
||||||
value.f <<= offset;
|
|
||||||
value.e -= offset;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assigns d to this and return true iff predecessor is closer than successor.
|
// Assigns d to this and return true iff predecessor is closer than successor.
|
||||||
template <typename Double, FMT_ENABLE_IF(sizeof(Double) == sizeof(uint64_t))>
|
template <typename Double, FMT_ENABLE_IF(sizeof(Double) == sizeof(uint64_t))>
|
||||||
bool assign(Double d) {
|
bool assign(Double d) {
|
||||||
@ -406,7 +372,8 @@ class fp {
|
|||||||
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
|
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
|
||||||
auto u = bit_cast<uint64_t>(d);
|
auto u = bit_cast<uint64_t>(d);
|
||||||
f = u & significand_mask;
|
f = u & significand_mask;
|
||||||
auto biased_e = (u & exponent_mask) >> double_significand_size;
|
int biased_e =
|
||||||
|
static_cast<int>((u & exponent_mask) >> double_significand_size);
|
||||||
// Predecessor is closer if d is a normalized power of 2 (f == 0) other than
|
// Predecessor is closer if d is a normalized power of 2 (f == 0) other than
|
||||||
// the smallest normalized number (biased_e > 1).
|
// the smallest normalized number (biased_e > 1).
|
||||||
bool is_predecessor_closer = f == 0 && biased_e > 1;
|
bool is_predecessor_closer = f == 0 && biased_e > 1;
|
||||||
@ -414,7 +381,7 @@ class fp {
|
|||||||
f += implicit_bit;
|
f += implicit_bit;
|
||||||
else
|
else
|
||||||
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
|
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
|
||||||
e = static_cast<int>(biased_e - exponent_bias - double_significand_size);
|
e = biased_e - exponent_bias - double_significand_size;
|
||||||
return is_predecessor_closer;
|
return is_predecessor_closer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,6 +420,22 @@ class fp {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
|
||||||
|
template <int SHIFT> fp normalize(fp value) {
|
||||||
|
// Handle subnormals.
|
||||||
|
const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
|
||||||
|
while ((value.f & shifted_implicit_bit) == 0) {
|
||||||
|
value.f <<= 1;
|
||||||
|
--value.e;
|
||||||
|
}
|
||||||
|
// Subtract 1 to account for hidden bit.
|
||||||
|
const auto offset =
|
||||||
|
fp::significand_size - fp::double_significand_size - SHIFT - 1;
|
||||||
|
value.f <<= offset;
|
||||||
|
value.e -= offset;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
|
inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
|
||||||
|
|
||||||
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
|
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
|
||||||
@ -477,14 +460,12 @@ inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; }
|
|||||||
|
|
||||||
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
|
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
|
||||||
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
|
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
|
||||||
FMT_FUNC fp get_cached_power(int min_exponent, int& pow10_exponent) {
|
inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
|
||||||
const uint64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10))
|
const int64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10))
|
||||||
int index = static_cast<int>(
|
int index = static_cast<int>(
|
||||||
static_cast<int64_t>(
|
((min_exponent + fp::significand_size - 1) * one_over_log2_10 +
|
||||||
(min_exponent + fp::significand_size - 1) * one_over_log2_10 +
|
((int64_t(1) << 32) - 1)) // ceil
|
||||||
((uint64_t(1) << 32) - 1) // ceil
|
>> 32 // arithmetic shift
|
||||||
) >>
|
|
||||||
32 // arithmetic shift
|
|
||||||
);
|
);
|
||||||
// Decimal exponent of the first (smallest) cached power of 10.
|
// Decimal exponent of the first (smallest) cached power of 10.
|
||||||
const int first_dec_exp = -348;
|
const int first_dec_exp = -348;
|
||||||
@ -526,20 +507,23 @@ class bigint {
|
|||||||
basic_memory_buffer<bigit, bigits_capacity> bigits_;
|
basic_memory_buffer<bigit, bigits_capacity> bigits_;
|
||||||
int exp_;
|
int exp_;
|
||||||
|
|
||||||
|
bigit operator[](int index) const { return bigits_[to_unsigned(index)]; }
|
||||||
|
bigit& operator[](int index) { return bigits_[to_unsigned(index)]; }
|
||||||
|
|
||||||
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
|
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
|
||||||
|
|
||||||
friend struct formatter<bigint>;
|
friend struct formatter<bigint>;
|
||||||
|
|
||||||
void subtract_bigits(int index, bigit other, bigit& borrow) {
|
void subtract_bigits(int index, bigit other, bigit& borrow) {
|
||||||
auto result = static_cast<double_bigit>(bigits_[index]) - other - borrow;
|
auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
|
||||||
bigits_[index] = static_cast<bigit>(result);
|
(*this)[index] = static_cast<bigit>(result);
|
||||||
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
|
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove_leading_zeros() {
|
void remove_leading_zeros() {
|
||||||
int num_bigits = static_cast<int>(bigits_.size()) - 1;
|
int num_bigits = static_cast<int>(bigits_.size()) - 1;
|
||||||
while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits;
|
while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
|
||||||
bigits_.resize(num_bigits + 1);
|
bigits_.resize(to_unsigned(num_bigits + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes *this -= other assuming aligned bigints and *this >= other.
|
// Computes *this -= other assuming aligned bigints and *this >= other.
|
||||||
@ -548,8 +532,7 @@ class bigint {
|
|||||||
FMT_ASSERT(compare(*this, other) >= 0, "");
|
FMT_ASSERT(compare(*this, other) >= 0, "");
|
||||||
bigit borrow = 0;
|
bigit borrow = 0;
|
||||||
int i = other.exp_ - exp_;
|
int i = other.exp_ - exp_;
|
||||||
for (int j = 0, n = static_cast<int>(other.bigits_.size()); j != n;
|
for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) {
|
||||||
++i, ++j) {
|
|
||||||
subtract_bigits(i, other.bigits_[j], borrow);
|
subtract_bigits(i, other.bigits_[j], borrow);
|
||||||
}
|
}
|
||||||
while (borrow > 0) subtract_bigits(i, 0, borrow);
|
while (borrow > 0) subtract_bigits(i, 0, borrow);
|
||||||
@ -600,7 +583,7 @@ class bigint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void assign(uint64_t n) {
|
void assign(uint64_t n) {
|
||||||
int num_bigits = 0;
|
size_t num_bigits = 0;
|
||||||
do {
|
do {
|
||||||
bigits_[num_bigits++] = n & ~bigit(0);
|
bigits_[num_bigits++] = n & ~bigit(0);
|
||||||
n >>= bigit_bits;
|
n >>= bigit_bits;
|
||||||
@ -641,7 +624,7 @@ class bigint {
|
|||||||
int end = i - j;
|
int end = i - j;
|
||||||
if (end < 0) end = 0;
|
if (end < 0) end = 0;
|
||||||
for (; i >= end; --i, --j) {
|
for (; i >= end; --i, --j) {
|
||||||
bigit lhs_bigit = lhs.bigits_[i], rhs_bigit = rhs.bigits_[j];
|
bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
|
||||||
if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
|
if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
|
||||||
}
|
}
|
||||||
if (i != j) return i > j ? 1 : -1;
|
if (i != j) return i > j ? 1 : -1;
|
||||||
@ -656,7 +639,7 @@ class bigint {
|
|||||||
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
|
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
|
||||||
if (max_lhs_bigits > num_rhs_bigits) return 1;
|
if (max_lhs_bigits > num_rhs_bigits) return 1;
|
||||||
auto get_bigit = [](const bigint& n, int i) -> bigit {
|
auto get_bigit = [](const bigint& n, int i) -> bigit {
|
||||||
return i >= n.exp_ && i < n.num_bigits() ? n.bigits_[i - n.exp_] : 0;
|
return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
|
||||||
};
|
};
|
||||||
double_bigit borrow = 0;
|
double_bigit borrow = 0;
|
||||||
int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
|
int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
|
||||||
@ -696,7 +679,7 @@ class bigint {
|
|||||||
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
|
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
|
||||||
int num_bigits = static_cast<int>(bigits_.size());
|
int num_bigits = static_cast<int>(bigits_.size());
|
||||||
int num_result_bigits = 2 * num_bigits;
|
int num_result_bigits = 2 * num_bigits;
|
||||||
bigits_.resize(num_result_bigits);
|
bigits_.resize(to_unsigned(num_result_bigits));
|
||||||
using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
|
using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
|
||||||
auto sum = accumulator_t();
|
auto sum = accumulator_t();
|
||||||
for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
|
for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
|
||||||
@ -706,7 +689,7 @@ class bigint {
|
|||||||
// Most terms are multiplied twice which can be optimized in the future.
|
// Most terms are multiplied twice which can be optimized in the future.
|
||||||
sum += static_cast<double_bigit>(n[i]) * n[j];
|
sum += static_cast<double_bigit>(n[i]) * n[j];
|
||||||
}
|
}
|
||||||
bigits_[bigit_index] = static_cast<bigit>(sum);
|
(*this)[bigit_index] = static_cast<bigit>(sum);
|
||||||
sum >>= bits<bigit>::value; // Compute the carry.
|
sum >>= bits<bigit>::value; // Compute the carry.
|
||||||
}
|
}
|
||||||
// Do the same for the top half.
|
// Do the same for the top half.
|
||||||
@ -714,7 +697,7 @@ class bigint {
|
|||||||
++bigit_index) {
|
++bigit_index) {
|
||||||
for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
|
for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
|
||||||
sum += static_cast<double_bigit>(n[i++]) * n[j--];
|
sum += static_cast<double_bigit>(n[i++]) * n[j--];
|
||||||
bigits_[bigit_index] = static_cast<bigit>(sum);
|
(*this)[bigit_index] = static_cast<bigit>(sum);
|
||||||
sum >>= bits<bigit>::value;
|
sum >>= bits<bigit>::value;
|
||||||
}
|
}
|
||||||
--num_result_bigits;
|
--num_result_bigits;
|
||||||
@ -728,11 +711,11 @@ class bigint {
|
|||||||
FMT_ASSERT(this != &divisor, "");
|
FMT_ASSERT(this != &divisor, "");
|
||||||
if (compare(*this, divisor) < 0) return 0;
|
if (compare(*this, divisor) < 0) return 0;
|
||||||
int num_bigits = static_cast<int>(bigits_.size());
|
int num_bigits = static_cast<int>(bigits_.size());
|
||||||
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1] != 0, "");
|
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
|
||||||
int exp_difference = exp_ - divisor.exp_;
|
int exp_difference = exp_ - divisor.exp_;
|
||||||
if (exp_difference > 0) {
|
if (exp_difference > 0) {
|
||||||
// Align bigints by adding trailing zeros to simplify subtraction.
|
// Align bigints by adding trailing zeros to simplify subtraction.
|
||||||
bigits_.resize(num_bigits + exp_difference);
|
bigits_.resize(to_unsigned(num_bigits + exp_difference));
|
||||||
for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
|
for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
|
||||||
bigits_[j] = bigits_[i];
|
bigits_[j] = bigits_[i];
|
||||||
std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
|
std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
|
||||||
@ -747,7 +730,7 @@ class bigint {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum round_direction { unknown, up, down };
|
enum class round_direction { unknown, up, down };
|
||||||
|
|
||||||
// Given the divisor (normally a power of 10), the remainder = v % divisor for
|
// Given the divisor (normally a power of 10), the remainder = v % divisor for
|
||||||
// some number v and the error, returns whether v should be rounded up, down, or
|
// some number v and the error, returns whether v should be rounded up, down, or
|
||||||
@ -760,13 +743,13 @@ inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder,
|
|||||||
FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
|
FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
|
||||||
// Round down if (remainder + error) * 2 <= divisor.
|
// Round down if (remainder + error) * 2 <= divisor.
|
||||||
if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
|
if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
|
||||||
return down;
|
return round_direction::down;
|
||||||
// Round up if (remainder - error) * 2 >= divisor.
|
// Round up if (remainder - error) * 2 >= divisor.
|
||||||
if (remainder >= error &&
|
if (remainder >= error &&
|
||||||
remainder - error >= divisor - (remainder - error)) {
|
remainder - error >= divisor - (remainder - error)) {
|
||||||
return up;
|
return round_direction::up;
|
||||||
}
|
}
|
||||||
return unknown;
|
return round_direction::unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace digits {
|
namespace digits {
|
||||||
@ -777,6 +760,20 @@ enum result {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A version of count_digits optimized for grisu_gen_digits.
|
||||||
|
inline int grisu_count_digits(uint32_t n) {
|
||||||
|
if (n < 10) return 1;
|
||||||
|
if (n < 100) return 2;
|
||||||
|
if (n < 1000) return 3;
|
||||||
|
if (n < 10000) return 4;
|
||||||
|
if (n < 100000) return 5;
|
||||||
|
if (n < 1000000) return 6;
|
||||||
|
if (n < 10000000) return 7;
|
||||||
|
if (n < 100000000) return 8;
|
||||||
|
if (n < 1000000000) return 9;
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
// Generates output using the Grisu digit-gen algorithm.
|
// Generates output using the Grisu digit-gen algorithm.
|
||||||
// error: the size of the region (lower, upper) outside of which numbers
|
// error: the size of the region (lower, upper) outside of which numbers
|
||||||
// definitely do not round to value (Delta in Grisu3).
|
// definitely do not round to value (Delta in Grisu3).
|
||||||
@ -792,7 +789,7 @@ FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error,
|
|||||||
FMT_ASSERT(integral == value.f >> -one.e, "");
|
FMT_ASSERT(integral == value.f >> -one.e, "");
|
||||||
// The fractional part of scaled value (p2 in Grisu) c = value % one.
|
// The fractional part of scaled value (p2 in Grisu) c = value % one.
|
||||||
uint64_t fractional = value.f & (one.f - 1);
|
uint64_t fractional = value.f & (one.f - 1);
|
||||||
exp = count_digits(integral); // kappa in Grisu.
|
exp = grisu_count_digits(integral); // kappa in Grisu.
|
||||||
// Divide by 10 to prevent overflow.
|
// Divide by 10 to prevent overflow.
|
||||||
auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e,
|
auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e,
|
||||||
value.f / 10, error * 10, exp);
|
value.f / 10, error * 10, exp);
|
||||||
@ -882,8 +879,8 @@ struct fixed_handler {
|
|||||||
if (precision > 0) return digits::more;
|
if (precision > 0) return digits::more;
|
||||||
if (precision < 0) return digits::done;
|
if (precision < 0) return digits::done;
|
||||||
auto dir = get_round_direction(divisor, remainder, error);
|
auto dir = get_round_direction(divisor, remainder, error);
|
||||||
if (dir == unknown) return digits::error;
|
if (dir == round_direction::unknown) return digits::error;
|
||||||
buf[size++] = dir == up ? '1' : '0';
|
buf[size++] = dir == round_direction::up ? '1' : '0';
|
||||||
return digits::done;
|
return digits::done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -901,7 +898,8 @@ struct fixed_handler {
|
|||||||
FMT_ASSERT(error == 1 && divisor > 2, "");
|
FMT_ASSERT(error == 1 && divisor > 2, "");
|
||||||
}
|
}
|
||||||
auto dir = get_round_direction(divisor, remainder, error);
|
auto dir = get_round_direction(divisor, remainder, error);
|
||||||
if (dir != up) return dir == down ? digits::done : digits::error;
|
if (dir != round_direction::up)
|
||||||
|
return dir == round_direction::down ? digits::done : digits::error;
|
||||||
++buf[size - 1];
|
++buf[size - 1];
|
||||||
for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
|
for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
|
||||||
buf[i] = '0';
|
buf[i] = '0';
|
||||||
@ -1028,7 +1026,7 @@ void fallback_format(Double d, buffer<char>& buf, int& exp10) {
|
|||||||
if (result > 0 || (result == 0 && (digit % 2) != 0))
|
if (result > 0 || (result == 0 && (digit % 2) != 0))
|
||||||
++data[num_digits - 1];
|
++data[num_digits - 1];
|
||||||
}
|
}
|
||||||
buf.resize(num_digits);
|
buf.resize(to_unsigned(num_digits));
|
||||||
exp10 -= num_digits - 1;
|
exp10 -= num_digits - 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1043,7 +1041,7 @@ void fallback_format(Double d, buffer<char>& buf, int& exp10) {
|
|||||||
// if T is a IEEE754 binary32 or binary64 and snprintf otherwise.
|
// if T is a IEEE754 binary32 or binary64 and snprintf otherwise.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
||||||
static_assert(!std::is_same<T, float>(), "");
|
static_assert(!std::is_same<T, float>::value, "");
|
||||||
FMT_ASSERT(value >= 0, "value is negative");
|
FMT_ASSERT(value >= 0, "value is negative");
|
||||||
|
|
||||||
const bool fixed = specs.format == float_format::fixed;
|
const bool fixed = specs.format == float_format::fixed;
|
||||||
@ -1062,25 +1060,7 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
|||||||
int exp = 0;
|
int exp = 0;
|
||||||
const int min_exp = -60; // alpha in Grisu.
|
const int min_exp = -60; // alpha in Grisu.
|
||||||
int cached_exp10 = 0; // K in Grisu.
|
int cached_exp10 = 0; // K in Grisu.
|
||||||
if (precision != -1) {
|
if (precision < 0) {
|
||||||
if (precision > 17) return snprintf_float(value, precision, specs, buf);
|
|
||||||
fp normalized = normalize(fp(value));
|
|
||||||
const auto cached_pow = get_cached_power(
|
|
||||||
min_exp - (normalized.e + fp::significand_size), cached_exp10);
|
|
||||||
normalized = normalized * cached_pow;
|
|
||||||
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
|
|
||||||
if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error)
|
|
||||||
return snprintf_float(value, precision, specs, buf);
|
|
||||||
int num_digits = handler.size;
|
|
||||||
if (!fixed) {
|
|
||||||
// Remove trailing zeros.
|
|
||||||
while (num_digits > 0 && buf[num_digits - 1] == '0') {
|
|
||||||
--num_digits;
|
|
||||||
++exp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf.resize(to_unsigned(num_digits));
|
|
||||||
} else {
|
|
||||||
fp fp_value;
|
fp fp_value;
|
||||||
auto boundaries = specs.binary32
|
auto boundaries = specs.binary32
|
||||||
? fp_value.assign_float_with_boundaries(value)
|
? fp_value.assign_float_with_boundaries(value)
|
||||||
@ -1109,6 +1089,24 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
|||||||
return exp;
|
return exp;
|
||||||
}
|
}
|
||||||
buf.resize(to_unsigned(handler.size));
|
buf.resize(to_unsigned(handler.size));
|
||||||
|
} else {
|
||||||
|
if (precision > 17) return snprintf_float(value, precision, specs, buf);
|
||||||
|
fp normalized = normalize(fp(value));
|
||||||
|
const auto cached_pow = get_cached_power(
|
||||||
|
min_exp - (normalized.e + fp::significand_size), cached_exp10);
|
||||||
|
normalized = normalized * cached_pow;
|
||||||
|
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
|
||||||
|
if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error)
|
||||||
|
return snprintf_float(value, precision, specs, buf);
|
||||||
|
int num_digits = handler.size;
|
||||||
|
if (!fixed) {
|
||||||
|
// Remove trailing zeros.
|
||||||
|
while (num_digits > 0 && buf[num_digits - 1] == '0') {
|
||||||
|
--num_digits;
|
||||||
|
++exp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.resize(to_unsigned(num_digits));
|
||||||
}
|
}
|
||||||
return exp - cached_exp10;
|
return exp - cached_exp10;
|
||||||
}
|
}
|
||||||
@ -1118,7 +1116,7 @@ int snprintf_float(T value, int precision, float_specs specs,
|
|||||||
buffer<char>& buf) {
|
buffer<char>& buf) {
|
||||||
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
|
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
|
||||||
FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
|
FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
|
||||||
static_assert(!std::is_same<T, float>(), "");
|
static_assert(!std::is_same<T, float>::value, "");
|
||||||
|
|
||||||
// Subtract 1 to account for the difference in precision since we use %e for
|
// Subtract 1 to account for the difference in precision since we use %e for
|
||||||
// both general and exponent format.
|
// both general and exponent format.
|
||||||
@ -1131,7 +1129,7 @@ int snprintf_float(T value, int precision, float_specs specs,
|
|||||||
char format[max_format_size];
|
char format[max_format_size];
|
||||||
char* format_ptr = format;
|
char* format_ptr = format;
|
||||||
*format_ptr++ = '%';
|
*format_ptr++ = '%';
|
||||||
if (specs.trailing_zeros) *format_ptr++ = '#';
|
if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#';
|
||||||
if (precision >= 0) {
|
if (precision >= 0) {
|
||||||
*format_ptr++ = '.';
|
*format_ptr++ = '.';
|
||||||
*format_ptr++ = '*';
|
*format_ptr++ = '*';
|
||||||
@ -1153,7 +1151,8 @@ int snprintf_float(T value, int precision, float_specs specs,
|
|||||||
"fuzz mode - avoid large allocation inside snprintf");
|
"fuzz mode - avoid large allocation inside snprintf");
|
||||||
#endif
|
#endif
|
||||||
// Suppress the warning about a nonliteral format string.
|
// Suppress the warning about a nonliteral format string.
|
||||||
auto snprintf_ptr = FMT_SNPRINTF;
|
// Cannot use auto becase of a bug in MinGW (#1532).
|
||||||
|
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
|
||||||
int result = precision >= 0
|
int result = precision >= 0
|
||||||
? snprintf_ptr(begin, capacity, format, precision, value)
|
? snprintf_ptr(begin, capacity, format, precision, value)
|
||||||
: snprintf_ptr(begin, capacity, format, value);
|
: snprintf_ptr(begin, capacity, format, value);
|
||||||
@ -1161,7 +1160,7 @@ int snprintf_float(T value, int precision, float_specs specs,
|
|||||||
buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially.
|
buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
unsigned size = to_unsigned(result);
|
auto size = to_unsigned(result);
|
||||||
// Size equal to capacity means that the last character was truncated.
|
// Size equal to capacity means that the last character was truncated.
|
||||||
if (size >= capacity) {
|
if (size >= capacity) {
|
||||||
buf.reserve(size + offset + 1); // Add 1 for the terminating '\0'.
|
buf.reserve(size + offset + 1); // Add 1 for the terminating '\0'.
|
||||||
@ -1179,7 +1178,7 @@ int snprintf_float(T value, int precision, float_specs specs,
|
|||||||
--p;
|
--p;
|
||||||
} while (is_digit(*p));
|
} while (is_digit(*p));
|
||||||
int fraction_size = static_cast<int>(end - p - 1);
|
int fraction_size = static_cast<int>(end - p - 1);
|
||||||
std::memmove(p, p + 1, fraction_size);
|
std::memmove(p, p + 1, to_unsigned(fraction_size));
|
||||||
buf.resize(size - 1);
|
buf.resize(size - 1);
|
||||||
return -fraction_size;
|
return -fraction_size;
|
||||||
}
|
}
|
||||||
@ -1208,12 +1207,67 @@ int snprintf_float(T value, int precision, float_specs specs,
|
|||||||
while (*fraction_end == '0') --fraction_end;
|
while (*fraction_end == '0') --fraction_end;
|
||||||
// Move the fractional part left to get rid of the decimal point.
|
// Move the fractional part left to get rid of the decimal point.
|
||||||
fraction_size = static_cast<int>(fraction_end - begin - 1);
|
fraction_size = static_cast<int>(fraction_end - begin - 1);
|
||||||
std::memmove(begin + 1, begin + 2, fraction_size);
|
std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size));
|
||||||
}
|
}
|
||||||
buf.resize(fraction_size + offset + 1);
|
buf.resize(to_unsigned(fraction_size) + offset + 1);
|
||||||
return exp - fraction_size;
|
return exp - fraction_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A public domain branchless UTF-8 decoder by Christopher Wellons:
|
||||||
|
// https://github.com/skeeto/branchless-utf8
|
||||||
|
/* Decode the next character, c, from buf, reporting errors in e.
|
||||||
|
*
|
||||||
|
* Since this is a branchless decoder, four bytes will be read from the
|
||||||
|
* buffer regardless of the actual length of the next character. This
|
||||||
|
* means the buffer _must_ have at least three bytes of zero padding
|
||||||
|
* following the end of the data stream.
|
||||||
|
*
|
||||||
|
* Errors are reported in e, which will be non-zero if the parsed
|
||||||
|
* character was somehow invalid: invalid byte sequence, non-canonical
|
||||||
|
* encoding, or a surrogate half.
|
||||||
|
*
|
||||||
|
* The function returns a pointer to the next character. When an error
|
||||||
|
* occurs, this pointer will be a guess that depends on the particular
|
||||||
|
* error, but it will always advance at least one byte.
|
||||||
|
*/
|
||||||
|
FMT_FUNC const char* utf8_decode(const char* buf, uint32_t* c, int* e) {
|
||||||
|
static const char lengths[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 2, 2, 2, 2, 3, 3, 4, 0};
|
||||||
|
static const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07};
|
||||||
|
static const uint32_t mins[] = {4194304, 0, 128, 2048, 65536};
|
||||||
|
static const int shiftc[] = {0, 18, 12, 6, 0};
|
||||||
|
static const int shifte[] = {0, 6, 4, 2, 0};
|
||||||
|
|
||||||
|
auto s = reinterpret_cast<const unsigned char*>(buf);
|
||||||
|
int len = lengths[s[0] >> 3];
|
||||||
|
|
||||||
|
// Compute the pointer to the next character early so that the next
|
||||||
|
// iteration can start working on the next character. Neither Clang
|
||||||
|
// nor GCC figure out this reordering on their own.
|
||||||
|
const char* next = buf + len + !len;
|
||||||
|
|
||||||
|
// Assume a four-byte character and load four bytes. Unused bits are
|
||||||
|
// shifted out.
|
||||||
|
*c = uint32_t(s[0] & masks[len]) << 18;
|
||||||
|
*c |= uint32_t(s[1] & 0x3f) << 12;
|
||||||
|
*c |= uint32_t(s[2] & 0x3f) << 6;
|
||||||
|
*c |= uint32_t(s[3] & 0x3f) << 0;
|
||||||
|
*c >>= shiftc[len];
|
||||||
|
|
||||||
|
// Accumulate the various error conditions.
|
||||||
|
*e = (*c < mins[len]) << 6; // non-canonical encoding
|
||||||
|
*e |= ((*c >> 11) == 0x1b) << 7; // surrogate half?
|
||||||
|
*e |= (*c > 0x10FFFF) << 8; // out of range?
|
||||||
|
*e |= (s[1] & 0xc0) >> 2;
|
||||||
|
*e |= (s[2] & 0xc0) >> 4;
|
||||||
|
*e |= (s[3]) >> 6;
|
||||||
|
*e ^= 0x2a; // top two bits of each tail byte correct?
|
||||||
|
*e >>= shifte[len];
|
||||||
|
|
||||||
|
return next;
|
||||||
|
}
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
template <> struct formatter<internal::bigint> {
|
template <> struct formatter<internal::bigint> {
|
||||||
@ -1226,7 +1280,7 @@ template <> struct formatter<internal::bigint> {
|
|||||||
auto out = ctx.out();
|
auto out = ctx.out();
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto i = n.bigits_.size(); i > 0; --i) {
|
for (auto i = n.bigits_.size(); i > 0; --i) {
|
||||||
auto value = n.bigits_[i - 1];
|
auto value = n.bigits_[i - 1u];
|
||||||
if (first) {
|
if (first) {
|
||||||
out = format_to(out, "{:x}", value);
|
out = format_to(out, "{:x}", value);
|
||||||
first = false;
|
first = false;
|
||||||
@ -1240,101 +1294,37 @@ template <> struct formatter<internal::bigint> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#if FMT_USE_WINDOWS_H
|
|
||||||
|
|
||||||
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
||||||
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
|
auto transcode = [this](const char* p) {
|
||||||
if (s.size() > INT_MAX)
|
auto cp = uint32_t();
|
||||||
FMT_THROW(windows_error(ERROR_INVALID_PARAMETER, ERROR_MSG));
|
auto error = 0;
|
||||||
int s_size = static_cast<int>(s.size());
|
p = utf8_decode(p, &cp, &error);
|
||||||
if (s_size == 0) {
|
if (error != 0) FMT_THROW(std::runtime_error("invalid utf8"));
|
||||||
// MultiByteToWideChar does not support zero length, handle separately.
|
if (cp <= 0xFFFF) {
|
||||||
buffer_.resize(1);
|
buffer_.push_back(static_cast<wchar_t>(cp));
|
||||||
buffer_[0] = 0;
|
} else {
|
||||||
return;
|
cp -= 0x10000;
|
||||||
}
|
buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
|
||||||
|
buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
|
||||||
int length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(),
|
|
||||||
s_size, nullptr, 0);
|
|
||||||
if (length == 0) FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
|
|
||||||
buffer_.resize(length + 1);
|
|
||||||
length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size,
|
|
||||||
&buffer_[0], length);
|
|
||||||
if (length == 0) FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
|
|
||||||
buffer_[length] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
|
|
||||||
if (int error_code = convert(s)) {
|
|
||||||
FMT_THROW(windows_error(error_code,
|
|
||||||
"cannot convert string from UTF-16 to UTF-8"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s) {
|
|
||||||
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
|
|
||||||
int s_size = static_cast<int>(s.size());
|
|
||||||
if (s_size == 0) {
|
|
||||||
// WideCharToMultiByte does not support zero length, handle separately.
|
|
||||||
buffer_.resize(1);
|
|
||||||
buffer_[0] = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
|
|
||||||
nullptr, nullptr);
|
|
||||||
if (length == 0) return GetLastError();
|
|
||||||
buffer_.resize(length + 1);
|
|
||||||
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
|
|
||||||
length, nullptr, nullptr);
|
|
||||||
if (length == 0) return GetLastError();
|
|
||||||
buffer_[length] = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC void windows_error::init(int err_code, string_view format_str,
|
|
||||||
format_args args) {
|
|
||||||
error_code_ = err_code;
|
|
||||||
memory_buffer buffer;
|
|
||||||
internal::format_windows_error(buffer, err_code, vformat(format_str, args));
|
|
||||||
std::runtime_error& base = *this;
|
|
||||||
base = std::runtime_error(to_string(buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC void internal::format_windows_error(internal::buffer<char>& out,
|
|
||||||
int error_code,
|
|
||||||
string_view message) FMT_NOEXCEPT {
|
|
||||||
FMT_TRY {
|
|
||||||
wmemory_buffer buf;
|
|
||||||
buf.resize(inline_buffer_size);
|
|
||||||
for (;;) {
|
|
||||||
wchar_t* system_message = &buf[0];
|
|
||||||
int result = FormatMessageW(
|
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
|
|
||||||
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message,
|
|
||||||
static_cast<uint32_t>(buf.size()), nullptr);
|
|
||||||
if (result != 0) {
|
|
||||||
utf16_to_utf8 utf8_message;
|
|
||||||
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
|
||||||
internal::writer w(out);
|
|
||||||
w.write(message);
|
|
||||||
w.write(": ");
|
|
||||||
w.write(utf8_message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
||||||
break; // Can't get error message, report error code instead.
|
|
||||||
buf.resize(buf.size() * 2);
|
|
||||||
}
|
}
|
||||||
|
return p;
|
||||||
|
};
|
||||||
|
auto p = s.data();
|
||||||
|
const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars.
|
||||||
|
if (s.size() >= block_size) {
|
||||||
|
for (auto end = p + s.size() - block_size + 1; p < end;) p = transcode(p);
|
||||||
}
|
}
|
||||||
FMT_CATCH(...) {}
|
if (auto num_chars_left = s.data() + s.size() - p) {
|
||||||
format_error_code(out, error_code, message);
|
char buf[2 * block_size - 1] = {};
|
||||||
|
memcpy(buf, p, to_unsigned(num_chars_left));
|
||||||
|
p = buf;
|
||||||
|
do {
|
||||||
|
p = transcode(p);
|
||||||
|
} while (p - buf < num_chars_left);
|
||||||
|
}
|
||||||
|
buffer_.push_back(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FMT_USE_WINDOWS_H
|
|
||||||
|
|
||||||
FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
|
FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
|
||||||
string_view message) FMT_NOEXCEPT {
|
string_view message) FMT_NOEXCEPT {
|
||||||
FMT_TRY {
|
FMT_TRY {
|
||||||
@ -1369,20 +1359,37 @@ FMT_FUNC void report_system_error(int error_code,
|
|||||||
report_error(format_system_error, error_code, message);
|
report_error(format_system_error, error_code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if FMT_USE_WINDOWS_H
|
|
||||||
FMT_FUNC void report_windows_error(int error_code,
|
|
||||||
fmt::string_view message) FMT_NOEXCEPT {
|
|
||||||
report_error(internal::format_windows_error, error_code, message);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
|
FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
|
||||||
memory_buffer buffer;
|
memory_buffer buffer;
|
||||||
internal::vformat_to(buffer, format_str,
|
internal::vformat_to(buffer, format_str,
|
||||||
basic_format_args<buffer_context<char>>(args));
|
basic_format_args<buffer_context<char>>(args));
|
||||||
|
#ifdef _WIN32
|
||||||
|
auto fd = _fileno(f);
|
||||||
|
if (_isatty(fd)) {
|
||||||
|
internal::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size()));
|
||||||
|
auto written = DWORD();
|
||||||
|
if (!WriteConsoleW(reinterpret_cast<HANDLE>(_get_osfhandle(fd)),
|
||||||
|
u16.c_str(), static_cast<DWORD>(u16.size()), &written,
|
||||||
|
nullptr)) {
|
||||||
|
FMT_THROW(format_error("failed to write to console"));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
internal::fwrite_fully(buffer.data(), 1, buffer.size(), f);
|
internal::fwrite_fully(buffer.data(), 1, buffer.size(), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Print assuming legacy (non-Unicode) encoding.
|
||||||
|
FMT_FUNC void internal::vprint_mojibake(std::FILE* f, string_view format_str,
|
||||||
|
format_args args) {
|
||||||
|
memory_buffer buffer;
|
||||||
|
internal::vformat_to(buffer, format_str,
|
||||||
|
basic_format_args<buffer_context<char>>(args));
|
||||||
|
fwrite_fully(buffer.data(), 1, buffer.size(), f);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
FMT_FUNC void vprint(string_view format_str, format_args args) {
|
FMT_FUNC void vprint(string_view format_str, format_args args) {
|
||||||
vprint(stdout, format_str, args);
|
vprint(stdout, format_str, args);
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,7 @@
|
|||||||
#define FMT_LOCALE_H_
|
#define FMT_LOCALE_H_
|
||||||
|
|
||||||
#include <locale>
|
#include <locale>
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
@ -18,16 +19,16 @@ template <typename Char>
|
|||||||
typename buffer_context<Char>::iterator vformat_to(
|
typename buffer_context<Char>::iterator vformat_to(
|
||||||
const std::locale& loc, buffer<Char>& buf,
|
const std::locale& loc, buffer<Char>& buf,
|
||||||
basic_string_view<Char> format_str,
|
basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
using range = buffer_range<Char>;
|
using range = buffer_range<Char>;
|
||||||
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
|
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
|
||||||
internal::locale_ref(loc));
|
internal::locale_ref(loc));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
std::basic_string<Char> vformat(const std::locale& loc,
|
std::basic_string<Char> vformat(
|
||||||
basic_string_view<Char> format_str,
|
const std::locale& loc, basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
internal::vformat_to(loc, buffer, format_str, args);
|
internal::vformat_to(loc, buffer, format_str, args);
|
||||||
return fmt::to_string(buffer);
|
return fmt::to_string(buffer);
|
||||||
@ -37,7 +38,7 @@ std::basic_string<Char> vformat(const std::locale& loc,
|
|||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline std::basic_string<Char> vformat(
|
inline std::basic_string<Char> vformat(
|
||||||
const std::locale& loc, const S& format_str,
|
const std::locale& loc, const S& format_str,
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
return internal::vformat(loc, to_string_view(format_str), args);
|
return internal::vformat(loc, to_string_view(format_str), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,15 +47,15 @@ inline std::basic_string<Char> format(const std::locale& loc,
|
|||||||
const S& format_str, Args&&... args) {
|
const S& format_str, Args&&... args) {
|
||||||
return internal::vformat(
|
return internal::vformat(
|
||||||
loc, to_string_view(format_str),
|
loc, to_string_view(format_str),
|
||||||
{internal::make_args_checked<Args...>(format_str, args...)});
|
internal::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename OutputIt, typename... Args,
|
template <typename S, typename OutputIt, typename... Args,
|
||||||
typename Char = enable_if_t<
|
typename Char = enable_if_t<
|
||||||
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
|
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
|
||||||
inline OutputIt vformat_to(OutputIt out, const std::locale& loc,
|
inline OutputIt vformat_to(
|
||||||
const S& format_str,
|
OutputIt out, const std::locale& loc, const S& format_str,
|
||||||
format_args_t<OutputIt, Char> args) {
|
format_args_t<type_identity_t<OutputIt>, Char> args) {
|
||||||
using range = internal::output_range<OutputIt, Char>;
|
using range = internal::output_range<OutputIt, Char>;
|
||||||
return vformat_to<arg_formatter<range>>(
|
return vformat_to<arg_formatter<range>>(
|
||||||
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
||||||
|
@ -93,7 +93,9 @@ void format_value(buffer<Char>& buf, const T& value,
|
|||||||
locale_ref loc = locale_ref()) {
|
locale_ref loc = locale_ref()) {
|
||||||
formatbuf<Char> format_buf(buf);
|
formatbuf<Char> format_buf(buf);
|
||||||
std::basic_ostream<Char> output(&format_buf);
|
std::basic_ostream<Char> output(&format_buf);
|
||||||
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
if (loc) output.imbue(loc.get<std::locale>());
|
if (loc) output.imbue(loc.get<std::locale>());
|
||||||
|
#endif
|
||||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||||
output << value;
|
output << value;
|
||||||
buf.resize(buf.size());
|
buf.resize(buf.size());
|
||||||
@ -115,7 +117,7 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
|||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
internal::vformat_to(buffer, format_str, args);
|
internal::vformat_to(buffer, format_str, args);
|
||||||
internal::write(os, buffer);
|
internal::write(os, buffer);
|
||||||
@ -134,7 +136,7 @@ template <typename S, typename... Args,
|
|||||||
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
||||||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
||||||
vprint(os, to_string_view(format_str),
|
vprint(os, to_string_view(format_str),
|
||||||
{internal::make_args_checked<Args...>(format_str, args...)});
|
internal::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
@ -1,321 +1,2 @@
|
|||||||
// A C++ interface to POSIX functions.
|
#include "os.h"
|
||||||
//
|
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"
|
||||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
|
|
||||||
#ifndef FMT_POSIX_H_
|
|
||||||
#define FMT_POSIX_H_
|
|
||||||
|
|
||||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
|
||||||
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
|
|
||||||
# undef __STRICT_ANSI__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <clocale> // for locale_t
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib> // for strtod_l
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
|
||||||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
// UWP doesn't provide _pipe.
|
|
||||||
#if FMT_HAS_INCLUDE("winapifamily.h")
|
|
||||||
# include <winapifamily.h>
|
|
||||||
#endif
|
|
||||||
#if FMT_HAS_INCLUDE("fcntl.h") && \
|
|
||||||
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
|
||||||
# include <fcntl.h> // for O_RDONLY
|
|
||||||
# define FMT_USE_FCNTL 1
|
|
||||||
#else
|
|
||||||
# define FMT_USE_FCNTL 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef FMT_POSIX
|
|
||||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
|
||||||
// Fix warnings about deprecated symbols.
|
|
||||||
# define FMT_POSIX(call) _##call
|
|
||||||
# else
|
|
||||||
# define FMT_POSIX(call) call
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
|
||||||
#ifdef FMT_SYSTEM
|
|
||||||
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
|
||||||
#else
|
|
||||||
# define FMT_SYSTEM(call) call
|
|
||||||
# ifdef _WIN32
|
|
||||||
// Fix warnings about deprecated symbols.
|
|
||||||
# define FMT_POSIX_CALL(call) ::_##call
|
|
||||||
# else
|
|
||||||
# define FMT_POSIX_CALL(call) ::call
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Retries the expression while it evaluates to error_result and errno
|
|
||||||
// equals to EINTR.
|
|
||||||
#ifndef _WIN32
|
|
||||||
# define FMT_RETRY_VAL(result, expression, error_result) \
|
|
||||||
do { \
|
|
||||||
(result) = (expression); \
|
|
||||||
} while ((result) == (error_result) && errno == EINTR)
|
|
||||||
#else
|
|
||||||
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
A reference to a null-terminated string. It can be constructed from a C
|
|
||||||
string or ``std::string``.
|
|
||||||
|
|
||||||
You can use one of the following type aliases for common character types:
|
|
||||||
|
|
||||||
+---------------+-----------------------------+
|
|
||||||
| Type | Definition |
|
|
||||||
+===============+=============================+
|
|
||||||
| cstring_view | basic_cstring_view<char> |
|
|
||||||
+---------------+-----------------------------+
|
|
||||||
| wcstring_view | basic_cstring_view<wchar_t> |
|
|
||||||
+---------------+-----------------------------+
|
|
||||||
|
|
||||||
This class is most useful as a parameter type to allow passing
|
|
||||||
different types of strings to a function, for example::
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
std::string format(cstring_view format_str, const Args & ... args);
|
|
||||||
|
|
||||||
format("{}", 42);
|
|
||||||
format(std::string("{}"), 42);
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename Char> class basic_cstring_view {
|
|
||||||
private:
|
|
||||||
const Char* data_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/** Constructs a string reference object from a C string. */
|
|
||||||
basic_cstring_view(const Char* s) : data_(s) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Constructs a string reference from an ``std::string`` object.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
|
|
||||||
|
|
||||||
/** Returns the pointer to a C string. */
|
|
||||||
const Char* c_str() const { return data_; }
|
|
||||||
};
|
|
||||||
|
|
||||||
using cstring_view = basic_cstring_view<char>;
|
|
||||||
using wcstring_view = basic_cstring_view<wchar_t>;
|
|
||||||
|
|
||||||
// An error code.
|
|
||||||
class error_code {
|
|
||||||
private:
|
|
||||||
int value_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
|
||||||
|
|
||||||
int get() const FMT_NOEXCEPT { return value_; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// A buffered file.
|
|
||||||
class buffered_file {
|
|
||||||
private:
|
|
||||||
FILE* file_;
|
|
||||||
|
|
||||||
friend class file;
|
|
||||||
|
|
||||||
explicit buffered_file(FILE* f) : file_(f) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
buffered_file(const buffered_file&) = delete;
|
|
||||||
void operator=(const buffered_file&) = delete;
|
|
||||||
|
|
||||||
// Constructs a buffered_file object which doesn't represent any file.
|
|
||||||
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
|
|
||||||
|
|
||||||
// Destroys the object closing the file it represents if any.
|
|
||||||
FMT_API ~buffered_file() FMT_NOEXCEPT;
|
|
||||||
|
|
||||||
public:
|
|
||||||
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
|
|
||||||
other.file_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffered_file& operator=(buffered_file&& other) {
|
|
||||||
close();
|
|
||||||
file_ = other.file_;
|
|
||||||
other.file_ = nullptr;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Opens a file.
|
|
||||||
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
|
||||||
|
|
||||||
// Closes the file.
|
|
||||||
FMT_API void close();
|
|
||||||
|
|
||||||
// Returns the pointer to a FILE object representing this file.
|
|
||||||
FILE* get() const FMT_NOEXCEPT { return file_; }
|
|
||||||
|
|
||||||
// We place parentheses around fileno to workaround a bug in some versions
|
|
||||||
// of MinGW that define fileno as a macro.
|
|
||||||
FMT_API int(fileno)() const;
|
|
||||||
|
|
||||||
void vprint(string_view format_str, format_args args) {
|
|
||||||
fmt::vprint(file_, format_str, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
inline void print(string_view format_str, const Args&... args) {
|
|
||||||
vprint(format_str, make_format_args(args...));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#if FMT_USE_FCNTL
|
|
||||||
// A file. Closed file is represented by a file object with descriptor -1.
|
|
||||||
// Methods that are not declared with FMT_NOEXCEPT may throw
|
|
||||||
// fmt::system_error in case of failure. Note that some errors such as
|
|
||||||
// closing the file multiple times will cause a crash on Windows rather
|
|
||||||
// than an exception. You can get standard behavior by overriding the
|
|
||||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
|
||||||
class file {
|
|
||||||
private:
|
|
||||||
int fd_; // File descriptor.
|
|
||||||
|
|
||||||
// Constructs a file object with a given descriptor.
|
|
||||||
explicit file(int fd) : fd_(fd) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Possible values for the oflag argument to the constructor.
|
|
||||||
enum {
|
|
||||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
|
||||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
|
||||||
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
|
||||||
};
|
|
||||||
|
|
||||||
// Constructs a file object which doesn't represent any file.
|
|
||||||
file() FMT_NOEXCEPT : fd_(-1) {}
|
|
||||||
|
|
||||||
// Opens a file and constructs a file object representing this file.
|
|
||||||
FMT_API file(cstring_view path, int oflag);
|
|
||||||
|
|
||||||
public:
|
|
||||||
file(const file&) = delete;
|
|
||||||
void operator=(const file&) = delete;
|
|
||||||
|
|
||||||
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
|
|
||||||
|
|
||||||
file& operator=(file&& other) FMT_NOEXCEPT {
|
|
||||||
close();
|
|
||||||
fd_ = other.fd_;
|
|
||||||
other.fd_ = -1;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroys the object closing the file it represents if any.
|
|
||||||
FMT_API ~file() FMT_NOEXCEPT;
|
|
||||||
|
|
||||||
// Returns the file descriptor.
|
|
||||||
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
|
||||||
|
|
||||||
// Closes the file.
|
|
||||||
FMT_API void close();
|
|
||||||
|
|
||||||
// Returns the file size. The size has signed type for consistency with
|
|
||||||
// stat::st_size.
|
|
||||||
FMT_API long long size() const;
|
|
||||||
|
|
||||||
// Attempts to read count bytes from the file into the specified buffer.
|
|
||||||
FMT_API std::size_t read(void* buffer, std::size_t count);
|
|
||||||
|
|
||||||
// Attempts to write count bytes from the specified buffer to the file.
|
|
||||||
FMT_API std::size_t write(const void* buffer, std::size_t count);
|
|
||||||
|
|
||||||
// Duplicates a file descriptor with the dup function and returns
|
|
||||||
// the duplicate as a file object.
|
|
||||||
FMT_API static file dup(int fd);
|
|
||||||
|
|
||||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
|
||||||
// necessary.
|
|
||||||
FMT_API void dup2(int fd);
|
|
||||||
|
|
||||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
|
||||||
// necessary.
|
|
||||||
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
|
|
||||||
|
|
||||||
// Creates a pipe setting up read_end and write_end file objects for reading
|
|
||||||
// and writing respectively.
|
|
||||||
FMT_API static void pipe(file& read_end, file& write_end);
|
|
||||||
|
|
||||||
// Creates a buffered_file object associated with this file and detaches
|
|
||||||
// this file object from the file.
|
|
||||||
FMT_API buffered_file fdopen(const char* mode);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Returns the memory page size.
|
|
||||||
long getpagesize();
|
|
||||||
#endif // FMT_USE_FCNTL
|
|
||||||
|
|
||||||
#ifdef FMT_LOCALE
|
|
||||||
// A "C" numeric locale.
|
|
||||||
class Locale {
|
|
||||||
private:
|
|
||||||
# ifdef _WIN32
|
|
||||||
using locale_t = _locale_t;
|
|
||||||
|
|
||||||
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
|
||||||
|
|
||||||
static locale_t newlocale(int category_mask, const char* locale, locale_t) {
|
|
||||||
return _create_locale(category_mask, locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void freelocale(locale_t locale) { _free_locale(locale); }
|
|
||||||
|
|
||||||
static double strtod_l(const char* nptr, char** endptr, _locale_t locale) {
|
|
||||||
return _strtod_l(nptr, endptr, locale);
|
|
||||||
}
|
|
||||||
# endif
|
|
||||||
|
|
||||||
locale_t locale_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using type = locale_t;
|
|
||||||
Locale(const Locale&) = delete;
|
|
||||||
void operator=(const Locale&) = delete;
|
|
||||||
|
|
||||||
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
|
|
||||||
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
|
|
||||||
}
|
|
||||||
~Locale() { freelocale(locale_); }
|
|
||||||
|
|
||||||
type get() const { return locale_; }
|
|
||||||
|
|
||||||
// Converts string to floating-point number and advances str past the end
|
|
||||||
// of the parsed input.
|
|
||||||
double strtod(const char*& str) const {
|
|
||||||
char* end = nullptr;
|
|
||||||
double result = strtod_l(str, &end, locale_);
|
|
||||||
str = end;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif // FMT_LOCALE
|
|
||||||
FMT_END_NAMESPACE
|
|
||||||
|
|
||||||
#endif // FMT_POSIX_H_
|
|
@ -28,7 +28,7 @@ template <bool IsSigned> struct int_checker {
|
|||||||
|
|
||||||
template <> struct int_checker<true> {
|
template <> struct int_checker<true> {
|
||||||
template <typename T> static bool fits_in_int(T value) {
|
template <typename T> static bool fits_in_int(T value) {
|
||||||
return value >= std::numeric_limits<int>::min() &&
|
return value >= (std::numeric_limits<int>::min)() &&
|
||||||
value <= max_value<int>();
|
value <= max_value<int>();
|
||||||
}
|
}
|
||||||
static bool fits_in_int(int) { return true; }
|
static bool fits_in_int(int) { return true; }
|
||||||
@ -303,6 +303,8 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename T> struct printf_formatter {
|
template <typename T> struct printf_formatter {
|
||||||
|
printf_formatter() = delete;
|
||||||
|
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
return ctx.begin();
|
return ctx.begin();
|
||||||
@ -320,6 +322,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
|
|||||||
public:
|
public:
|
||||||
/** The character type for the output. */
|
/** The character type for the output. */
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
|
using iterator = OutputIt;
|
||||||
using format_arg = basic_format_arg<basic_printf_context>;
|
using format_arg = basic_format_arg<basic_printf_context>;
|
||||||
template <typename T> using formatter_type = printf_formatter<T>;
|
template <typename T> using formatter_type = printf_formatter<T>;
|
||||||
|
|
||||||
@ -355,6 +358,8 @@ template <typename OutputIt, typename Char> class basic_printf_context {
|
|||||||
OutputIt out() { return out_; }
|
OutputIt out() { return out_; }
|
||||||
void advance_to(OutputIt it) { out_ = it; }
|
void advance_to(OutputIt it) { out_ = it; }
|
||||||
|
|
||||||
|
internal::locale_ref locale() { return {}; }
|
||||||
|
|
||||||
format_arg arg(int id) const { return args_.get(id); }
|
format_arg arg(int id) const { return args_.get(id); }
|
||||||
|
|
||||||
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
|
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
|
||||||
@ -406,8 +411,9 @@ basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
template <typename OutputIt, typename Char>
|
||||||
int basic_printf_context<OutputIt, Char>::parse_header(
|
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
|
||||||
const Char*& it, const Char* end, format_specs& specs) {
|
const Char* end,
|
||||||
|
format_specs& specs) {
|
||||||
int arg_index = -1;
|
int arg_index = -1;
|
||||||
char_type c = *it;
|
char_type c = *it;
|
||||||
if (c >= '0' && c <= '9') {
|
if (c >= '0' && c <= '9') {
|
||||||
@ -476,8 +482,8 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
|||||||
specs.precision = parse_nonnegative_int(it, end, eh);
|
specs.precision = parse_nonnegative_int(it, end, eh);
|
||||||
} else if (c == '*') {
|
} else if (c == '*') {
|
||||||
++it;
|
++it;
|
||||||
specs.precision =
|
specs.precision = static_cast<int>(
|
||||||
static_cast<int>(visit_format_arg(internal::printf_precision_handler(), get_arg()));
|
visit_format_arg(internal::printf_precision_handler(), get_arg()));
|
||||||
} else {
|
} else {
|
||||||
specs.precision = 0;
|
specs.precision = 0;
|
||||||
}
|
}
|
||||||
@ -596,7 +602,8 @@ inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
|
|||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline std::basic_string<Char> vsprintf(
|
inline std::basic_string<Char> vsprintf(
|
||||||
const S& format, basic_format_args<basic_printf_context_t<Char>> args) {
|
const S& format,
|
||||||
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
printf(buffer, to_string_view(format), args);
|
printf(buffer, to_string_view(format), args);
|
||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
@ -615,12 +622,13 @@ template <typename S, typename... Args,
|
|||||||
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
||||||
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
|
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
|
||||||
using context = basic_printf_context_t<Char>;
|
using context = basic_printf_context_t<Char>;
|
||||||
return vsprintf(to_string_view(format), {make_format_args<context>(args...)});
|
return vsprintf(to_string_view(format), make_format_args<context>(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline int vfprintf(std::FILE* f, const S& format,
|
inline int vfprintf(
|
||||||
basic_format_args<basic_printf_context_t<Char>> args) {
|
std::FILE* f, const S& format,
|
||||||
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
printf(buffer, to_string_view(format), args);
|
printf(buffer, to_string_view(format), args);
|
||||||
std::size_t size = buffer.size();
|
std::size_t size = buffer.size();
|
||||||
@ -643,12 +651,13 @@ template <typename S, typename... Args,
|
|||||||
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
|
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
|
||||||
using context = basic_printf_context_t<Char>;
|
using context = basic_printf_context_t<Char>;
|
||||||
return vfprintf(f, to_string_view(format),
|
return vfprintf(f, to_string_view(format),
|
||||||
{make_format_args<context>(args...)});
|
make_format_args<context>(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline int vprintf(const S& format,
|
inline int vprintf(
|
||||||
basic_format_args<basic_printf_context_t<Char>> args) {
|
const S& format,
|
||||||
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||||
return vfprintf(stdout, to_string_view(format), args);
|
return vfprintf(stdout, to_string_view(format), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -666,12 +675,13 @@ template <typename S, typename... Args,
|
|||||||
inline int printf(const S& format_str, const Args&... args) {
|
inline int printf(const S& format_str, const Args&... args) {
|
||||||
using context = basic_printf_context_t<char_t<S>>;
|
using context = basic_printf_context_t<char_t<S>>;
|
||||||
return vprintf(to_string_view(format_str),
|
return vprintf(to_string_view(format_str),
|
||||||
{make_format_args<context>(args...)});
|
make_format_args<context>(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline int vfprintf(std::basic_ostream<Char>& os, const S& format,
|
inline int vfprintf(
|
||||||
basic_format_args<basic_printf_context_t<Char>> args) {
|
std::basic_ostream<Char>& os, const S& format,
|
||||||
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
printf(buffer, to_string_view(format), args);
|
printf(buffer, to_string_view(format), args);
|
||||||
internal::write(os, buffer);
|
internal::write(os, buffer);
|
||||||
@ -682,9 +692,9 @@ inline int vfprintf(std::basic_ostream<Char>& os, const S& format,
|
|||||||
template <typename ArgFormatter, typename Char,
|
template <typename ArgFormatter, typename Char,
|
||||||
typename Context =
|
typename Context =
|
||||||
basic_printf_context<typename ArgFormatter::iterator, Char>>
|
basic_printf_context<typename ArgFormatter::iterator, Char>>
|
||||||
typename ArgFormatter::iterator vprintf(internal::buffer<Char>& out,
|
typename ArgFormatter::iterator vprintf(
|
||||||
basic_string_view<Char> format_str,
|
internal::buffer<Char>& out, basic_string_view<Char> format_str,
|
||||||
basic_format_args<Context> args) {
|
basic_format_args<type_identity_t<Context>> args) {
|
||||||
typename ArgFormatter::iterator iter(out);
|
typename ArgFormatter::iterator iter(out);
|
||||||
Context(iter, format_str, args).template format<ArgFormatter>();
|
Context(iter, format_str, args).template format<ArgFormatter>();
|
||||||
return iter;
|
return iter;
|
||||||
@ -704,7 +714,7 @@ inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
|
|||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
using context = basic_printf_context_t<Char>;
|
using context = basic_printf_context_t<Char>;
|
||||||
return vfprintf(os, to_string_view(format_str),
|
return vfprintf(os, to_string_view(format_str),
|
||||||
{make_format_args<context>(args...)});
|
make_format_args<context>(args...));
|
||||||
}
|
}
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
@ -12,7 +12,9 @@
|
|||||||
#ifndef FMT_RANGES_H_
|
#ifndef FMT_RANGES_H_
|
||||||
#define FMT_RANGES_H_
|
#define FMT_RANGES_H_
|
||||||
|
|
||||||
|
#include <initializer_list>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
// output only up to N items from the range.
|
// output only up to N items from the range.
|
||||||
@ -104,10 +106,7 @@ struct is_range_<
|
|||||||
/// tuple_size and tuple_element check.
|
/// tuple_size and tuple_element check.
|
||||||
template <typename T> class is_tuple_like_ {
|
template <typename T> class is_tuple_like_ {
|
||||||
template <typename U>
|
template <typename U>
|
||||||
static auto check(U* p)
|
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
||||||
-> decltype(std::tuple_size<U>::value,
|
|
||||||
(void)std::declval<typename std::tuple_element<0, U>::type>(),
|
|
||||||
int());
|
|
||||||
template <typename> static void check(...);
|
template <typename> static void check(...);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -360,6 +359,29 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
|||||||
return {tuple, sep};
|
return {tuple, sep};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Returns an object that formats `initializer_list` with elements separated by
|
||||||
|
`sep`.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
|
||||||
|
// Output: "1, 2, 3"
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
arg_join<internal::iterator_t<const std::initializer_list<T>>, char> join(
|
||||||
|
std::initializer_list<T> list, string_view sep) {
|
||||||
|
return join(std::begin(list), std::end(list), sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
arg_join<internal::iterator_t<const std::initializer_list<T>>, wchar_t> join(
|
||||||
|
std::initializer_list<T> list, wstring_view sep) {
|
||||||
|
return join(std::begin(list), std::end(list), sep);
|
||||||
|
}
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_RANGES_H_
|
#endif // FMT_RANGES_H_
|
||||||
|
175
src/fmt.cpp
175
src/fmt.cpp
@ -1,71 +1,62 @@
|
|||||||
#ifndef SPDLOG_COMPILED_LIB
|
|
||||||
#error Please define SPDLOG_COMPILED_LIB to compile this file.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Slightly modified version of fmt lib's format.cc source file.
|
// Slightly modified version of fmt lib's format.cc source file.
|
||||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef SPDLOG_COMPILED_LIB
|
||||||
|
#error Please define SPDLOG_COMPILED_LIB to compile this file.
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !defined(SPDLOG_FMT_EXTERNAL)
|
#if !defined(SPDLOG_FMT_EXTERNAL)
|
||||||
#include <spdlog/fmt/bundled/format-inl.h>
|
#include <spdlog/fmt/bundled/format-inl.h>
|
||||||
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
int format_float(char *buf, std::size_t size, const char *format, int precision, T value)
|
int format_float(char* buf, std::size_t size, const char* format, int precision,
|
||||||
{
|
T value) {
|
||||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||||
if (precision > 100000)
|
if (precision > 100000)
|
||||||
throw std::runtime_error("fuzz mode - avoid large allocation inside snprintf");
|
throw std::runtime_error(
|
||||||
|
"fuzz mode - avoid large allocation inside snprintf");
|
||||||
#endif
|
#endif
|
||||||
// Suppress the warning about nonliteral format string.
|
// Suppress the warning about nonliteral format string.
|
||||||
auto snprintf_ptr = FMT_SNPRINTF;
|
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
|
||||||
return precision < 0 ? snprintf_ptr(buf, size, format, value) : snprintf_ptr(buf, size, format, precision, value);
|
return precision < 0 ? snprintf_ptr(buf, size, format, value)
|
||||||
|
: snprintf_ptr(buf, size, format, precision, value);
|
||||||
}
|
}
|
||||||
struct sprintf_specs
|
struct sprintf_specs {
|
||||||
{
|
|
||||||
int precision;
|
int precision;
|
||||||
char type;
|
char type;
|
||||||
bool alt : 1;
|
bool alt : 1;
|
||||||
|
|
||||||
template<typename Char>
|
template <typename Char>
|
||||||
constexpr sprintf_specs(basic_format_specs<Char> specs)
|
constexpr sprintf_specs(basic_format_specs<Char> specs)
|
||||||
: precision(specs.precision)
|
: precision(specs.precision), type(specs.type), alt(specs.alt) {}
|
||||||
, type(specs.type)
|
|
||||||
, alt(specs.alt)
|
|
||||||
{}
|
|
||||||
|
|
||||||
constexpr bool has_precision() const
|
constexpr bool has_precision() const { return precision >= 0; }
|
||||||
{
|
|
||||||
return precision >= 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is deprecated and is kept only to preserve ABI compatibility.
|
// This is deprecated and is kept only to preserve ABI compatibility.
|
||||||
template<typename Double>
|
template <typename Double>
|
||||||
char *sprintf_format(Double value, internal::buffer<char> &buf, sprintf_specs specs)
|
char* sprintf_format(Double value, internal::buffer<char>& buf,
|
||||||
{
|
sprintf_specs specs) {
|
||||||
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
|
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
|
||||||
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
|
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
|
||||||
|
|
||||||
// Build format string.
|
// Build format string.
|
||||||
enum
|
enum { max_format_size = 10 }; // longest format: %#-*.*Lg
|
||||||
{
|
|
||||||
max_format_size = 10
|
|
||||||
}; // longest format: %#-*.*Lg
|
|
||||||
char format[max_format_size];
|
char format[max_format_size];
|
||||||
char *format_ptr = format;
|
char* format_ptr = format;
|
||||||
*format_ptr++ = '%';
|
*format_ptr++ = '%';
|
||||||
if (specs.alt || !specs.type)
|
if (specs.alt || !specs.type) *format_ptr++ = '#';
|
||||||
*format_ptr++ = '#';
|
if (specs.precision >= 0) {
|
||||||
if (specs.precision >= 0)
|
|
||||||
{
|
|
||||||
*format_ptr++ = '.';
|
*format_ptr++ = '.';
|
||||||
*format_ptr++ = '*';
|
*format_ptr++ = '*';
|
||||||
}
|
}
|
||||||
if (std::is_same<Double, long double>::value)
|
if (std::is_same<Double, long double>::value) *format_ptr++ = 'L';
|
||||||
*format_ptr++ = 'L';
|
|
||||||
|
|
||||||
char type = specs.type;
|
char type = specs.type;
|
||||||
|
|
||||||
@ -74,66 +65,51 @@ char *sprintf_format(Double value, internal::buffer<char> &buf, sprintf_specs sp
|
|||||||
else if (type == 0 || type == 'n')
|
else if (type == 0 || type == 'n')
|
||||||
type = 'g';
|
type = 'g';
|
||||||
#if FMT_MSC_VER
|
#if FMT_MSC_VER
|
||||||
if (type == 'F')
|
if (type == 'F') {
|
||||||
{
|
// MSVC's printf doesn't support 'F'.
|
||||||
// MSVC's printf doesn't support 'F'.
|
type = 'f';
|
||||||
type = 'f';
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
*format_ptr++ = type;
|
*format_ptr++ = type;
|
||||||
*format_ptr = '\0';
|
*format_ptr = '\0';
|
||||||
|
|
||||||
// Format using snprintf.
|
// Format using snprintf.
|
||||||
char *start = nullptr;
|
char* start = nullptr;
|
||||||
char *decimal_point_pos = nullptr;
|
char* decimal_point_pos = nullptr;
|
||||||
for (;;)
|
for (;;) {
|
||||||
{
|
|
||||||
std::size_t buffer_size = buf.capacity();
|
std::size_t buffer_size = buf.capacity();
|
||||||
start = &buf[0];
|
start = &buf[0];
|
||||||
int result = format_float(start, buffer_size, format, specs.precision, value);
|
int result =
|
||||||
if (result >= 0)
|
format_float(start, buffer_size, format, specs.precision, value);
|
||||||
{
|
if (result >= 0) {
|
||||||
unsigned n = internal::to_unsigned(result);
|
unsigned n = internal::to_unsigned(result);
|
||||||
if (n < buf.capacity())
|
if (n < buf.capacity()) {
|
||||||
{
|
|
||||||
// Find the decimal point.
|
// Find the decimal point.
|
||||||
auto p = buf.data(), end = p + n;
|
auto p = buf.data(), end = p + n;
|
||||||
if (*p == '+' || *p == '-')
|
if (*p == '+' || *p == '-') ++p;
|
||||||
++p;
|
if (specs.type != 'a' && specs.type != 'A') {
|
||||||
if (specs.type != 'a' && specs.type != 'A')
|
while (p < end && *p >= '0' && *p <= '9') ++p;
|
||||||
{
|
if (p < end && *p != 'e' && *p != 'E') {
|
||||||
while (p < end && *p >= '0' && *p <= '9')
|
|
||||||
++p;
|
|
||||||
if (p < end && *p != 'e' && *p != 'E')
|
|
||||||
{
|
|
||||||
decimal_point_pos = p;
|
decimal_point_pos = p;
|
||||||
if (!specs.type)
|
if (!specs.type) {
|
||||||
{
|
|
||||||
// Keep only one trailing zero after the decimal point.
|
// Keep only one trailing zero after the decimal point.
|
||||||
++p;
|
++p;
|
||||||
if (*p == '0')
|
if (*p == '0') ++p;
|
||||||
++p;
|
while (p != end && *p >= '1' && *p <= '9') ++p;
|
||||||
while (p != end && *p >= '1' && *p <= '9')
|
char* where = p;
|
||||||
++p;
|
while (p != end && *p == '0') ++p;
|
||||||
char *where = p;
|
if (p == end || *p < '0' || *p > '9') {
|
||||||
while (p != end && *p == '0')
|
if (p != end) std::memmove(where, p, to_unsigned(end - p));
|
||||||
++p;
|
|
||||||
if (p == end || *p < '0' || *p > '9')
|
|
||||||
{
|
|
||||||
if (p != end)
|
|
||||||
std::memmove(where, p, to_unsigned(end - p));
|
|
||||||
n -= static_cast<unsigned>(p - where);
|
n -= static_cast<unsigned>(p - where);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf.resize(n);
|
buf.resize(n);
|
||||||
break; // The buffer is large enough - continue with formatting.
|
break; // The buffer is large enough - continue with formatting.
|
||||||
}
|
}
|
||||||
buf.reserve(n + 1);
|
buf.reserve(n + 1);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// If result is negative we ask to increase the capacity by at least 1,
|
// If result is negative we ask to increase the capacity by at least 1,
|
||||||
// but as std::vector, the buffer grows exponentially.
|
// but as std::vector, the buffer grows exponentially.
|
||||||
buf.reserve(buf.capacity() + 1);
|
buf.reserve(buf.capacity() + 1);
|
||||||
@ -141,18 +117,23 @@ char *sprintf_format(Double value, internal::buffer<char> &buf, sprintf_specs sp
|
|||||||
}
|
}
|
||||||
return decimal_point_pos;
|
return decimal_point_pos;
|
||||||
}
|
}
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
template FMT_API char *internal::sprintf_format(double, internal::buffer<char> &, sprintf_specs);
|
template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
|
||||||
template FMT_API char *internal::sprintf_format(long double, internal::buffer<char> &, sprintf_specs);
|
sprintf_specs);
|
||||||
|
template FMT_API char* internal::sprintf_format(long double,
|
||||||
|
internal::buffer<char>&,
|
||||||
|
sprintf_specs);
|
||||||
|
|
||||||
template struct FMT_API internal::basic_data<void>;
|
template struct FMT_INSTANTIATION_DEF_API internal::basic_data<void>;
|
||||||
|
|
||||||
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
||||||
int (*instantiate_format_float)(double, int, internal::float_specs, internal::buffer<char> &) = internal::format_float;
|
int (*instantiate_format_float)(double, int, internal::float_specs,
|
||||||
|
internal::buffer<char>&) =
|
||||||
|
internal::format_float;
|
||||||
|
|
||||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
template FMT_API internal::locale_ref::locale_ref(const std::locale &loc);
|
template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
|
||||||
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
|
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -162,18 +143,28 @@ template FMT_API std::string internal::grouping_impl<char>(locale_ref);
|
|||||||
template FMT_API char internal::thousands_sep_impl(locale_ref);
|
template FMT_API char internal::thousands_sep_impl(locale_ref);
|
||||||
template FMT_API char internal::decimal_point_impl(locale_ref);
|
template FMT_API char internal::decimal_point_impl(locale_ref);
|
||||||
|
|
||||||
template FMT_API void internal::buffer<char>::append(const char *, const char *);
|
template FMT_API void internal::buffer<char>::append(const char*, const char*);
|
||||||
|
|
||||||
template FMT_API void internal::arg_map<format_context>::init(const basic_format_args<format_context> &args);
|
template FMT_API void internal::arg_map<format_context>::init(
|
||||||
|
const basic_format_args<format_context>& args);
|
||||||
|
|
||||||
template FMT_API std::string internal::vformat<char>(string_view, basic_format_args<format_context>);
|
template FMT_API std::string internal::vformat<char>(
|
||||||
|
string_view, basic_format_args<format_context>);
|
||||||
|
|
||||||
template FMT_API format_context::iterator internal::vformat_to(internal::buffer<char> &, string_view, basic_format_args<format_context>);
|
template FMT_API format_context::iterator internal::vformat_to(
|
||||||
|
internal::buffer<char>&, string_view, basic_format_args<format_context>);
|
||||||
|
|
||||||
template FMT_API int internal::snprintf_float(double, int, internal::float_specs, internal::buffer<char> &);
|
template FMT_API int internal::snprintf_float(double, int,
|
||||||
template FMT_API int internal::snprintf_float(long double, int, internal::float_specs, internal::buffer<char> &);
|
internal::float_specs,
|
||||||
template FMT_API int internal::format_float(double, int, internal::float_specs, internal::buffer<char> &);
|
internal::buffer<char>&);
|
||||||
template FMT_API int internal::format_float(long double, int, internal::float_specs, internal::buffer<char> &);
|
template FMT_API int internal::snprintf_float(long double, int,
|
||||||
|
internal::float_specs,
|
||||||
|
internal::buffer<char>&);
|
||||||
|
template FMT_API int internal::format_float(double, int, internal::float_specs,
|
||||||
|
internal::buffer<char>&);
|
||||||
|
template FMT_API int internal::format_float(long double, int,
|
||||||
|
internal::float_specs,
|
||||||
|
internal::buffer<char>&);
|
||||||
|
|
||||||
// Explicit instantiations for wchar_t.
|
// Explicit instantiations for wchar_t.
|
||||||
|
|
||||||
@ -181,9 +172,11 @@ template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
|
|||||||
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
|
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
|
||||||
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
|
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
|
||||||
|
|
||||||
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t *, const wchar_t *);
|
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
|
||||||
|
const wchar_t*);
|
||||||
|
|
||||||
template FMT_API std::wstring internal::vformat<wchar_t>(wstring_view, basic_format_args<wformat_context>);
|
template FMT_API std::wstring internal::vformat<wchar_t>(
|
||||||
|
wstring_view, basic_format_args<wformat_context>);
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif
|
#endif // !SPDLOG_FMT_EXTERNAL
|
Loading…
Reference in New Issue
Block a user