Bump fmt version to 6.2.0

This commit is contained in:
gabime 2020-04-15 00:51:03 +03:00
parent c89a5148b2
commit 7698bb0ae1
12 changed files with 1147 additions and 1033 deletions

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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
# 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 # 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(
!is_string<T>::value && !is_char<T>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value &&
(has_formatter<T, Context>::value || (has_formatter<T, Context>::value ||
(has_fallback_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 ||
!std::is_reference<Args>::value)...>::value,
"passing views as lvalues is disallowed"); "passing views as lvalues is disallowed");
check_format_string<remove_const_t<remove_reference_t<Args>>...>(format_str); 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,43 +1737,51 @@ 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>()
? 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...)); 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>()
? 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...)); internal::make_args_checked<Args...>(format_str, args...));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -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
# endif
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
# include <windows.h> # 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)));
} }
return p;
int length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), };
s_size, nullptr, 0); auto p = s.data();
if (length == 0) FMT_THROW(windows_error(GetLastError(), ERROR_MSG)); const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars.
buffer_.resize(length + 1); if (s.size() >= block_size) {
length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, for (auto end = p + s.size() - block_size + 1; p < end;) p = transcode(p);
&buffer_[0], length);
if (length == 0) FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
buffer_[length] = 0;
} }
if (auto num_chars_left = s.data() + s.size() - p) {
FMT_FUNC internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) { char buf[2 * block_size - 1] = {};
if (int error_code = convert(s)) { memcpy(buf, p, to_unsigned(num_chars_left));
FMT_THROW(windows_error(error_code, p = buf;
"cannot convert string from UTF-16 to UTF-8")); do {
p = transcode(p);
} while (p - buf < num_chars_left);
} }
buffer_.push_back(0);
} }
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);
}
}
FMT_CATCH(...) {}
format_error_code(out, error_code, message);
}
#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

View File

@ -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));

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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_

View File

@ -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,8 +65,7 @@ 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';
} }
@ -86,42 +76,30 @@ char *sprintf_format(Double value, internal::buffer<char> &buf, sprintf_specs sp
// 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')
++p;
char* where = p; char* where = p;
while (p != end && *p == '0') while (p != end && *p == '0') ++p;
++p; if (p == end || *p < '0' || *p > '9') {
if (p == end || *p < '0' || *p > '9') if (p != end) std::memmove(where, p, to_unsigned(end - p));
{
if (p != end)
std::memmove(where, p, to_unsigned(end - p));
n -= static_cast<unsigned>(p - where); n -= static_cast<unsigned>(p - where);
} }
} }
@ -131,9 +109,7 @@ char *sprintf_format(Double value, internal::buffer<char> &buf, sprintf_specs sp
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);
@ -143,13 +119,18 @@ char *sprintf_format(Double value, internal::buffer<char> &buf, sprintf_specs sp
} }
} // 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);
@ -164,16 +145,26 @@ 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