diff --git a/include/spdlog/fmt/bundled/chrono.h b/include/spdlog/fmt/bundled/chrono.h index ca4ed30a..421d464a 100644 --- a/include/spdlog/fmt/bundled/chrono.h +++ b/include/spdlog/fmt/bundled/chrono.h @@ -8,14 +8,14 @@ #ifndef FMT_CHRONO_H_ #define FMT_CHRONO_H_ -#include "format.h" -#include "locale.h" - #include #include #include #include +#include "format.h" +#include "locale.h" + FMT_BEGIN_NAMESPACE // 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); break; case 'n': { - const char newline[] = "\n"; + const Char newline[] = {'\n'}; handler.on_text(newline, newline + 1); break; } case 't': { - const char tab[] = "\t"; + const Char tab[] = {'\t'}; handler.on_text(tab, tab + 1); break; } @@ -759,18 +759,30 @@ inline std::chrono::duration get_milliseconds( return std::chrono::duration(static_cast(ms)); } -template -OutputIt format_chrono_duration_value(OutputIt out, Rep val, int precision) { - if (precision >= 0) return format_to(out, "{:.{}f}", val, precision); - return format_to(out, std::is_floating_point::value ? "{:g}" : "{}", +template +OutputIt format_duration_value(OutputIt out, Rep val, int precision) { + const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0}; + 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::value ? fp_f : format, val); } -template -static OutputIt format_chrono_duration_unit(OutputIt out) { - if (const char* unit = get_units()) return format_to(out, "{}", unit); - if (Period::den == 1) return format_to(out, "[{}]s", Period::num); - return format_to(out, "[{}/{}]s", Period::num, Period::den); +template +OutputIt format_duration_unit(OutputIt out) { + if (const char* unit = get_units()) { + string_view s(unit); + if (const_check(std::is_same())) { + 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 (); auto& facet = std::use_facet>(locale); std::basic_ostringstream os; os.imbue(locale); - facet.put(os, os, ' ', &time, format, format + std::strlen(format)); + facet.put(os, os, ' ', &time, format, modifier); auto str = os.str(); std::copy(str.begin(), str.end(), out); } @@ -907,7 +919,7 @@ struct chrono_formatter { if (ns == numeric_system::standard) return write(hour(), 2); auto time = tm(); time.tm_hour = to_nonnegative_int(hour(), 24); - format_localized(time, "%OH"); + format_localized(time, 'H', 'O'); } void on_12_hour(numeric_system ns) { @@ -916,7 +928,7 @@ struct chrono_formatter { if (ns == numeric_system::standard) return write(hour12(), 2); auto time = tm(); time.tm_hour = to_nonnegative_int(hour12(), 12); - format_localized(time, "%OI"); + format_localized(time, 'I', 'O'); } void on_minute(numeric_system ns) { @@ -925,7 +937,7 @@ struct chrono_formatter { if (ns == numeric_system::standard) return write(minute(), 2); auto time = tm(); time.tm_min = to_nonnegative_int(minute(), 60); - format_localized(time, "%OM"); + format_localized(time, 'M', 'O'); } void on_second(numeric_system ns) { @@ -950,13 +962,12 @@ struct chrono_formatter { } auto time = tm(); time.tm_sec = to_nonnegative_int(second(), 60); - format_localized(time, "%OS"); + format_localized(time, 'S', 'O'); } void on_12_hour_time() { if (handle_nan_inf()) return; - - format_localized(time(), "%r"); + format_localized(time(), 'r'); } void on_24_hour_time() { @@ -980,16 +991,18 @@ struct chrono_formatter { void on_am_pm() { if (handle_nan_inf()) return; - format_localized(time(), "%p"); + format_localized(time(), 'p'); } void on_duration_value() { if (handle_nan_inf()) return; write_sign(); - out = format_chrono_duration_value(out, val, precision); + out = format_duration_value(out, val, precision); } - void on_duration_unit() { out = format_chrono_duration_unit(out); } + void on_duration_unit() { + out = format_duration_unit(out); + } }; } // namespace internal @@ -1024,7 +1037,7 @@ struct formatter, Char> { } 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 fill) { f.specs.fill = fill; } void on_align(align_t align) { f.specs.align = align; } void on_width(int width) { f.specs.width = width; } void on_precision(int _precision) { f.precision = _precision; } @@ -1088,8 +1101,8 @@ struct formatter, Char> { internal::handle_dynamic_spec( precision, precision_ref, ctx); if (begin == end || *begin == '}') { - out = internal::format_chrono_duration_value(out, d.count(), precision); - internal::format_chrono_duration_unit(out); + out = internal::format_duration_value(out, d.count(), precision); + internal::format_duration_unit(out); } else { internal::chrono_formatter f( ctx, out, d); diff --git a/include/spdlog/fmt/bundled/color.h b/include/spdlog/fmt/bundled/color.h index 362a95e1..96d9ab6b 100644 --- a/include/spdlog/fmt/bundled/color.h +++ b/include/spdlog/fmt/bundled/color.h @@ -412,7 +412,7 @@ template struct ansi_color_escape { FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT { - return buffer + std::strlen(buffer); + return buffer + std::char_traits::length(buffer); } private: @@ -491,10 +491,8 @@ void vformat_to(basic_memory_buffer& buf, const text_style& ts, internal::make_background_color(ts.get_background()); buf.append(background.begin(), background.end()); } - vformat_to(buf, format_str, args); - if (has_style) { - internal::reset_color(buf); - } + internal::vformat_to(buf, format_str, args); + if (has_style) internal::reset_color(buf); } } // namespace internal @@ -540,7 +538,7 @@ void print(const text_style& ts, const S& format_str, const Args&... args) { template > inline std::basic_string vformat( const text_style& ts, const S& format_str, - basic_format_args> args) { + basic_format_args>> args) { basic_memory_buffer buf; internal::vformat_to(buf, ts, to_string_view(format_str), args); return fmt::to_string(buf); @@ -562,7 +560,7 @@ template > inline std::basic_string format(const text_style& ts, const S& format_str, const Args&... args) { return vformat(ts, to_string_view(format_str), - {internal::make_args_checked(format_str, args...)}); + internal::make_args_checked(format_str, args...)); } FMT_END_NAMESPACE diff --git a/include/spdlog/fmt/bundled/compile.h b/include/spdlog/fmt/bundled/compile.h index 5829f623..e4b12f34 100644 --- a/include/spdlog/fmt/bundled/compile.h +++ b/include/spdlog/fmt/bundled/compile.h @@ -9,6 +9,7 @@ #define FMT_COMPILE_H_ #include + #include "format.h" FMT_BEGIN_NAMESPACE @@ -350,6 +351,8 @@ template struct get_type_impl> { template using get_type = typename get_type_impl::type; +template struct is_compiled_format : std::false_type {}; + template struct text { basic_string_view data; using char_type = Char; @@ -361,6 +364,9 @@ template struct text { } }; +template +struct is_compiled_format> : std::true_type {}; + template constexpr text make_text(basic_string_view s, size_t pos, size_t size) { @@ -406,6 +412,9 @@ template struct field { } }; +template +struct is_compiled_format> : std::true_type {}; + template struct concat { L lhs; R rhs; @@ -418,6 +427,9 @@ template struct concat { } }; +template +struct is_compiled_format> : std::true_type {}; + template constexpr concat make_concat(L lhs, R rhs) { return {lhs, rhs}; @@ -508,8 +520,7 @@ constexpr auto compile(S format_str) { template ::value)> + FMT_ENABLE_IF(internal::is_compiled_format::value)> std::basic_string format(const CompiledFormat& cf, const Args&... args) { basic_memory_buffer buffer; cf.format(std::back_inserter(buffer), args...); @@ -517,8 +528,7 @@ std::basic_string format(const CompiledFormat& cf, const Args&... args) { } template ::value)> + FMT_ENABLE_IF(internal::is_compiled_format::value)> OutputIt format_to(OutputIt out, const CompiledFormat& cf, const Args&... args) { return cf.format(out, args...); @@ -549,7 +559,7 @@ std::basic_string format(const CompiledFormat& cf, const Args&... args) { using range = buffer_range; using context = buffer_context; internal::cf::vformat_to(range(buffer), cf, - {make_format_args(args...)}); + make_format_args(args...)); return to_string(buffer); } @@ -561,8 +571,8 @@ OutputIt format_to(OutputIt out, const CompiledFormat& cf, using char_type = typename CompiledFormat::char_type; using range = internal::output_range; using context = format_context_t; - return internal::cf::vformat_to( - range(out), cf, {make_format_args(args...)}); + return internal::cf::vformat_to(range(out), cf, + make_format_args(args...)); } template // std::FILE #include +#include #include +#include #include #include +#include // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 60102 +#define FMT_VERSION 60200 #ifdef __has_feature # define FMT_HAS_FEATURE(x) __has_feature(x) @@ -36,6 +39,18 @@ # define FMT_HAS_CPP_ATTRIBUTE(x) 0 #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__) # define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) #else @@ -117,16 +132,25 @@ # endif #endif -// [[noreturn]] is disabled on MSVC because of bogus unreachable code warnings. -#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER +// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code +// warnings. +#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \ + !FMT_NVCC # define FMT_NORETURN [[noreturn]] #else # define FMT_NORETURN #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 -# if (FMT_HAS_CPP_ATTRIBUTE(deprecated) && __cplusplus >= 201402L) || \ - FMT_MSC_VER >= 1900 +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 # define FMT_DEPRECATED [[deprecated]] # else # if defined(__GNUC__) || defined(__clang__) @@ -139,8 +163,8 @@ # endif #endif -// Workaround broken [[deprecated]] in the Intel compiler and NVCC. -#if defined(__INTEL_COMPILER) || FMT_NVCC +// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers. +#if defined(__INTEL_COMPILER) || defined(__PGI) || FMT_NVCC # define FMT_DEPRECATED_ALIAS #else # define FMT_DEPRECATED_ALIAS FMT_DEPRECATED @@ -166,6 +190,12 @@ #endif #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 # define FMT_API __declspec(dllexport) # elif defined(FMT_SHARED) @@ -173,12 +203,24 @@ # define FMT_EXTERN_TEMPLATE_API FMT_API # endif #endif +#ifndef FMT_CLASS_API +# define FMT_CLASS_API +#endif #ifndef FMT_API -# define FMT_API +# if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_API __attribute__((visibility("default"))) +# define FMT_EXTERN_TEMPLATE_API FMT_API +# define FMT_INSTANTIATION_DEF_API +# else +# define FMT_API +# endif #endif #ifndef FMT_EXTERN_TEMPLATE_API # define FMT_EXTERN_TEMPLATE_API #endif +#ifndef FMT_INSTANTIATION_DEF_API +# define FMT_INSTANTIATION_DEF_API FMT_API +#endif #ifndef FMT_HEADER_ONLY # define FMT_EXTERN extern @@ -197,9 +239,16 @@ # define FMT_USE_EXPERIMENTAL_STRING_VIEW #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 -// 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 using enable_if_t = typename std::enable_if::type; template @@ -211,6 +260,8 @@ template using remove_const_t = typename std::remove_const::type; template using remove_cvref_t = typename std::remove_cv>::type; +template struct type_identity { using type = T; }; +template using type_identity_t = typename type_identity::type; struct monostate {}; @@ -221,19 +272,25 @@ struct monostate {}; namespace internal { +// A helper function to suppress bogus "conditional expression is constant" +// warnings. +template FMT_CONSTEXPR T const_check(T value) { return value; } + // A workaround for gcc 4.8 to make void_t work in a SFINAE context. template 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 # 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 -# define FMT_ASSERT(condition, message) \ - ((condition) \ - ? void() \ - : fmt::internal::assert_fail(__FILE__, __LINE__, (message))) +# define FMT_ASSERT(condition, message) \ + ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ + ? (void)0 \ + : ::fmt::internal::assert_fail(__FILE__, __LINE__, (message))) # endif #endif @@ -248,7 +305,7 @@ template struct std_string_view {}; #ifdef FMT_USE_INT128 // Do nothing. -#elif defined(__SIZEOF_INT128__) +#elif defined(__SIZEOF_INT128__) && !FMT_NVCC # define FMT_USE_INT128 1 using int128_t = __int128_t; using uint128_t = __uint128_t; @@ -266,6 +323,19 @@ FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) { FMT_ASSERT(value >= 0, "negative value"); return static_cast::type>(value); } + +constexpr unsigned char micro[] = "\u00B5"; + +template 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 template @@ -284,7 +354,8 @@ template class basic_string_view { size_t size_; public: - using char_type = Char; + using char_type FMT_DEPRECATED_ALIAS = Char; + using value_type = Char; using iterator = const Char*; FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {} @@ -300,6 +371,9 @@ template class basic_string_view { the size with ``std::char_traits::length``. \endrst */ +#if __cplusplus >= 201703L // C++17's char_traits::length() is constexpr. + FMT_CONSTEXPR +#endif basic_string_view(const Char* s) : data_(s), size_(std::char_traits::length(s)) {} @@ -365,15 +439,15 @@ using string_view = basic_string_view; using wstring_view = basic_string_view; #ifndef __cpp_char8_t -// A UTF-8 code unit type. -enum char8_t : unsigned char {}; +// char8_t is deprecated; use char instead. +using char8_t FMT_DEPRECATED_ALIAS = internal::char8_type; #endif /** Specifies if ``T`` is a character type. Can be specialized by users. */ template struct is_char : std::false_type {}; template <> struct is_char : std::true_type {}; template <> struct is_char : std::true_type {}; -template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; template <> struct is_char : std::true_type {}; template <> struct is_char : std::true_type {}; @@ -442,7 +516,7 @@ struct is_string : std::is_class()))> { template struct char_t_impl {}; template struct char_t_impl::value>> { using result = decltype(to_string_view(std::declval())); - using type = typename result::char_type; + using type = typename result::value_type; }; struct error_handler { @@ -603,6 +677,9 @@ template class buffer { T* begin() FMT_NOEXCEPT { return ptr_; } 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. */ std::size_t size() const FMT_NOEXCEPT { return size_; } @@ -639,8 +716,10 @@ template class buffer { /** Appends data to the end of the buffer. */ template void append(const U* begin, const U* end); - T& operator[](std::size_t index) { return ptr_[index]; } - const T& operator[](std::size_t index) const { return ptr_[index]; } + template T& operator[](I index) { return ptr_[index]; } + template const T& operator[](I index) const { + return ptr_[index]; + } }; // A container-backed buffer. @@ -684,7 +763,7 @@ using has_fallback_formatter = template struct named_arg_base; template struct named_arg; -enum type { +enum class type { none_type, named_arg_type, // Integer types should go first, @@ -710,11 +789,12 @@ enum type { // Maps core type T to the corresponding type enum constant. template -struct type_constant : std::integral_constant {}; +struct type_constant : std::integral_constant {}; #define FMT_TYPE_CONSTANT(Type, constant) \ template \ - struct type_constant : std::integral_constant {} + struct type_constant \ + : std::integral_constant {} FMT_TYPE_CONSTANT(const named_arg_base&, named_arg_type); FMT_TYPE_CONSTANT(int, int_type); @@ -733,13 +813,13 @@ FMT_TYPE_CONSTANT(basic_string_view, string_type); FMT_TYPE_CONSTANT(const void*, pointer_type); FMT_CONSTEXPR bool is_integral_type(type t) { - FMT_ASSERT(t != named_arg_type, "invalid argument type"); - return t > none_type && t <= last_integer_type; + FMT_ASSERT(t != type::named_arg_type, "invalid argument type"); + return t > type::none_type && t <= type::last_integer_type; } FMT_CONSTEXPR bool is_arithmetic_type(type t) { - FMT_ASSERT(t != named_arg_type, "invalid argument type"); - return t > none_type && t <= last_numeric_type; + FMT_ASSERT(t != type::named_arg_type, "invalid argument type"); + return t > type::none_type && t <= type::last_numeric_type; } template struct string_value { @@ -869,7 +949,8 @@ template struct arg_mapper { template , T>::value && - !is_string::value)> + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> FMT_CONSTEXPR basic_string_view map(const T& val) { return basic_string_view(val); } @@ -878,7 +959,8 @@ template struct arg_mapper { FMT_ENABLE_IF( std::is_constructible, T>::value && !std::is_constructible, T>::value && - !is_string::value && !has_formatter::value)> + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> FMT_CONSTEXPR basic_string_view map(const T& val) { return std_string_view(val); } @@ -907,18 +989,15 @@ template struct arg_mapper { FMT_ENABLE_IF(std::is_enum::value && !has_formatter::value && !has_fallback_formatter::value)> - FMT_CONSTEXPR auto map(const T& val) -> decltype( - map(static_cast::type>(val))) { + FMT_CONSTEXPR auto map(const T& val) + -> decltype(std::declval().map( + static_cast::type>(val))) { return map(static_cast::type>(val)); } - template < - typename T, - FMT_ENABLE_IF( - !is_string::value && !is_char::value && - !std::is_constructible, T>::value && - (has_formatter::value || - (has_fallback_formatter::value && - !std::is_constructible, T>::value)))> + template ::value && !is_char::value && + (has_formatter::value || + has_fallback_formatter::value))> FMT_CONSTEXPR const T& map(const T& val) { return val; } @@ -930,6 +1009,16 @@ template struct arg_mapper { std::memcpy(val.data, &arg, sizeof(arg)); return val; } + + int map(...) { + constexpr bool formattable = sizeof(Context) == 0; + static_assert( + formattable, + "Cannot format argument. To make type T formattable provide a " + "formatter specialization: " + "https://fmt.dev/latest/api.html#formatting-user-defined-types"); + return 0; + } }; // A type constant after applying arg_mapper. @@ -981,10 +1070,10 @@ template class basic_format_arg { internal::custom_value 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 { - return type_ != internal::none_type; + return type_ != internal::type::none_type; } internal::type type() const { return type_; } @@ -1006,47 +1095,47 @@ FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, -> decltype(vis(0)) { using char_type = typename Context::char_type; switch (arg.type_) { - case internal::none_type: + case internal::type::none_type: break; - case internal::named_arg_type: + case internal::type::named_arg_type: FMT_ASSERT(false, "invalid argument type"); break; - case internal::int_type: + case internal::type::int_type: return vis(arg.value_.int_value); - case internal::uint_type: + case internal::type::uint_type: return vis(arg.value_.uint_value); - case internal::long_long_type: + case internal::type::long_long_type: 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); #if FMT_USE_INT128 - case internal::int128_type: + case internal::type::int128_type: return vis(arg.value_.int128_value); - case internal::uint128_type: + case internal::type::uint128_type: return vis(arg.value_.uint128_value); #else - case internal::int128_type: - case internal::uint128_type: + case internal::type::int128_type: + case internal::type::uint128_type: break; #endif - case internal::bool_type: + case internal::type::bool_type: return vis(arg.value_.bool_value); - case internal::char_type: + case internal::type::char_type: return vis(arg.value_.char_value); - case internal::float_type: + case internal::type::float_type: return vis(arg.value_.float_value); - case internal::double_type: + case internal::type::double_type: return vis(arg.value_.double_value); - case internal::long_double_type: + case internal::type::long_double_type: return vis(arg.value_.long_double_value); - case internal::cstring_type: + case internal::type::cstring_type: return vis(arg.value_.string.data); - case internal::string_type: + case internal::type::string_type: return vis(basic_string_view(arg.value_.string.data, arg.value_.string.size)); - case internal::pointer_type: + case internal::type::pointer_type: return vis(arg.value_.pointer); - case internal::custom_type: + case internal::type::custom_type: return vis(typename basic_format_arg::handle(arg.value_.custom)); } return vis(monostate()); @@ -1106,7 +1195,7 @@ template constexpr unsigned long long encode_types() { return 0; } template constexpr unsigned long long encode_types() { - return mapped_type_constant::value | + return static_cast(mapped_type_constant::value) | (encode_types() << packed_arg_bits); } @@ -1129,6 +1218,43 @@ template make_arg(const T& value) { return make_arg(value); } + +template struct is_reference_wrapper : std::false_type {}; + +template +struct is_reference_wrapper> : 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 struct node { + virtual ~node() = default; + std::unique_ptr> next; + }; + + template struct typed_node : node<> { + T value; + + template + FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} + + template + FMT_CONSTEXPR typed_node(const basic_string_view& arg) + : value(arg.data(), arg.size()) {} + }; + + std::unique_ptr> head_; + + public: + template const T& push(const Arg& arg) { + auto node = std::unique_ptr>(new typed_node(arg)); + auto& value = node->value; + node->next = std::move(head_); + head_ = std::move(node); + return value; + } +}; } // namespace internal // Formatting context. @@ -1191,7 +1317,13 @@ using wformat_context = buffer_context; such as `~fmt::vformat`. \endrst */ -template class format_arg_store { +template +class format_arg_store +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ private: static const size_t num_args = sizeof...(Args); static const bool is_packed = num_args < internal::max_packed_args; @@ -1210,7 +1342,12 @@ template class format_arg_store { : internal::is_unpacked_bit | num_args; format_arg_store(const Args&... args) - : data_{internal::make_arg(args)...} {} + : +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + basic_format_args(*this), +#endif + data_{internal::make_arg(args)...} { + } }; /** @@ -1227,7 +1364,112 @@ inline format_arg_store make_format_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 +class dynamic_format_arg_store +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ + private: + using char_type = typename Context::char_type; + + template struct need_copy { + static constexpr internal::type mapped_type = + internal::mapped_type_constant::value; + + enum { + value = !(internal::is_reference_wrapper::value || + std::is_same>::value || + std::is_same>::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 + using stored_type = conditional_t::value, + std::basic_string, T>; + + // Storage of basic_format_arg must be contiguous. + std::vector> 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; + + unsigned long long get_types() const { + return internal::is_unpacked_bit | data_.size(); + } + + template void emplace_arg(const T& arg) { + data_.emplace_back(internal::make_arg(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 store; + store.push_back(42); + store.push_back("abc"); + store.push_back(1.5f); + std::string result = fmt::vformat("{} and {} and {}", store); + \endrst + */ + template void push_back(const T& arg) { + static_assert( + !std::is_base_of, T>::value, + "named arguments are not supported yet"); + if (internal::const_check(need_copy::value)) + emplace_arg(dynamic_args_.push>(arg)); + else + emplace_arg(arg); + } + + /** + Adds a reference to the argument into the dynamic store for later passing to + a formating function. + */ + template void push_back(std::reference_wrapper arg) { + static_assert( + need_copy::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 class basic_format_args { public: using size_type = int; @@ -1269,7 +1511,7 @@ template class basic_format_args { } if (index > internal::max_packed_args) return arg; arg.type_ = type(index); - if (arg.type_ == internal::none_type) return arg; + if (arg.type_ == internal::type::none_type) return arg; internal::value& val = arg.value_; val = values_[index]; return arg; @@ -1289,6 +1531,17 @@ template class basic_format_args { 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& store) + : types_(store.get_types()) { + set_data(store.data_.data()); + } + /** \rst Constructs a `basic_format_args` object from a dynamic set of arguments. @@ -1302,7 +1555,7 @@ template class basic_format_args { /** Returns the argument at specified index. */ format_arg get(int index) const { 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(); return arg; } @@ -1319,12 +1572,12 @@ template class basic_format_args { struct format_args : basic_format_args { template format_args(Args&&... args) - : basic_format_args(std::forward(args)...) {} + : basic_format_args(static_cast(args)...) {} }; struct wformat_args : basic_format_args { template wformat_args(Args&&... args) - : basic_format_args(std::forward(args)...) {} + : basic_format_args(static_cast(args)...) {} }; template struct is_contiguous : std::false_type {}; @@ -1358,7 +1611,10 @@ template struct named_arg_base { } }; -template struct named_arg : named_arg_base { +struct view {}; + +template +struct named_arg : view, named_arg_base { const T& value; named_arg(basic_string_view name, const T& val) @@ -1376,7 +1632,6 @@ inline void check_format_string(const S&) { template ::value)> void check_format_string(S); -struct view {}; template struct bool_pack; template using all_true = @@ -1386,32 +1641,38 @@ template > inline format_arg_store, remove_reference_t...> make_args_checked(const S& format_str, const remove_reference_t&... args) { - static_assert(all_true<(!std::is_base_of>() || - !std::is_reference())...>::value, - "passing views as lvalues is disallowed"); - check_format_string>...>(format_str); + static_assert( + all_true<(!std::is_base_of>::value || + !std::is_reference::value)...>::value, + "passing views as lvalues is disallowed"); + check_format_string(format_str); return {args...}; } template -std::basic_string vformat(basic_string_view format_str, - basic_format_args> args); +std::basic_string vformat( + basic_string_view format_str, + basic_format_args>> args); template typename buffer_context::iterator vformat_to( buffer& buf, basic_string_view format_str, - basic_format_args> args); + basic_format_args>> args); + +template ::value)> +inline void vprint_mojibake(std::FILE*, basic_string_view, 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 /** \rst - Returns a named argument to be used in 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. + Returns a named argument to be used in a formatting function. It should only + be used in a call to a formatting function. **Example**:: @@ -1434,8 +1695,9 @@ void arg(S, internal::named_arg) = delete; template , FMT_ENABLE_IF( internal::is_contiguous_back_insert_iterator::value)> -OutputIt vformat_to(OutputIt out, const S& format_str, - basic_format_args> args) { +OutputIt vformat_to( + OutputIt out, const S& format_str, + basic_format_args>> args) { using container = remove_reference_t; internal::container_buffer buf((internal::get_container(out))); internal::vformat_to(buf, to_string_view(format_str), args); @@ -1448,14 +1710,14 @@ template format_to( std::back_insert_iterator out, const S& format_str, Args&&... args) { - return vformat_to( - out, to_string_view(format_str), - {internal::make_args_checked(format_str, args...)}); + return vformat_to(out, to_string_view(format_str), + internal::make_args_checked(format_str, args...)); } template > inline std::basic_string vformat( - const S& format_str, basic_format_args> args) { + const S& format_str, + basic_format_args>> args) { return internal::vformat(to_string_view(format_str), args); } @@ -1475,44 +1737,52 @@ template > inline std::basic_string format(const S& format_str, Args&&... args) { return internal::vformat( to_string_view(format_str), - {internal::make_args_checked(format_str, args...)}); + internal::make_args_checked(format_str, args...)); } -FMT_API void vprint(std::FILE* f, string_view format_str, format_args args); -FMT_API void vprint(string_view format_str, format_args args); +FMT_API void vprint(string_view, format_args); +FMT_API void vprint(std::FILE*, string_view, format_args); /** \rst - Prints formatted data to the file *f*. For wide format strings, - *f* should be in wide-oriented mode set via ``fwide(f, 1)`` or - ``_setmode(_fileno(f), _O_U8TEXT)`` on Windows. + Formats ``args`` according to specifications in ``format_str`` and writes the + output to the file ``f``. Strings are assumed to be Unicode-encoded unless the + ``FMT_UNICODE`` macro is set to 0. **Example**:: fmt::print(stderr, "Don't {}!", "panic"); \endrst */ -template ::value)> +template > inline void print(std::FILE* f, const S& format_str, Args&&... args) { - vprint(f, to_string_view(format_str), - internal::make_args_checked(format_str, args...)); + return internal::is_unicode() + ? vprint(f, to_string_view(format_str), + internal::make_args_checked(format_str, args...)) + : internal::vprint_mojibake( + f, to_string_view(format_str), + internal::make_args_checked(format_str, args...)); } /** \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**:: fmt::print("Elapsed time: {0:.2f} seconds", 1.23); \endrst */ -template ::value)> +template > inline void print(const S& format_str, Args&&... args) { - vprint(to_string_view(format_str), - internal::make_args_checked(format_str, args...)); + return internal::is_unicode() + ? vprint(to_string_view(format_str), + internal::make_args_checked(format_str, args...)) + : internal::vprint_mojibake( + stdout, to_string_view(format_str), + internal::make_args_checked(format_str, args...)); } FMT_END_NAMESPACE diff --git a/include/spdlog/fmt/bundled/format-inl.h b/include/spdlog/fmt/bundled/format-inl.h index 72b30466..f632714d 100644 --- a/include/spdlog/fmt/bundled/format-inl.h +++ b/include/spdlog/fmt/bundled/format-inl.h @@ -8,8 +8,6 @@ #ifndef FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_ -#include "format.h" - #include #include #include @@ -17,29 +15,15 @@ #include #include // for std::memmove #include + +#include "format.h" #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) # include #endif -#if FMT_USE_WINDOWS_H -# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN) -# define WIN32_LEAN_AND_MEAN -# endif -# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) -# include -# else -# define NOMINMAX -# include -# 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) +#ifdef _WIN32 +# include +# include #endif #ifdef _MSC_VER @@ -73,8 +57,6 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) { # define FMT_SNPRINTF fmt_snprintf #endif // _MSC_VER -using format_func = void (*)(internal::buffer&, int, string_view); - // A portable thread-safe version of strerror. // Sets buffer to point to a string describing the error code. // 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. + FMT_MAYBE_UNUSED int handle(char* message) { // If the buffer is full then the message is probably truncated. 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. + FMT_MAYBE_UNUSED int handle(internal::null<>) { return fallback(strerror_s(buffer_, buffer_size_, error_code_)); } // Fallback to strerror_s when strerror_r is not available. + FMT_MAYBE_UNUSED int fallback(int result) { // If the buffer is full then the message is probably truncated. return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE @@ -168,15 +153,6 @@ FMT_FUNC void format_error_code(internal::buffer& out, int error_code, 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, string_view message) FMT_NOEXCEPT { 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); 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 #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) @@ -356,6 +339,10 @@ class fp { private: using significand_type = uint64_t; + public: + significand_type f; + int e; + // All sizes are in bits. // Subtract 1 to account for an implicit most significant bit in the // normalized form. @@ -363,11 +350,6 @@ class fp { std::numeric_limits::digits - 1; static FMT_CONSTEXPR_DECL const uint64_t implicit_bit = 1ULL << double_significand_size; - - public: - significand_type f; - int e; - static FMT_CONSTEXPR_DECL const int significand_size = bits::value; @@ -378,22 +360,6 @@ class fp { // errors on platforms where double is not IEEE754. template explicit fp(Double d) { assign(d); } - // Normalizes the value converted from double and multiplied by (1 << SHIFT). - template 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. template bool assign(Double d) { @@ -406,7 +372,8 @@ class fp { const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; auto u = bit_cast(d); f = u & significand_mask; - auto biased_e = (u & exponent_mask) >> double_significand_size; + int biased_e = + static_cast((u & exponent_mask) >> double_significand_size); // Predecessor is closer if d is a normalized power of 2 (f == 0) other than // the smallest normalized number (biased_e > 1). bool is_predecessor_closer = f == 0 && biased_e > 1; @@ -414,7 +381,7 @@ class fp { f += implicit_bit; else biased_e = 1; // Subnormals use biased exponent 1 (min exponent). - e = static_cast(biased_e - exponent_bias - double_significand_size); + e = biased_e - exponent_bias - double_significand_size; return is_predecessor_closer; } @@ -453,6 +420,22 @@ class fp { } }; +// Normalizes the value converted from double and multiplied by (1 << SHIFT). +template fp normalize(fp value) { + // Handle subnormals. + const auto shifted_implicit_bit = fp::implicit_bit << SHIFT; + while ((value.f & shifted_implicit_bit) == 0) { + value.f <<= 1; + --value.e; + } + // Subtract 1 to account for hidden bit. + const auto offset = + fp::significand_size - fp::double_significand_size - SHIFT - 1; + value.f <<= offset; + value.e -= offset; + return value; +} + inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; } // Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. @@ -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 // (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. -FMT_FUNC fp get_cached_power(int min_exponent, int& pow10_exponent) { - const uint64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10)) +inline fp get_cached_power(int min_exponent, int& pow10_exponent) { + const int64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10)) int index = static_cast( - static_cast( - (min_exponent + fp::significand_size - 1) * one_over_log2_10 + - ((uint64_t(1) << 32) - 1) // ceil - ) >> - 32 // arithmetic shift + ((min_exponent + fp::significand_size - 1) * one_over_log2_10 + + ((int64_t(1) << 32) - 1)) // ceil + >> 32 // arithmetic shift ); // Decimal exponent of the first (smallest) cached power of 10. const int first_dec_exp = -348; @@ -526,20 +507,23 @@ class bigint { basic_memory_buffer bigits_; 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::value; friend struct formatter; void subtract_bigits(int index, bigit other, bigit& borrow) { - auto result = static_cast(bigits_[index]) - other - borrow; - bigits_[index] = static_cast(result); + auto result = static_cast((*this)[index]) - other - borrow; + (*this)[index] = static_cast(result); borrow = static_cast(result >> (bigit_bits * 2 - 1)); } void remove_leading_zeros() { int num_bigits = static_cast(bigits_.size()) - 1; - while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits; - bigits_.resize(num_bigits + 1); + while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; + bigits_.resize(to_unsigned(num_bigits + 1)); } // Computes *this -= other assuming aligned bigints and *this >= other. @@ -548,8 +532,7 @@ class bigint { FMT_ASSERT(compare(*this, other) >= 0, ""); bigit borrow = 0; int i = other.exp_ - exp_; - for (int j = 0, n = static_cast(other.bigits_.size()); j != n; - ++i, ++j) { + for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) { subtract_bigits(i, other.bigits_[j], borrow); } while (borrow > 0) subtract_bigits(i, 0, borrow); @@ -600,7 +583,7 @@ class bigint { } void assign(uint64_t n) { - int num_bigits = 0; + size_t num_bigits = 0; do { bigits_[num_bigits++] = n & ~bigit(0); n >>= bigit_bits; @@ -641,7 +624,7 @@ class bigint { int end = i - j; if (end < 0) end = 0; 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 (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 > num_rhs_bigits) return 1; 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; int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_); @@ -696,7 +679,7 @@ class bigint { basic_memory_buffer n(std::move(bigits_)); int num_bigits = static_cast(bigits_.size()); int num_result_bigits = 2 * num_bigits; - bigits_.resize(num_result_bigits); + bigits_.resize(to_unsigned(num_result_bigits)); using accumulator_t = conditional_t; auto sum = accumulator_t(); 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. sum += static_cast(n[i]) * n[j]; } - bigits_[bigit_index] = static_cast(sum); + (*this)[bigit_index] = static_cast(sum); sum >>= bits::value; // Compute the carry. } // Do the same for the top half. @@ -714,7 +697,7 @@ class bigint { ++bigit_index) { for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) sum += static_cast(n[i++]) * n[j--]; - bigits_[bigit_index] = static_cast(sum); + (*this)[bigit_index] = static_cast(sum); sum >>= bits::value; } --num_result_bigits; @@ -728,11 +711,11 @@ class bigint { FMT_ASSERT(this != &divisor, ""); if (compare(*this, divisor) < 0) return 0; int num_bigits = static_cast(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_; if (exp_difference > 0) { // 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) bigits_[j] = bigits_[i]; 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 // 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. // Round down if (remainder + error) * 2 <= divisor. if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) - return down; + return round_direction::down; // Round up if (remainder - error) * 2 >= divisor. if (remainder >= error && remainder - error >= divisor - (remainder - error)) { - return up; + return round_direction::up; } - return unknown; + return round_direction::unknown; } 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. // error: the size of the region (lower, upper) outside of which numbers // 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, ""); // The fractional part of scaled value (p2 in Grisu) c = value % one. 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. auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e, value.f / 10, error * 10, exp); @@ -882,8 +879,8 @@ struct fixed_handler { if (precision > 0) return digits::more; if (precision < 0) return digits::done; auto dir = get_round_direction(divisor, remainder, error); - if (dir == unknown) return digits::error; - buf[size++] = dir == up ? '1' : '0'; + if (dir == round_direction::unknown) return digits::error; + buf[size++] = dir == round_direction::up ? '1' : '0'; return digits::done; } @@ -901,7 +898,8 @@ struct fixed_handler { FMT_ASSERT(error == 1 && divisor > 2, ""); } 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]; for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { buf[i] = '0'; @@ -1028,7 +1026,7 @@ void fallback_format(Double d, buffer& buf, int& exp10) { if (result > 0 || (result == 0 && (digit % 2) != 0)) ++data[num_digits - 1]; } - buf.resize(num_digits); + buf.resize(to_unsigned(num_digits)); exp10 -= num_digits - 1; return; } @@ -1043,7 +1041,7 @@ void fallback_format(Double d, buffer& buf, int& exp10) { // if T is a IEEE754 binary32 or binary64 and snprintf otherwise. template int format_float(T value, int precision, float_specs specs, buffer& buf) { - static_assert(!std::is_same(), ""); + static_assert(!std::is_same::value, ""); FMT_ASSERT(value >= 0, "value is negative"); const bool fixed = specs.format == float_format::fixed; @@ -1062,25 +1060,7 @@ int format_float(T value, int precision, float_specs specs, buffer& buf) { int exp = 0; const int min_exp = -60; // alpha in Grisu. int cached_exp10 = 0; // K in Grisu. - if (precision != -1) { - 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 { + if (precision < 0) { fp fp_value; auto boundaries = specs.binary32 ? fp_value.assign_float_with_boundaries(value) @@ -1109,6 +1089,24 @@ int format_float(T value, int precision, float_specs specs, buffer& buf) { return exp; } 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; } @@ -1118,7 +1116,7 @@ int snprintf_float(T value, int precision, float_specs specs, buffer& buf) { // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); - static_assert(!std::is_same(), ""); + static_assert(!std::is_same::value, ""); // Subtract 1 to account for the difference in precision since we use %e for // 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_ptr = format; *format_ptr++ = '%'; - if (specs.trailing_zeros) *format_ptr++ = '#'; + if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#'; if (precision >= 0) { *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"); #endif // 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 ? snprintf_ptr(begin, capacity, format, precision, 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. continue; } - unsigned size = to_unsigned(result); + auto size = to_unsigned(result); // Size equal to capacity means that the last character was truncated. if (size >= capacity) { 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; } while (is_digit(*p)); int fraction_size = static_cast(end - p - 1); - std::memmove(p, p + 1, fraction_size); + std::memmove(p, p + 1, to_unsigned(fraction_size)); buf.resize(size - 1); return -fraction_size; } @@ -1208,12 +1207,67 @@ int snprintf_float(T value, int precision, float_specs specs, while (*fraction_end == '0') --fraction_end; // Move the fractional part left to get rid of the decimal point. fraction_size = static_cast(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; } } + +// 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(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 template <> struct formatter { @@ -1226,7 +1280,7 @@ template <> struct formatter { auto out = ctx.out(); bool first = true; for (auto i = n.bigits_.size(); i > 0; --i) { - auto value = n.bigits_[i - 1]; + auto value = n.bigits_[i - 1u]; if (first) { out = format_to(out, "{:x}", value); first = false; @@ -1240,101 +1294,37 @@ template <> struct formatter { } }; -#if FMT_USE_WINDOWS_H - 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"; - if (s.size() > INT_MAX) - FMT_THROW(windows_error(ERROR_INVALID_PARAMETER, ERROR_MSG)); - int s_size = static_cast(s.size()); - if (s_size == 0) { - // MultiByteToWideChar does not support zero length, handle separately. - buffer_.resize(1); - buffer_[0] = 0; - return; - } - - int length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), - s_size, nullptr, 0); - if (length == 0) FMT_THROW(windows_error(GetLastError(), ERROR_MSG)); - buffer_.resize(length + 1); - length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, - &buffer_[0], length); - if (length == 0) FMT_THROW(windows_error(GetLastError(), ERROR_MSG)); - buffer_[length] = 0; -} - -FMT_FUNC internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) { - if (int error_code = convert(s)) { - FMT_THROW(windows_error(error_code, - "cannot convert string from UTF-16 to UTF-8")); - } -} - -FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s) { - if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; - int s_size = static_cast(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& 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(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); + auto transcode = [this](const char* p) { + auto cp = uint32_t(); + auto error = 0; + p = utf8_decode(p, &cp, &error); + if (error != 0) FMT_THROW(std::runtime_error("invalid utf8")); + if (cp <= 0xFFFF) { + buffer_.push_back(static_cast(cp)); + } else { + cp -= 0x10000; + buffer_.push_back(static_cast(0xD800 + (cp >> 10))); + buffer_.push_back(static_cast(0xDC00 + (cp & 0x3FF))); } + return p; + }; + auto p = s.data(); + const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. + if (s.size() >= block_size) { + for (auto end = p + s.size() - block_size + 1; p < end;) p = transcode(p); } - FMT_CATCH(...) {} - format_error_code(out, error_code, message); + if (auto num_chars_left = s.data() + s.size() - p) { + char buf[2 * block_size - 1] = {}; + memcpy(buf, p, to_unsigned(num_chars_left)); + p = buf; + do { + p = transcode(p); + } while (p - buf < num_chars_left); + } + buffer_.push_back(0); } -#endif // FMT_USE_WINDOWS_H - FMT_FUNC void format_system_error(internal::buffer& out, int error_code, string_view message) FMT_NOEXCEPT { FMT_TRY { @@ -1369,20 +1359,37 @@ FMT_FUNC void report_system_error(int error_code, 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) { memory_buffer buffer; internal::vformat_to(buffer, format_str, basic_format_args>(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(_get_osfhandle(fd)), + u16.c_str(), static_cast(u16.size()), &written, + nullptr)) { + FMT_THROW(format_error("failed to write to console")); + } + return; + } +#endif 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>(args)); + fwrite_fully(buffer.data(), 1, buffer.size(), f); +} +#endif + FMT_FUNC void vprint(string_view format_str, format_args args) { vprint(stdout, format_str, args); } diff --git a/include/spdlog/fmt/bundled/format.h b/include/spdlog/fmt/bundled/format.h index 01f41f5c..4e96539f 100644 --- a/include/spdlog/fmt/bundled/format.h +++ b/include/spdlog/fmt/bundled/format.h @@ -33,8 +33,6 @@ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ -#include "core.h" - #include #include #include @@ -43,10 +41,10 @@ #include #include -#ifdef __clang__ -# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) -#else -# define FMT_CLANG_VERSION 0 +#include "core.h" + +#ifdef FMT_DEPRECATED_INCLUDE_OS +# include "os.h" #endif #ifdef __INTEL_COMPILER @@ -69,8 +67,22 @@ # define FMT_HAS_BUILTIN(x) 0 #endif -#if FMT_HAS_CPP_ATTRIBUTE(fallthrough) && \ - (__cplusplus >= 201703 || FMT_GCC_VERSION != 0) +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_NOINLINE __attribute__((noinline)) +#else +# define FMT_NOINLINE +#endif + +#if __cplusplus == 201103L || __cplusplus == 201402L +# if defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +# elif FMT_GCC_VERSION >= 700 && !defined(__PGI) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] +# else +# define FMT_FALLTHROUGH +# endif +#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \ + (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) # define FMT_FALLTHROUGH [[fallthrough]] #else # define FMT_FALLTHROUGH @@ -78,12 +90,12 @@ #ifndef FMT_THROW # if FMT_EXCEPTIONS -# if FMT_MSC_VER +# if FMT_MSC_VER || FMT_NVCC FMT_BEGIN_NAMESPACE namespace internal { template inline void do_throw(const Exception& x) { - // Silence unreachable code warnings in MSVC because these are nearly - // impossible to fix in a generic code. + // Silence unreachable code warnings in MSVC and NVCC because these + // are nearly impossible to fix in a generic code. volatile bool b = true; if (b) throw x; } @@ -102,6 +114,14 @@ FMT_END_NAMESPACE # 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 + #ifndef FMT_USE_USER_DEFINED_LITERALS // For Intel and NVIDIA compilers both they and the system gcc/msc support UDLs. # if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ @@ -115,11 +135,11 @@ FMT_END_NAMESPACE #endif #ifndef FMT_USE_UDL_TEMPLATE -// EDG front end based compilers (icc, nvcc) do not support UDL templates yet -// and GCC 9 warns about them. +// EDG front end based compilers (icc, nvcc) and GCC < 6.4 do not propertly +// support UDL templates and GCC >= 9 warns about them. # if FMT_USE_USER_DEFINED_LITERALS && FMT_ICC_VERSION == 0 && \ FMT_CUDA_VERSION == 0 && \ - ((FMT_GCC_VERSION >= 600 && FMT_GCC_VERSION <= 900 && \ + ((FMT_GCC_VERSION >= 604 && FMT_GCC_VERSION <= 900 && \ __cplusplus >= 201402L) || \ FMT_CLANG_VERSION >= 304) # define FMT_USE_UDL_TEMPLATE 1 @@ -128,6 +148,18 @@ FMT_END_NAMESPACE # endif #endif +#ifndef FMT_USE_FLOAT +# define FMT_USE_FLOAT 1 +#endif + +#ifndef FMT_USE_DOUBLE +# define FMT_USE_DOUBLE 1 +#endif + +#ifndef FMT_USE_LONG_DOUBLE +# define FMT_USE_LONG_DOUBLE 1 +#endif + // __builtin_clz is broken in clang with Microsoft CodeGen: // https://github.com/fmtlib/fmt/issues/519 #if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER @@ -203,10 +235,6 @@ FMT_END_NAMESPACE FMT_BEGIN_NAMESPACE namespace internal { -// A helper function to suppress bogus "conditional expression is constant" -// warnings. -template inline T const_check(T value) { return value; } - // An equivalent of `*reinterpret_cast(&source)` that doesn't have // undefined behavior (e.g. due to type aliasing). // Example: uint64_t d = bit_cast(2.718); @@ -219,7 +247,7 @@ inline Dest bit_cast(const Source& source) { } inline bool is_big_endian() { - auto u = 1u; + const auto u = 1u; struct bytes { char data[sizeof(u)]; }; @@ -296,7 +324,7 @@ template class is_output_iterator { using type = decltype(test(typename iterator_category::type{})); public: - static const bool value = !std::is_const>::value; + enum { value = !std::is_const>::value }; }; // A workaround for std::string not having mutable data() until C++17. @@ -308,7 +336,7 @@ inline typename Container::value_type* get_data(Container& c) { return c.data(); } -#ifdef _SECURE_SCL +#if defined(_SECURE_SCL) && _SECURE_SCL // Make a checked iterator to avoid MSVC warnings. template using checked_ptr = stdext::checked_array_iterator; template checked_ptr make_checked(T* p, std::size_t size) { @@ -379,6 +407,7 @@ template class truncating_iterator_base { public: using iterator_category = std::output_iterator_tag; + using value_type = typename std::iterator_traits::value_type; using difference_type = void; using pointer = void; using reference = void; @@ -399,12 +428,10 @@ class truncating_iterator; template class truncating_iterator : public truncating_iterator_base { - using traits = std::iterator_traits; - - mutable typename traits::value_type blackhole_; + mutable typename truncating_iterator_base::value_type blackhole_; public: - using value_type = typename traits::value_type; + using value_type = typename truncating_iterator_base::value_type; truncating_iterator(OutputIt out, std::size_t limit) : truncating_iterator_base(out, limit) {} @@ -429,13 +456,11 @@ template class truncating_iterator : public truncating_iterator_base { public: - using value_type = typename OutputIt::container_type::value_type; - truncating_iterator(OutputIt out, std::size_t limit) : truncating_iterator_base(out, limit) {} - truncating_iterator& operator=(value_type val) { - if (this->count_++ < this->limit_) this->out_ = val; + template truncating_iterator& operator=(T val) { + if (this->count_++ < this->limit_) *this->out_++ = val; return *this; } @@ -466,8 +491,8 @@ inline size_t count_code_points(basic_string_view s) { } // Counts the number of code points in a UTF-8 string. -inline size_t count_code_points(basic_string_view s) { - const char8_t* data = s.data(); +inline size_t count_code_points(basic_string_view s) { + const char* data = s.data(); size_t num_code_points = 0; for (size_t i = 0, size = s.size(); i != size; ++i) { if ((data[i] & 0xc0) != 0x80) ++num_code_points; @@ -475,6 +500,11 @@ inline size_t count_code_points(basic_string_view s) { return num_code_points; } +inline size_t count_code_points(basic_string_view s) { + return count_code_points(basic_string_view( + reinterpret_cast(s.data()), s.size())); +} + template inline size_t code_point_index(basic_string_view s, size_t n) { size_t size = s.size(); @@ -482,8 +512,8 @@ inline size_t code_point_index(basic_string_view s, size_t n) { } // Calculates the index of the nth code point in a UTF-8 string. -inline size_t code_point_index(basic_string_view s, size_t n) { - const char8_t* data = s.data(); +inline size_t code_point_index(basic_string_view s, size_t n) { + const char8_type* data = s.data(); size_t num_code_points = 0; for (size_t i = 0, size = s.size(); i != size; ++i) { if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) { @@ -493,13 +523,13 @@ inline size_t code_point_index(basic_string_view s, size_t n) { return s.size(); } -inline char8_t to_char8_t(char c) { return static_cast(c); } +inline char8_type to_char8_t(char c) { return static_cast(c); } template using needs_conversion = bool_constant< std::is_same::value_type, char>::value && - std::is_same::value>; + std::is_same::value>; template ::value)> @@ -543,20 +573,22 @@ class buffer_range : public internal::output_range< : internal::output_range(std::back_inserter(buf)) {} }; -// A UTF-8 string view. -class u8string_view : public basic_string_view { +class FMT_DEPRECATED u8string_view + : public basic_string_view { public: u8string_view(const char* s) - : basic_string_view(reinterpret_cast(s)) {} + : basic_string_view( + reinterpret_cast(s)) {} u8string_view(const char* s, size_t count) FMT_NOEXCEPT - : basic_string_view(reinterpret_cast(s), count) { - } + : basic_string_view( + reinterpret_cast(s), count) {} }; #if FMT_USE_USER_DEFINED_LITERALS inline namespace literals { -inline u8string_view operator"" _u(const char* s, std::size_t n) { - return {s, n}; +FMT_DEPRECATED inline basic_string_view operator"" _u( + const char* s, std::size_t n) { + return {reinterpret_cast(s), n}; } } // namespace literals #endif @@ -688,6 +720,7 @@ using memory_buffer = basic_memory_buffer; using wmemory_buffer = basic_memory_buffer; /** A formatting error such as invalid format string. */ +FMT_CLASS_API class FMT_API format_error : public std::runtime_error { public: explicit format_error(const char* message) : std::runtime_error(message) {} @@ -713,6 +746,13 @@ FMT_CONSTEXPR bool is_negative(T) { return false; } +template ::value)> +FMT_CONSTEXPR bool is_supported_floating_point(T) { + return (std::is_same::value && FMT_USE_FLOAT) || + (std::is_same::value && FMT_USE_DOUBLE) || + (std::is_same::value && FMT_USE_LONG_DOUBLE); +} + // Smallest of uint32_t, uint64_t, uint128_t that is large enough to // represent all values of T. template @@ -865,11 +905,11 @@ inline Char* format_decimal(Char* buffer, UInt value, int num_digits, return end; } -template constexpr int digits10() noexcept { +template constexpr int digits10() FMT_NOEXCEPT { return std::numeric_limits::digits10; } -template <> constexpr int digits10() noexcept { return 38; } -template <> constexpr int digits10() noexcept { return 38; } +template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } +template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } template inline Iterator format_decimal(Iterator out, UInt value, int num_digits, @@ -931,61 +971,42 @@ inline It format_uint(It out, UInt value, int num_digits, bool upper = false) { return internal::copy_str(buffer, buffer + num_digits, out); } -#ifndef _WIN32 -# define FMT_USE_WINDOWS_H 0 -#elif !defined(FMT_USE_WINDOWS_H) -# define FMT_USE_WINDOWS_H 1 -#endif - -// Define FMT_USE_WINDOWS_H to 0 to disable use of windows.h. -// All the functionality that relies on it will be disabled too. -#if FMT_USE_WINDOWS_H // A converter from UTF-8 to UTF-16. -// It is only provided for Windows since other systems support UTF-8 natively. class utf8_to_utf16 { private: wmemory_buffer buffer_; public: FMT_API explicit utf8_to_utf16(string_view s); - operator wstring_view() const { return wstring_view(&buffer_[0], size()); } + operator wstring_view() const { return {&buffer_[0], size()}; } size_t size() const { return buffer_.size() - 1; } const wchar_t* c_str() const { return &buffer_[0]; } - std::wstring str() const { return std::wstring(&buffer_[0], size()); } + std::wstring str() const { return {&buffer_[0], size()}; } }; -// A converter from UTF-16 to UTF-8. -// It is only provided for Windows since other systems support UTF-8 natively. -class utf16_to_utf8 { - private: - memory_buffer buffer_; - - public: - utf16_to_utf8() {} - FMT_API explicit utf16_to_utf8(wstring_view s); - operator string_view() const { return string_view(&buffer_[0], size()); } - size_t size() const { return buffer_.size() - 1; } - const char* c_str() const { return &buffer_[0]; } - std::string str() const { return std::string(&buffer_[0], size()); } - - // Performs conversion returning a system error code instead of - // throwing exception on conversion error. This method may still throw - // in case of memory allocation error. - FMT_API int convert(wstring_view s); -}; - -FMT_API void format_windows_error(internal::buffer& out, int error_code, - string_view message) FMT_NOEXCEPT; -#endif - template struct null {}; // Workaround an array initialization issue in gcc 4.8. template struct fill_t { private: - Char data_[6]; + enum { max_size = 4 }; + Char data_[max_size]; + unsigned char size_; public: + FMT_CONSTEXPR void operator=(basic_string_view s) { + auto size = s.size(); + if (size > max_size) { + FMT_THROW(format_error("invalid fill")); + return; + } + for (size_t i = 0; i < size; ++i) data_[i] = s[i]; + size_ = static_cast(size); + } + + size_t size() const { return size_; } + const Char* data() const { return data_; } + FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; } FMT_CONSTEXPR const Char& operator[](size_t index) const { return data_[index]; @@ -994,6 +1015,7 @@ template struct fill_t { static FMT_CONSTEXPR fill_t make() { auto fill = fill_t(); fill[0] = Char(' '); + fill.size_ = 1; return fill; } }; @@ -1052,7 +1074,7 @@ struct float_specs { bool percent : 1; bool binary32 : 1; bool use_grisu : 1; - bool trailing_zeros : 1; + bool showpoint : 1; }; // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. @@ -1093,10 +1115,9 @@ template class float_writer { // Insert a decimal point after the first digit and add an exponent. *it++ = static_cast(*digits_); int num_zeros = specs_.precision - num_digits_; - bool trailing_zeros = num_zeros > 0 && specs_.trailing_zeros; - if (num_digits_ > 1 || trailing_zeros) *it++ = decimal_point_; + if (num_digits_ > 1 || specs_.showpoint) *it++ = decimal_point_; it = copy_str(digits_ + 1, digits_ + num_digits_, it); - if (trailing_zeros) + if (num_zeros > 0 && specs_.showpoint) it = std::fill_n(it, num_zeros, static_cast('0')); *it++ = static_cast(specs_.upper ? 'E' : 'e'); return write_exponent(full_exp - 1, it); @@ -1105,7 +1126,7 @@ template class float_writer { // 1234e7 -> 12340000000[.0+] it = copy_str(digits_, digits_ + num_digits_, it); it = std::fill_n(it, full_exp - num_digits_, static_cast('0')); - if (specs_.trailing_zeros) { + if (specs_.showpoint || specs_.precision < 0) { *it++ = decimal_point_; int num_zeros = specs_.precision - full_exp; if (num_zeros <= 0) { @@ -1122,7 +1143,7 @@ template class float_writer { } else if (full_exp > 0) { // 1234e-2 -> 12.34[0+] it = copy_str(digits_, digits_ + full_exp, it); - if (!specs_.trailing_zeros) { + if (!specs_.showpoint) { // Remove trailing zeros. int num_digits = num_digits_; while (num_digits > full_exp && digits_[num_digits - 1] == '0') @@ -1141,12 +1162,15 @@ template class float_writer { // 1234e-6 -> 0.001234 *it++ = static_cast('0'); int num_zeros = -full_exp; - if (specs_.precision >= 0 && specs_.precision < num_zeros) - num_zeros = specs_.precision; int num_digits = num_digits_; - if (!specs_.trailing_zeros) + if (num_digits == 0 && specs_.precision >= 0 && + specs_.precision < num_zeros) { + num_zeros = specs_.precision; + } + // Remove trailing zeros. + if (!specs_.showpoint) while (num_digits > 0 && digits_[num_digits - 1] == '0') --num_digits; - if (num_zeros != 0 || num_digits != 0) { + if (num_zeros != 0 || num_digits != 0 || specs_.showpoint) { *it++ = decimal_point_; it = std::fill_n(it, num_zeros, static_cast('0')); it = copy_str(digits_, digits_ + num_digits, it); @@ -1191,7 +1215,7 @@ int snprintf_float(T value, int precision, float_specs specs, buffer& buf); template T promote_float(T value) { return value; } -inline double promote_float(float value) { return value; } +inline double promote_float(float value) { return static_cast(value); } template FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) { @@ -1212,6 +1236,7 @@ FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) { handler.on_oct(); break; case 'n': + case 'L': handler.on_num(); break; default: @@ -1223,11 +1248,11 @@ template FMT_CONSTEXPR float_specs parse_float_type_spec( const basic_format_specs& specs, ErrorHandler&& eh = {}) { auto result = float_specs(); - result.trailing_zeros = specs.alt; + result.showpoint = specs.alt; switch (specs.type) { case 0: result.format = float_format::general; - result.trailing_zeros |= specs.precision != 0; + result.showpoint |= specs.precision > 0; break; case 'G': result.upper = true; @@ -1240,14 +1265,14 @@ FMT_CONSTEXPR float_specs parse_float_type_spec( FMT_FALLTHROUGH; case 'e': result.format = float_format::exp; - result.trailing_zeros |= specs.precision != 0; + result.showpoint |= specs.precision != 0; break; case 'F': result.upper = true; FMT_FALLTHROUGH; case 'f': result.format = float_format::fixed; - result.trailing_zeros |= specs.precision != 0; + result.showpoint |= specs.precision != 0; break; #if FMT_DEPRECATED_PERCENT case '%': @@ -1348,13 +1373,14 @@ void arg_map::init(const basic_format_args& args) { if (args.is_packed()) { for (int i = 0;; ++i) { internal::type arg_type = args.type(i); - if (arg_type == internal::none_type) return; - if (arg_type == internal::named_arg_type) push_back(args.values_[i]); + if (arg_type == internal::type::none_type) return; + if (arg_type == internal::type::named_arg_type) + push_back(args.values_[i]); } } for (int i = 0, n = args.max_size(); i < n; ++i) { auto type = args.args_[i].type_; - if (type == internal::named_arg_type) push_back(args.args_[i].value_); + if (type == internal::type::named_arg_type) push_back(args.args_[i].value_); } } @@ -1372,6 +1398,14 @@ template struct nonfinite_writer { } }; +template +FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t& fill) { + auto fill_size = fill.size(); + if (fill_size == 1) return std::fill_n(it, n, fill[0]); + for (size_t i = 0; i < n; ++i) it = std::copy_n(fill.data(), fill_size, it); + return it; +} + // This template provides operations for formatting and writing data into a // character range. template class basic_writer { @@ -1605,6 +1639,18 @@ template class basic_writer { } }; + struct bytes_writer { + string_view bytes; + + size_t size() const { return bytes.size(); } + size_t width() const { return bytes.size(); } + + template void operator()(It&& it) const { + const char* data = bytes.data(); + it = copy_str(data, data + size(), it); + } + }; + template struct pointer_writer { UIntPtr value; int num_digits; @@ -1634,20 +1680,20 @@ template class basic_writer { size_t size = f.size(); // The number of code units. size_t num_code_points = width != 0 ? f.width() : size; if (width <= num_code_points) return f(reserve(size)); - auto&& it = reserve(width + (size - num_code_points)); - char_type fill = specs.fill[0]; - std::size_t padding = width - num_code_points; + size_t padding = width - num_code_points; + size_t fill_size = specs.fill.size(); + auto&& it = reserve(size + padding * fill_size); if (specs.align == align::right) { - it = std::fill_n(it, padding, fill); + it = fill(it, padding, specs.fill); f(it); } else if (specs.align == align::center) { std::size_t left_padding = padding / 2; - it = std::fill_n(it, left_padding, fill); + it = fill(it, left_padding, specs.fill); f(it); - it = std::fill_n(it, padding - left_padding, fill); + it = fill(it, padding - left_padding, specs.fill); } else { f(it); - it = std::fill_n(it, padding, fill); + it = fill(it, padding, specs.fill); } } @@ -1671,6 +1717,9 @@ template class basic_writer { template ::value)> void write(T value, format_specs specs = {}) { + if (const_check(!is_supported_floating_point(value))) { + return; + } float_specs fspecs = parse_float_type_spec(specs); fspecs.sign = specs.sign; if (std::signbit(value)) { // value < 0 is false for NaN so use signbit. @@ -1706,7 +1755,12 @@ template class basic_writer { return; } int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; - if (fspecs.format == float_format::exp) ++precision; + if (fspecs.format == float_format::exp) { + if (precision == max_value()) + FMT_THROW(format_error("number is too big")); + else + ++precision; + } if (const_check(std::is_same())) fspecs.binary32 = true; fspecs.use_grisu = use_grisu(); if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) value *= 100; @@ -1758,6 +1812,10 @@ template class basic_writer { write(data, size, specs); } + void write_bytes(string_view bytes, const format_specs& specs) { + write_padded(specs, bytes_writer{bytes}); + } + template void write_pointer(UIntPtr value, const format_specs* specs) { int num_digits = count_digits<4>(value); @@ -1860,7 +1918,10 @@ class arg_formatter_base { template ::value)> iterator operator()(T value) { - writer_.write(value, specs_ ? *specs_ : format_specs()); + if (const_check(is_supported_floating_point(value))) + writer_.write(value, specs_ ? *specs_ : format_specs()); + else + FMT_ASSERT(false, "unsupported float argument type"); return out(); } @@ -1926,10 +1987,6 @@ template FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end, ErrorHandler&& eh) { FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); - if (*begin == '0') { - ++begin; - return 0; - } unsigned value = 0; // Convert to unsigned to prevent a warning. constexpr unsigned max_int = max_value(); @@ -2023,7 +2080,9 @@ template class specs_setter { : specs_(other.specs_) {} FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; } - FMT_CONSTEXPR void on_fill(Char fill) { specs_.fill[0] = fill; } + FMT_CONSTEXPR void on_fill(basic_string_view fill) { + specs_.fill = fill; + } FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; } FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; } FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; } @@ -2060,14 +2119,14 @@ template class numeric_specs_checker { FMT_CONSTEXPR void check_sign() { require_numeric_argument(); - if (is_integral_type(arg_type_) && arg_type_ != int_type && - arg_type_ != long_long_type && arg_type_ != internal::char_type) { + if (is_integral_type(arg_type_) && arg_type_ != type::int_type && + arg_type_ != type::long_long_type && arg_type_ != type::char_type) { error_handler_.on_error("format specifier requires signed argument"); } } FMT_CONSTEXPR void check_precision() { - if (is_integral_type(arg_type_) || arg_type_ == internal::pointer_type) + if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type) error_handler_.on_error("precision not allowed for this argument type"); } @@ -2190,6 +2249,7 @@ enum class arg_id_kind { none, index, name }; // An argument reference. template struct arg_ref { FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {} + FMT_CONSTEXPR explicit arg_ref(int index) : kind(arg_id_kind::index), val(index) {} FMT_CONSTEXPR explicit arg_ref(basic_string_view name) @@ -2282,7 +2342,11 @@ FMT_CONSTEXPR const Char* parse_arg_id(const Char* begin, const Char* end, return begin; } if (c >= '0' && c <= '9') { - int index = parse_nonnegative_int(begin, end, handler); + int index = 0; + if (c != '0') + index = parse_nonnegative_int(begin, end, handler); + else + ++begin; if (begin == end || (*begin != '}' && *begin != ':')) handler.on_error("invalid format string"); else @@ -2335,16 +2399,25 @@ template struct precision_adapter { SpecHandler& handler; }; +template +FMT_CONSTEXPR const Char* next_code_point(const Char* begin, const Char* end) { + if (const_check(sizeof(Char) != 1) || (*begin & 0x80) == 0) return begin + 1; + do { + ++begin; + } while (begin != end && (*begin & 0xc0) == 0x80); + return begin; +} + // Parses fill and alignment. template FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end, Handler&& handler) { FMT_ASSERT(begin != end, ""); auto align = align::none; - int i = 0; - if (begin + 1 != end) ++i; - do { - switch (static_cast(begin[i])) { + auto p = next_code_point(begin, end); + if (p == end) p = begin; + for (;;) { + switch (static_cast(*p)) { case '<': align = align::left; break; @@ -2361,18 +2434,21 @@ FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end, break; } if (align != align::none) { - if (i > 0) { + if (p != begin) { auto c = *begin; if (c == '{') return handler.on_error("invalid fill character '{'"), begin; - begin += 2; - handler.on_fill(c); + handler.on_fill(basic_string_view(begin, to_unsigned(p - begin))); + begin = p + 1; } else ++begin; handler.on_align(align); break; + } else if (p == begin) { + break; } - } while (i-- > 0); + p = begin; + } return begin; } @@ -2520,7 +2596,7 @@ FMT_CONSTEXPR void parse_format_string(basic_string_view format_str, // Doing two passes with memchr (one for '{' and another for '}') is up to // 2.5x faster than the naive one-pass implementation on big format strings. const Char* p = begin; - if (*begin != '{' && !find(begin, end, '{', p)) + if (*begin != '{' && !find(begin + 1, end, '{', p)) return write(begin, end); write(begin, p); ++p; @@ -2554,7 +2630,7 @@ FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs( using context = buffer_context; using mapped_type = conditional_t::value != - internal::custom_type, + type::custom_type, decltype(arg_mapper().map(std::declval())), T>; auto f = conditional_t::value, formatter, @@ -2624,10 +2700,9 @@ FMT_CONSTEXPR bool do_check_format_string(basic_string_view s, template ::value), int>> void check_format_string(S format_str) { - FMT_CONSTEXPR_DECL bool invalid_format = - internal::do_check_format_string( - to_string_view(format_str)); + FMT_CONSTEXPR_DECL bool invalid_format = internal::do_check_format_string< + typename S::char_type, internal::error_handler, + remove_const_t>...>(to_string_view(format_str)); (void)invalid_format; } @@ -2647,6 +2722,14 @@ void handle_dynamic_spec(int& value, arg_ref ref, break; } } + +using format_func = void (*)(internal::buffer&, int, string_view); + +FMT_API void format_error_code(buffer& out, int error_code, + string_view message) FMT_NOEXCEPT; + +FMT_API void report_error(format_func func, int error_code, + string_view message) FMT_NOEXCEPT; } // namespace internal template @@ -2699,6 +2782,7 @@ class arg_formatter : public internal::arg_formatter_base { An error returned by an operating system or a language runtime, for example a file opening error. */ +FMT_CLASS_API class FMT_API system_error : public std::runtime_error { private: void init(int err_code, string_view format_str, format_args args); @@ -2765,55 +2849,6 @@ FMT_API void format_system_error(internal::buffer& out, int error_code, FMT_API void report_system_error(int error_code, string_view message) FMT_NOEXCEPT; -#if FMT_USE_WINDOWS_H - -/** A Windows error. */ -class windows_error : public system_error { - private: - FMT_API void init(int error_code, string_view format_str, format_args args); - - public: - /** - \rst - Constructs a :class:`fmt::windows_error` object with the description - of the form - - .. parsed-literal:: - **: ** - - where ** is the formatted message and ** is the - system message corresponding to the error code. - *error_code* is a Windows error code as given by ``GetLastError``. - If *error_code* is not a valid error code such as -1, the system message - will look like "error -1". - - **Example**:: - - // This throws a windows_error with the description - // cannot open file 'madeup': The system cannot find the file specified. - // or similar (system message may vary). - const char *filename = "madeup"; - LPOFSTRUCT of = LPOFSTRUCT(); - HFILE file = OpenFile(filename, &of, OF_READ); - if (file == HFILE_ERROR) { - throw fmt::windows_error(GetLastError(), - "cannot open file '{}'", filename); - } - \endrst - */ - template - windows_error(int error_code, string_view message, const Args&... args) { - init(error_code, message, make_format_args(args...)); - } -}; - -// Reports a Windows error without throwing an exception. -// Can be used to report errors from destructors. -FMT_API void report_windows_error(int error_code, - string_view message) FMT_NOEXCEPT; - -#endif - /** Fast integer formatter. */ class format_int { private: @@ -2894,7 +2929,7 @@ class format_int { template struct formatter::value != - internal::custom_type>> { + internal::type::custom_type>> { FMT_CONSTEXPR formatter() = default; // Parses format specifiers stopping either at the end of the range or at the @@ -2908,40 +2943,56 @@ struct formatter(eh)); break; - case internal::char_type: + case internal::type::char_type: handle_char_specs( &specs_, internal::char_specs_checker(specs_.type, eh)); break; - case internal::float_type: - case internal::double_type: - case internal::long_double_type: - internal::parse_float_type_spec(specs_, eh); + case internal::type::float_type: + if (internal::const_check(FMT_USE_FLOAT)) { + internal::parse_float_type_spec(specs_, eh); + } else { + FMT_ASSERT(false, "float support disabled"); + } + break; + case internal::type::double_type: + if (internal::const_check(FMT_USE_DOUBLE)) { + internal::parse_float_type_spec(specs_, eh); + } else { + FMT_ASSERT(false, "double support disabled"); + } + break; + case internal::type::long_double_type: + if (internal::const_check(FMT_USE_LONG_DOUBLE)) { + internal::parse_float_type_spec(specs_, eh); + } else { + FMT_ASSERT(false, "long double support disabled"); + } break; - case internal::cstring_type: + case internal::type::cstring_type: internal::handle_cstring_type_spec( specs_.type, internal::cstring_type_checker(eh)); break; - case internal::string_type: + case internal::type::string_type: internal::check_string_type_spec(specs_.type, eh); break; - case internal::pointer_type: + case internal::type::pointer_type: internal::check_pointer_type_spec(specs_.type, eh); break; - case internal::custom_type: + case internal::type::custom_type: // Custom format specifiers should be checked in parse functions of // formatter specializations. break; @@ -2970,7 +3021,7 @@ struct formatter \ struct formatter : formatter { \ template \ - auto format(const Type& val, FormatContext& ctx) -> decltype(ctx.out()) { \ + auto format(Type const& val, FormatContext& ctx) -> decltype(ctx.out()) { \ return formatter::format(val, ctx); \ } \ } @@ -3077,7 +3128,8 @@ typename basic_format_context::format_arg basic_format_context::arg(basic_string_view name) { map_.init(args_); format_arg arg = map_.find(name); - if (arg.type() == internal::none_type) this->on_error("argument not found"); + if (arg.type() == internal::type::none_type) + this->on_error("argument not found"); return arg; } @@ -3164,6 +3216,43 @@ template inline const void* ptr(const std::shared_ptr& p) { return p.get(); } +class bytes { + private: + string_view data_; + friend struct formatter; + + public: + explicit bytes(string_view data) : data_(data) {} +}; + +template <> struct formatter { + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + using handler_type = internal::dynamic_specs_handler; + internal::specs_checker handler(handler_type(specs_, ctx), + internal::type::string_type); + auto it = parse_format_specs(ctx.begin(), ctx.end(), handler); + internal::check_string_type_spec(specs_.type, ctx.error_handler()); + return it; + } + + template + auto format(bytes b, FormatContext& ctx) -> decltype(ctx.out()) { + internal::handle_dynamic_spec( + specs_.width, specs_.width_ref, ctx); + internal::handle_dynamic_spec( + specs_.precision, specs_.precision_ref, ctx); + using range_type = + internal::output_range; + internal::basic_writer writer(range_type(ctx.out())); + writer.write_bytes(b.data_, specs_); + return writer.out(); + } + + private: + internal::dynamic_format_specs specs_; +}; + template struct arg_join : internal::view { It begin; It end; @@ -3216,6 +3305,11 @@ arg_join join(It begin, It end, wstring_view sep) { std::vector v = {1, 2, 3}; fmt::print("{}", fmt::join(v, ", ")); // Output: "1, 2, 3" + + ``fmt::join`` applies passed format specifiers to the range elements:: + + fmt::print("{:02}", fmt::join(v, ", ")); + // Output: "01, 02, 03" \endrst */ template @@ -3233,7 +3327,6 @@ arg_join, wchar_t> join(const Range& range, /** \rst Converts *value* to ``std::string`` using the default format for type *T*. - It doesn't support user-defined types with custom formatters. **Example**:: @@ -3261,7 +3354,7 @@ std::basic_string to_string(const basic_memory_buffer& buf) { template typename buffer_context::iterator internal::vformat_to( internal::buffer& buf, basic_string_view format_str, - basic_format_args> args) { + basic_format_args>> args) { using range = buffer_range; return vformat_to>(buf, to_string_view(format_str), args); @@ -3271,7 +3364,7 @@ template , FMT_ENABLE_IF(internal::is_string::value)> inline typename buffer_context::iterator vformat_to( internal::buffer& buf, const S& format_str, - basic_format_args> args) { + basic_format_args>> args) { return internal::vformat_to(buf, to_string_view(format_str), args); } @@ -3282,7 +3375,7 @@ inline typename buffer_context::iterator format_to( internal::check_format_string(format_str); using context = buffer_context; return internal::vformat_to(buf, to_string_view(format_str), - {make_format_args(args...)}); + make_format_args(args...)); } template @@ -3295,8 +3388,9 @@ template ::value && !internal::is_contiguous_back_insert_iterator::value)> -inline OutputIt vformat_to(OutputIt out, const S& format_str, - format_args_t> args) { +inline OutputIt vformat_to( + OutputIt out, const S& format_str, + format_args_t, char_t> args) { using range = internal::output_range>; return vformat_to>(range(out), to_string_view(format_str), args); @@ -3322,7 +3416,7 @@ inline OutputIt format_to(OutputIt out, const S& format_str, Args&&... args) { internal::check_format_string(format_str); using context = format_context_t>; return vformat_to(out, to_string_view(format_str), - {make_format_args(args...)}); + make_format_args(args...)); } template struct format_to_n_result { @@ -3350,7 +3444,7 @@ template ::value)> inline format_to_n_result vformat_to_n( OutputIt out, std::size_t n, basic_string_view format_str, - format_to_n_args args) { + format_to_n_args, type_identity_t> args) { auto it = vformat_to(internal::truncating_iterator(out, n), format_str, args); return {it.base(), it.count()}; @@ -3372,13 +3466,13 @@ inline format_to_n_result format_to_n(OutputIt out, std::size_t n, internal::check_format_string(format_str); using context = format_to_n_context>; return vformat_to_n(out, n, to_string_view(format_str), - {make_format_args(args...)}); + make_format_args(args...)); } template inline std::basic_string internal::vformat( basic_string_view format_str, - basic_format_args> args) { + basic_format_args>> args) { basic_memory_buffer buffer; internal::vformat_to(buffer, format_str, args); return to_string(buffer); @@ -3443,6 +3537,22 @@ template struct udl_arg { } }; +// Converts string literals to basic_string_view. +template +FMT_CONSTEXPR basic_string_view compile_string_to_view( + const Char (&s)[N]) { + // Remove trailing null character if needed. Won't be present if this is used + // with raw character array (i.e. not defined as a string). + return {s, + N - ((std::char_traits::to_int_type(s[N - 1]) == 0) ? 1 : 0)}; +} + +// Converts string_view to basic_string_view. +template +FMT_CONSTEXPR basic_string_view compile_string_to_view( + const std_string_view& s) { + return {s.data(), s.size()}; +} } // namespace internal inline namespace literals { @@ -3499,25 +3609,22 @@ FMT_CONSTEXPR internal::udl_arg operator"" _a(const wchar_t* s, #endif // FMT_USE_USER_DEFINED_LITERALS FMT_END_NAMESPACE -#define FMT_STRING_IMPL(s, ...) \ - [] { \ - struct str : fmt::compile_string { \ - using char_type = typename std::remove_cv::type>::type>::type; \ - __VA_ARGS__ FMT_CONSTEXPR \ - operator fmt::basic_string_view() const { \ - return {s, sizeof(s) / sizeof(char_type) - 1}; \ - } \ - } result; \ - /* Suppress Qt Creator warning about unused operator. */ \ - (void)static_cast>( \ - result); \ - return result; \ +#define FMT_STRING_IMPL(s, ...) \ + [] { \ + /* Use a macro-like name to avoid shadowing warnings. */ \ + struct FMT_COMPILE_STRING : fmt::compile_string { \ + using char_type = fmt::remove_cvref_t; \ + FMT_MAYBE_UNUSED __VA_ARGS__ FMT_CONSTEXPR \ + operator fmt::basic_string_view() const { \ + return fmt::internal::compile_string_to_view(s); \ + } \ + }; \ + return FMT_COMPILE_STRING(); \ }() /** \rst - Constructs a compile-time format string. + Constructs a compile-time format string from a string literal *s*. **Example**:: diff --git a/include/spdlog/fmt/bundled/locale.h b/include/spdlog/fmt/bundled/locale.h index 7c13656e..70bc1935 100644 --- a/include/spdlog/fmt/bundled/locale.h +++ b/include/spdlog/fmt/bundled/locale.h @@ -9,6 +9,7 @@ #define FMT_LOCALE_H_ #include + #include "format.h" FMT_BEGIN_NAMESPACE @@ -18,16 +19,16 @@ template typename buffer_context::iterator vformat_to( const std::locale& loc, buffer& buf, basic_string_view format_str, - basic_format_args> args) { + basic_format_args>> args) { using range = buffer_range; return vformat_to>(buf, to_string_view(format_str), args, internal::locale_ref(loc)); } template -std::basic_string vformat(const std::locale& loc, - basic_string_view format_str, - basic_format_args> args) { +std::basic_string vformat( + const std::locale& loc, basic_string_view format_str, + basic_format_args>> args) { basic_memory_buffer buffer; internal::vformat_to(loc, buffer, format_str, args); return fmt::to_string(buffer); @@ -37,7 +38,7 @@ std::basic_string vformat(const std::locale& loc, template > inline std::basic_string vformat( const std::locale& loc, const S& format_str, - basic_format_args> args) { + basic_format_args>> args) { return internal::vformat(loc, to_string_view(format_str), args); } @@ -46,15 +47,15 @@ inline std::basic_string format(const std::locale& loc, const S& format_str, Args&&... args) { return internal::vformat( loc, to_string_view(format_str), - {internal::make_args_checked(format_str, args...)}); + internal::make_args_checked(format_str, args...)); } template ::value, char_t>> -inline OutputIt vformat_to(OutputIt out, const std::locale& loc, - const S& format_str, - format_args_t args) { +inline OutputIt vformat_to( + OutputIt out, const std::locale& loc, const S& format_str, + format_args_t, Char> args) { using range = internal::output_range; return vformat_to>( range(out), to_string_view(format_str), args, internal::locale_ref(loc)); diff --git a/include/spdlog/fmt/bundled/ostream.h b/include/spdlog/fmt/bundled/ostream.h index 72d078b2..c4831533 100644 --- a/include/spdlog/fmt/bundled/ostream.h +++ b/include/spdlog/fmt/bundled/ostream.h @@ -93,7 +93,9 @@ void format_value(buffer& buf, const T& value, locale_ref loc = locale_ref()) { formatbuf format_buf(buf); std::basic_ostream output(&format_buf); + #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) if (loc) output.imbue(loc.get()); + #endif output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output << value; buf.resize(buf.size()); @@ -115,7 +117,7 @@ struct fallback_formatter::value>> template void vprint(std::basic_ostream& os, basic_string_view format_str, - basic_format_args> args) { + basic_format_args>> args) { basic_memory_buffer buffer; internal::vformat_to(buffer, format_str, args); internal::write(os, buffer); @@ -134,7 +136,7 @@ template ::value, char_t>> void print(std::basic_ostream& os, const S& format_str, Args&&... args) { vprint(os, to_string_view(format_str), - {internal::make_args_checked(format_str, args...)}); + internal::make_args_checked(format_str, args...)); } FMT_END_NAMESPACE diff --git a/include/spdlog/fmt/bundled/posix.h b/include/spdlog/fmt/bundled/posix.h index e68e226f..0e7bc646 100644 --- a/include/spdlog/fmt/bundled/posix.h +++ b/include/spdlog/fmt/bundled/posix.h @@ -1,321 +1,2 @@ -// A C++ interface to POSIX functions. -// -// 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 -#include // for locale_t -#include -#include // for strtod_l - -#include - -#if defined __APPLE__ || defined(__FreeBSD__) -# include // for LC_NUMERIC_MASK on OS X -#endif - -#include "format.h" - -// UWP doesn't provide _pipe. -#if FMT_HAS_INCLUDE("winapifamily.h") -# include -#endif -#if FMT_HAS_INCLUDE("fcntl.h") && \ - (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) -# include // 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 | - +---------------+-----------------------------+ - | wcstring_view | basic_cstring_view | - +---------------+-----------------------------+ - - This class is most useful as a parameter type to allow passing - different types of strings to a function, for example:: - - template - std::string format(cstring_view format_str, const Args & ... args); - - format("{}", 42); - format(std::string("{}"), 42); - \endrst - */ -template 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& 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; -using wcstring_view = basic_cstring_view; - -// 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 - 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_ +#include "os.h" +#warning "fmt/posix.h is deprecated; use fmt/os.h instead" \ No newline at end of file diff --git a/include/spdlog/fmt/bundled/printf.h b/include/spdlog/fmt/bundled/printf.h index a2fa945d..a7902280 100644 --- a/include/spdlog/fmt/bundled/printf.h +++ b/include/spdlog/fmt/bundled/printf.h @@ -28,7 +28,7 @@ template struct int_checker { template <> struct int_checker { template static bool fits_in_int(T value) { - return value >= std::numeric_limits::min() && + return value >= (std::numeric_limits::min)() && value <= max_value(); } static bool fits_in_int(int) { return true; } @@ -303,6 +303,8 @@ class printf_arg_formatter : public internal::arg_formatter_base { }; template struct printf_formatter { + printf_formatter() = delete; + template auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { return ctx.begin(); @@ -320,6 +322,7 @@ template class basic_printf_context { public: /** The character type for the output. */ using char_type = Char; + using iterator = OutputIt; using format_arg = basic_format_arg; template using formatter_type = printf_formatter; @@ -355,6 +358,8 @@ template class basic_printf_context { OutputIt out() { return out_; } void advance_to(OutputIt it) { out_ = it; } + internal::locale_ref locale() { return {}; } + format_arg arg(int id) const { return args_.get(id); } basic_format_parse_context& parse_context() { return parse_ctx_; } @@ -406,8 +411,9 @@ basic_printf_context::get_arg(int arg_index) { } template -int basic_printf_context::parse_header( - const Char*& it, const Char* end, format_specs& specs) { +int basic_printf_context::parse_header(const Char*& it, + const Char* end, + format_specs& specs) { int arg_index = -1; char_type c = *it; if (c >= '0' && c <= '9') { @@ -476,8 +482,8 @@ OutputIt basic_printf_context::format() { specs.precision = parse_nonnegative_int(it, end, eh); } else if (c == '*') { ++it; - specs.precision = - static_cast(visit_format_arg(internal::printf_precision_handler(), get_arg())); + specs.precision = static_cast( + visit_format_arg(internal::printf_precision_handler(), get_arg())); } else { specs.precision = 0; } @@ -596,7 +602,8 @@ inline format_arg_store make_wprintf_args( template > inline std::basic_string vsprintf( - const S& format, basic_format_args> args) { + const S& format, + basic_format_args>> args) { basic_memory_buffer buffer; printf(buffer, to_string_view(format), args); return to_string(buffer); @@ -615,12 +622,13 @@ template ::value, char_t>> inline std::basic_string sprintf(const S& format, const Args&... args) { using context = basic_printf_context_t; - return vsprintf(to_string_view(format), {make_format_args(args...)}); + return vsprintf(to_string_view(format), make_format_args(args...)); } template > -inline int vfprintf(std::FILE* f, const S& format, - basic_format_args> args) { +inline int vfprintf( + std::FILE* f, const S& format, + basic_format_args>> args) { basic_memory_buffer buffer; printf(buffer, to_string_view(format), args); std::size_t size = buffer.size(); @@ -643,12 +651,13 @@ template ; return vfprintf(f, to_string_view(format), - {make_format_args(args...)}); + make_format_args(args...)); } template > -inline int vprintf(const S& format, - basic_format_args> args) { +inline int vprintf( + const S& format, + basic_format_args>> args) { return vfprintf(stdout, to_string_view(format), args); } @@ -666,12 +675,13 @@ template >; return vprintf(to_string_view(format_str), - {make_format_args(args...)}); + make_format_args(args...)); } template > -inline int vfprintf(std::basic_ostream& os, const S& format, - basic_format_args> args) { +inline int vfprintf( + std::basic_ostream& os, const S& format, + basic_format_args>> args) { basic_memory_buffer buffer; printf(buffer, to_string_view(format), args); internal::write(os, buffer); @@ -682,9 +692,9 @@ inline int vfprintf(std::basic_ostream& os, const S& format, template > -typename ArgFormatter::iterator vprintf(internal::buffer& out, - basic_string_view format_str, - basic_format_args args) { +typename ArgFormatter::iterator vprintf( + internal::buffer& out, basic_string_view format_str, + basic_format_args> args) { typename ArgFormatter::iterator iter(out); Context(iter, format_str, args).template format(); return iter; @@ -704,7 +714,7 @@ inline int fprintf(std::basic_ostream& os, const S& format_str, const Args&... args) { using context = basic_printf_context_t; return vfprintf(os, to_string_view(format_str), - {make_format_args(args...)}); + make_format_args(args...)); } FMT_END_NAMESPACE diff --git a/include/spdlog/fmt/bundled/ranges.h b/include/spdlog/fmt/bundled/ranges.h index 6110fdaf..f8f9adb7 100644 --- a/include/spdlog/fmt/bundled/ranges.h +++ b/include/spdlog/fmt/bundled/ranges.h @@ -12,7 +12,9 @@ #ifndef FMT_RANGES_H_ #define FMT_RANGES_H_ +#include #include + #include "format.h" // output only up to N items from the range. @@ -104,10 +106,7 @@ struct is_range_< /// tuple_size and tuple_element check. template class is_tuple_like_ { template - static auto check(U* p) - -> decltype(std::tuple_size::value, - (void)std::declval::type>(), - int()); + static auto check(U* p) -> decltype(std::tuple_size::value, int()); template static void check(...); public: @@ -360,6 +359,29 @@ FMT_CONSTEXPR tuple_arg_join join(const std::tuple& tuple, 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 +arg_join>, char> join( + std::initializer_list list, string_view sep) { + return join(std::begin(list), std::end(list), sep); +} + +template +arg_join>, wchar_t> join( + std::initializer_list list, wstring_view sep) { + return join(std::begin(list), std::end(list), sep); +} + FMT_END_NAMESPACE #endif // FMT_RANGES_H_ diff --git a/src/fmt.cpp b/src/fmt.cpp index c196ab1b..7d42f084 100644 --- a/src/fmt.cpp +++ b/src/fmt.cpp @@ -1,71 +1,62 @@ -#ifndef SPDLOG_COMPILED_LIB -#error Please define SPDLOG_COMPILED_LIB to compile this file. -#endif - // Slightly modified version of fmt lib's format.cc source file. // Copyright (c) 2012 - 2016, Victor Zverovich // All rights reserved. + +#ifndef SPDLOG_COMPILED_LIB +#error Please define SPDLOG_COMPILED_LIB to compile this file. +#endif + #if !defined(SPDLOG_FMT_EXTERNAL) #include + FMT_BEGIN_NAMESPACE namespace internal { -template -int format_float(char *buf, std::size_t size, const char *format, int precision, T value) -{ +template +int format_float(char* buf, std::size_t size, const char* format, int precision, + T value) { #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 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 // Suppress the warning about nonliteral format string. - auto snprintf_ptr = FMT_SNPRINTF; - return precision < 0 ? snprintf_ptr(buf, size, format, value) : snprintf_ptr(buf, size, format, precision, value); + 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); } -struct sprintf_specs -{ +struct sprintf_specs { int precision; char type; bool alt : 1; - template + template constexpr sprintf_specs(basic_format_specs specs) - : precision(specs.precision) - , type(specs.type) - , alt(specs.alt) - {} - - constexpr bool has_precision() const - { - return precision >= 0; - } + : precision(specs.precision), type(specs.type), alt(specs.alt) {} + + constexpr bool has_precision() const { return precision >= 0; } }; // This is deprecated and is kept only to preserve ABI compatibility. -template -char *sprintf_format(Double value, internal::buffer &buf, sprintf_specs specs) -{ +template +char* sprintf_format(Double value, internal::buffer& buf, + sprintf_specs specs) { // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. FMT_ASSERT(buf.capacity() != 0, "empty buffer"); // Build format string. - enum - { - max_format_size = 10 - }; // longest format: %#-*.*Lg + enum { max_format_size = 10 }; // longest format: %#-*.*Lg char format[max_format_size]; - char *format_ptr = format; + char* format_ptr = format; *format_ptr++ = '%'; - if (specs.alt || !specs.type) - *format_ptr++ = '#'; - if (specs.precision >= 0) - { + if (specs.alt || !specs.type) *format_ptr++ = '#'; + if (specs.precision >= 0) { *format_ptr++ = '.'; *format_ptr++ = '*'; } - if (std::is_same::value) - *format_ptr++ = 'L'; + if (std::is_same::value) *format_ptr++ = 'L'; char type = specs.type; @@ -74,66 +65,51 @@ char *sprintf_format(Double value, internal::buffer &buf, sprintf_specs sp else if (type == 0 || type == 'n') type = 'g'; #if FMT_MSC_VER - if (type == 'F') - { - // MSVC's printf doesn't support 'F'. - type = 'f'; - } + if (type == 'F') { + // MSVC's printf doesn't support 'F'. + type = 'f'; + } #endif *format_ptr++ = type; *format_ptr = '\0'; // Format using snprintf. - char *start = nullptr; - char *decimal_point_pos = nullptr; - for (;;) - { + char* start = nullptr; + char* decimal_point_pos = nullptr; + for (;;) { std::size_t buffer_size = buf.capacity(); start = &buf[0]; - int result = format_float(start, buffer_size, format, specs.precision, value); - if (result >= 0) - { + int result = + format_float(start, buffer_size, format, specs.precision, value); + if (result >= 0) { unsigned n = internal::to_unsigned(result); - if (n < buf.capacity()) - { + if (n < buf.capacity()) { // Find the decimal point. auto p = buf.data(), end = p + n; - if (*p == '+' || *p == '-') - ++p; - if (specs.type != 'a' && specs.type != 'A') - { - while (p < end && *p >= '0' && *p <= '9') - ++p; - if (p < end && *p != 'e' && *p != 'E') - { + if (*p == '+' || *p == '-') ++p; + if (specs.type != 'a' && specs.type != 'A') { + while (p < end && *p >= '0' && *p <= '9') ++p; + if (p < end && *p != 'e' && *p != 'E') { decimal_point_pos = p; - if (!specs.type) - { + if (!specs.type) { // Keep only one trailing zero after the decimal point. ++p; - if (*p == '0') - ++p; - while (p != end && *p >= '1' && *p <= '9') - ++p; - char *where = p; - while (p != end && *p == '0') - ++p; - if (p == end || *p < '0' || *p > '9') - { - if (p != end) - std::memmove(where, p, to_unsigned(end - p)); + if (*p == '0') ++p; + while (p != end && *p >= '1' && *p <= '9') ++p; + char* where = p; + while (p != end && *p == '0') ++p; + if (p == end || *p < '0' || *p > '9') { + if (p != end) std::memmove(where, p, to_unsigned(end - p)); n -= static_cast(p - where); } } } } buf.resize(n); - break; // The buffer is large enough - continue with formatting. + break; // The buffer is large enough - continue with formatting. } buf.reserve(n + 1); - } - else - { + } else { // If result is negative we ask to increase the capacity by at least 1, // but as std::vector, the buffer grows exponentially. buf.reserve(buf.capacity() + 1); @@ -141,18 +117,23 @@ char *sprintf_format(Double value, internal::buffer &buf, sprintf_specs sp } return decimal_point_pos; } -} // namespace internal +} // namespace internal -template FMT_API char *internal::sprintf_format(double, internal::buffer &, sprintf_specs); -template FMT_API char *internal::sprintf_format(long double, internal::buffer &, sprintf_specs); +template FMT_API char* internal::sprintf_format(double, internal::buffer&, + sprintf_specs); +template FMT_API char* internal::sprintf_format(long double, + internal::buffer&, + sprintf_specs); -template struct FMT_API internal::basic_data; +template struct FMT_INSTANTIATION_DEF_API internal::basic_data; // Workaround a bug in MSVC2013 that prevents instantiation of format_float. -int (*instantiate_format_float)(double, int, internal::float_specs, internal::buffer &) = internal::format_float; +int (*instantiate_format_float)(double, int, internal::float_specs, + internal::buffer&) = +internal::format_float; #ifndef FMT_STATIC_THOUSANDS_SEPARATOR -template FMT_API internal::locale_ref::locale_ref(const std::locale &loc); +template FMT_API internal::locale_ref::locale_ref(const std::locale& loc); template FMT_API std::locale internal::locale_ref::get() const; #endif @@ -162,18 +143,28 @@ template FMT_API std::string internal::grouping_impl(locale_ref); template FMT_API char internal::thousands_sep_impl(locale_ref); template FMT_API char internal::decimal_point_impl(locale_ref); -template FMT_API void internal::buffer::append(const char *, const char *); +template FMT_API void internal::buffer::append(const char*, const char*); -template FMT_API void internal::arg_map::init(const basic_format_args &args); +template FMT_API void internal::arg_map::init( + const basic_format_args& args); -template FMT_API std::string internal::vformat(string_view, basic_format_args); +template FMT_API std::string internal::vformat( + string_view, basic_format_args); -template FMT_API format_context::iterator internal::vformat_to(internal::buffer &, string_view, basic_format_args); +template FMT_API format_context::iterator internal::vformat_to( + internal::buffer&, string_view, basic_format_args); -template FMT_API int internal::snprintf_float(double, int, internal::float_specs, internal::buffer &); -template FMT_API int internal::snprintf_float(long double, int, internal::float_specs, internal::buffer &); -template FMT_API int internal::format_float(double, int, internal::float_specs, internal::buffer &); -template FMT_API int internal::format_float(long double, int, internal::float_specs, internal::buffer &); +template FMT_API int internal::snprintf_float(double, int, + internal::float_specs, + internal::buffer&); +template FMT_API int internal::snprintf_float(long double, int, + internal::float_specs, + internal::buffer&); +template FMT_API int internal::format_float(double, int, internal::float_specs, + internal::buffer&); +template FMT_API int internal::format_float(long double, int, + internal::float_specs, + internal::buffer&); // Explicit instantiations for wchar_t. @@ -181,9 +172,11 @@ template FMT_API std::string internal::grouping_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 void internal::buffer::append(const wchar_t *, const wchar_t *); +template FMT_API void internal::buffer::append(const wchar_t*, + const wchar_t*); -template FMT_API std::wstring internal::vformat(wstring_view, basic_format_args); +template FMT_API std::wstring internal::vformat( + wstring_view, basic_format_args); FMT_END_NAMESPACE -#endif +#endif // !SPDLOG_FMT_EXTERNAL \ No newline at end of file