diff --git a/cscore/src/main/native/cpp/Log.cpp b/cscore/src/main/native/cpp/Log.cpp index baf2e5eb98..cf671bb75f 100644 --- a/cscore/src/main/native/cpp/Log.cpp +++ b/cscore/src/main/native/cpp/Log.cpp @@ -8,8 +8,8 @@ void cs::NamedLogV(wpi::Logger& logger, unsigned int level, const char* file, unsigned int line, std::string_view name, fmt::string_view format, fmt::format_args args) { fmt::memory_buffer out; - fmt::format_to(out, "{}: ", name); - fmt::vformat_to(out, format, args); + fmt::format_to(fmt::appender{out}, "{}: ", name); + fmt::vformat_to(fmt::appender{out}, format, args); out.push_back('\0'); logger.DoLog(level, file, line, out.data()); } diff --git a/wpilibc/src/main/native/cpp/Errors.cpp b/wpilibc/src/main/native/cpp/Errors.cpp index 01cef389cc..0ff4dc1699 100644 --- a/wpilibc/src/main/native/cpp/Errors.cpp +++ b/wpilibc/src/main/native/cpp/Errors.cpp @@ -59,8 +59,8 @@ void frc::ReportErrorV(int32_t status, const char* fileName, int lineNumber, return; } fmt::memory_buffer out; - fmt::format_to(out, "{}: ", GetErrorMessage(&status)); - fmt::vformat_to(out, format, args); + fmt::format_to(fmt::appender{out}, "{}: ", GetErrorMessage(&status)); + fmt::vformat_to(fmt::appender{out}, format, args); out.push_back('\0'); HAL_SendError(status < 0, status, 0, out.data(), funcName, wpi::GetStackTrace(2).c_str(), 1); @@ -70,8 +70,8 @@ RuntimeError frc::MakeErrorV(int32_t status, const char* fileName, int lineNumber, const char* funcName, fmt::string_view format, fmt::format_args args) { fmt::memory_buffer out; - fmt::format_to(out, "{}: ", GetErrorMessage(&status)); - fmt::vformat_to(out, format, args); + fmt::format_to(fmt::appender{out}, "{}: ", GetErrorMessage(&status)); + fmt::vformat_to(fmt::appender{out}, format, args); return RuntimeError{status, fileName, lineNumber, diff --git a/wpiutil/src/main/native/cpp/Logger.cpp b/wpiutil/src/main/native/cpp/Logger.cpp index 629c14a60d..2b8ec6d7c2 100644 --- a/wpiutil/src/main/native/cpp/Logger.cpp +++ b/wpiutil/src/main/native/cpp/Logger.cpp @@ -20,7 +20,7 @@ void Logger::LogV(unsigned int level, const char* file, unsigned int line, return; } fmt::memory_buffer out; - fmt::vformat_to(out, format, args); + fmt::vformat_to(fmt::appender{out}, format, args); out.push_back('\0'); m_func(level, file, line, out.data()); } diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/chrono.h b/wpiutil/src/main/native/fmtlib/include/fmt/chrono.h index 3cc4b1aa76..c024fd710c 100644 --- a/wpiutil/src/main/native/fmtlib/include/fmt/chrono.h +++ b/wpiutil/src/main/native/fmtlib/include/fmt/chrono.h @@ -287,6 +287,75 @@ inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } inline null<> localtime_s(...) { return null<>(); } inline null<> gmtime_r(...) { return null<>(); } inline null<> gmtime_s(...) { return null<>(); } + +inline auto do_write(const std::tm& time, const std::locale& loc, char format, + char modifier) -> std::string { + auto&& os = std::ostringstream(); + os.imbue(loc); + using iterator = std::ostreambuf_iterator; + const auto& facet = std::use_facet>(loc); + auto end = facet.put(os, os, ' ', &time, format, modifier); + if (end.failed()) FMT_THROW(format_error("failed to format time")); + auto str = os.str(); + if (!detail::is_utf8() || loc == std::locale::classic()) return str; + // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and + // gcc-4. +#if FMT_MSC_VER != 0 || \ + (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) + // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 + // and newer. + using code_unit = wchar_t; +#else + using code_unit = char32_t; +#endif + auto& f = std::use_facet>(loc); + auto mb = std::mbstate_t(); + const char* from_next = nullptr; + code_unit* to_next = nullptr; + constexpr size_t buf_size = 32; + code_unit buf[buf_size] = {}; + auto result = f.in(mb, str.data(), str.data() + str.size(), from_next, buf, + buf + buf_size, to_next); + if (result != std::codecvt_base::ok) + FMT_THROW(format_error("failed to format time")); + str.clear(); + for (code_unit* p = buf; p != to_next; ++p) { + uint32_t c = static_cast(*p); + if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) { + // surrogate pair + ++p; + if (p == to_next || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) { + FMT_THROW(format_error("failed to format time")); + } + c = (c << 10) + static_cast(*p) - 0x35fdc00; + } + if (c < 0x80) { + str.push_back(static_cast(c)); + } else if (c < 0x800) { + str.push_back(static_cast(0xc0 | (c >> 6))); + str.push_back(static_cast(0x80 | (c & 0x3f))); + } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { + str.push_back(static_cast(0xe0 | (c >> 12))); + str.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + str.push_back(static_cast(0x80 | (c & 0x3f))); + } else if (c >= 0x10000 && c <= 0x10ffff) { + str.push_back(static_cast(0xf0 | (c >> 18))); + str.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12))); + str.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + str.push_back(static_cast(0x80 | (c & 0x3f))); + } else { + FMT_THROW(format_error("failed to format time")); + } + } + return str; +} + +template +auto write(OutputIt out, const std::tm& time, const std::locale& loc, + char format, char modifier = 0) -> OutputIt { + auto str = do_write(time, loc, format, modifier); + return std::copy(str.begin(), str.end(), out); +} } // namespace detail FMT_MODULE_EXPORT_BEGIN @@ -408,14 +477,37 @@ FMT_END_DETAIL_NAMESPACE template struct formatter, Char> : formatter { + FMT_CONSTEXPR formatter() { + this->specs = {default_specs, sizeof(default_specs) / sizeof(Char)}; + } + + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + auto it = ctx.begin(); + if (it != ctx.end() && *it == ':') ++it; + auto end = it; + while (end != ctx.end() && *end != '}') ++end; + if (end != it) this->specs = {it, detail::to_unsigned(end - it)}; + return end; + } + template auto format(std::chrono::time_point val, FormatContext& ctx) -> decltype(ctx.out()) { std::tm time = localtime(val); return formatter::format(time, ctx); } + + static constexpr Char default_specs[] = {'%', 'Y', '-', '%', 'm', '-', + '%', 'd', ' ', '%', 'H', ':', + '%', 'M', ':', '%', 'S'}; }; +template +constexpr Char + formatter, + Char>::default_specs[]; + template struct formatter { template FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { @@ -458,66 +550,28 @@ template struct formatter { FMT_BEGIN_DETAIL_NAMESPACE -template FMT_CONSTEXPR const char* get_units() { +template FMT_CONSTEXPR inline const char* get_units() { + if (std::is_same::value) return "as"; + if (std::is_same::value) return "fs"; + if (std::is_same::value) return "ps"; + if (std::is_same::value) return "ns"; + if (std::is_same::value) return "µs"; + if (std::is_same::value) return "ms"; + if (std::is_same::value) return "cs"; + if (std::is_same::value) return "ds"; + if (std::is_same>::value) return "s"; + if (std::is_same::value) return "das"; + if (std::is_same::value) return "hs"; + if (std::is_same::value) return "ks"; + if (std::is_same::value) return "Ms"; + if (std::is_same::value) return "Gs"; + if (std::is_same::value) return "Ts"; + if (std::is_same::value) return "Ps"; + if (std::is_same::value) return "Es"; + if (std::is_same>::value) return "m"; + if (std::is_same>::value) return "h"; return nullptr; } -template <> FMT_CONSTEXPR inline const char* get_units() { - return "as"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "fs"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "ps"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "ns"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "µs"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "ms"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "cs"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "ds"; -} -template <> FMT_CONSTEXPR inline const char* get_units>() { - return "s"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "das"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "hs"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "ks"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "Ms"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "Gs"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "Ts"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "Ps"; -} -template <> FMT_CONSTEXPR inline const char* get_units() { - return "Es"; -} -template <> FMT_CONSTEXPR inline const char* get_units>() { - return "m"; -} -template <> FMT_CONSTEXPR inline const char* get_units>() { - return "h"; -} enum class numeric_system { standard, @@ -683,34 +737,50 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, return ptr; } -struct chrono_format_checker { - FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); } +template struct null_chrono_spec_handler { + FMT_CONSTEXPR void unsupported() { + static_cast(this)->unsupported(); + } + FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); } + FMT_CONSTEXPR void on_full_weekday() { unsupported(); } + FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_abbr_month() { unsupported(); } + FMT_CONSTEXPR void on_full_month() { unsupported(); } + FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_us_date() { unsupported(); } + FMT_CONSTEXPR void on_iso_date() { unsupported(); } + FMT_CONSTEXPR void on_12_hour_time() { unsupported(); } + FMT_CONSTEXPR void on_24_hour_time() { unsupported(); } + FMT_CONSTEXPR void on_iso_time() { unsupported(); } + FMT_CONSTEXPR void on_am_pm() { unsupported(); } + FMT_CONSTEXPR void on_duration_value() { unsupported(); } + FMT_CONSTEXPR void on_duration_unit() { unsupported(); } + FMT_CONSTEXPR void on_utc_offset() { unsupported(); } + FMT_CONSTEXPR void on_tz_name() { unsupported(); } +}; + +struct chrono_format_checker : null_chrono_spec_handler { + FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); } template FMT_CONSTEXPR void on_text(const Char*, const Char*) {} - FMT_NORETURN void on_abbr_weekday() { report_no_date(); } - FMT_NORETURN void on_full_weekday() { report_no_date(); } - FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); } - FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); } - FMT_NORETURN void on_abbr_month() { report_no_date(); } - FMT_NORETURN void on_full_month() { report_no_date(); } FMT_CONSTEXPR void on_24_hour(numeric_system) {} FMT_CONSTEXPR void on_12_hour(numeric_system) {} FMT_CONSTEXPR void on_minute(numeric_system) {} FMT_CONSTEXPR void on_second(numeric_system) {} - FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); } - FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); } - FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); } - FMT_NORETURN void on_us_date() { report_no_date(); } - FMT_NORETURN void on_iso_date() { report_no_date(); } FMT_CONSTEXPR void on_12_hour_time() {} FMT_CONSTEXPR void on_24_hour_time() {} FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} FMT_CONSTEXPR void on_duration_value() {} FMT_CONSTEXPR void on_duration_unit() {} - FMT_NORETURN void on_utc_offset() { report_no_date(); } - FMT_NORETURN void on_tz_name() { report_no_date(); } }; template ::value)> @@ -957,14 +1027,9 @@ struct chrono_formatter { void format_localized(const tm& time, char format, char modifier = 0) { if (isnan(val)) return write_nan(); - auto locale = localized ? context.locale().template get() - : std::locale::classic(); - auto& facet = std::use_facet>(locale); - std::basic_ostringstream os; - os.imbue(locale); - facet.put(os, os, ' ', &time, format, modifier); - auto str = os.str(); - std::copy(str.begin(), str.end(), out); + const auto& loc = localized ? context.locale().template get() + : std::locale::classic(); + out = detail::write(out, time, loc, format, modifier); } void on_text(const char_type* begin, const char_type* end) { @@ -1080,6 +1145,46 @@ struct chrono_formatter { FMT_END_DETAIL_NAMESPACE +#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 +using weekday = std::chrono::weekday; +#else +// A fallback version of weekday. +class weekday { + private: + unsigned char value; + + public: + weekday() = default; + explicit constexpr weekday(unsigned wd) noexcept + : value(static_cast(wd != 7 ? wd : 0)) {} + constexpr unsigned c_encoding() const noexcept { return value; } +}; +#endif + +// A rudimentary weekday formatter. +template <> struct formatter { + private: + bool localized = false; + + public: + FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin != end && *begin == 'L') { + ++begin; + localized = true; + } + return begin; + } + + auto format(weekday wd, format_context& ctx) -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_wday = static_cast(wd.c_encoding()); + const auto& loc = localized ? ctx.locale().template get() + : std::locale::classic(); + return detail::write(ctx.out(), time, loc, 'a'); + } +}; + template struct formatter, Char> { private: @@ -1190,7 +1295,7 @@ struct formatter, Char> { ctx, out, d); f.precision = precision_copy; f.localized = localized; - parse_chrono_format(begin, end, f); + detail::parse_chrono_format(begin, end, f); } return detail::write( ctx.out(), basic_string_view(buf.data(), buf.size()), specs_copy); diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/compile.h b/wpiutil/src/main/native/fmtlib/include/fmt/compile.h index 718815357e..00000c92e3 100644 --- a/wpiutil/src/main/native/fmtlib/include/fmt/compile.h +++ b/wpiutil/src/main/native/fmtlib/include/fmt/compile.h @@ -164,7 +164,8 @@ struct is_compiled_string : std::is_base_of {}; #endif #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -template Str> +template Str> struct udl_compiled_string : compiled_string { using char_type = Char; constexpr operator basic_string_view() const { @@ -606,9 +607,23 @@ size_t formatted_size(const S& format_str, const Args&... args) { return format_to(detail::counting_iterator(), format_str, args...).count(); } +template ::value)> +void print(std::FILE* f, const S& format_str, const Args&... args) { + memory_buffer buffer; + format_to(std::back_inserter(buffer), format_str, args...); + detail::print(f, {buffer.data(), buffer.size()}); +} + +template ::value)> +void print(const S& format_str, const Args&... args) { + print(stdout, format_str, args...); +} + #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS inline namespace literals { -template +template constexpr detail::udl_compiled_string< remove_cvref_t, sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/core.h b/wpiutil/src/main/native/fmtlib/include/fmt/core.h index 9db39267a3..4c1f5e2c4b 100644 --- a/wpiutil/src/main/native/fmtlib/include/fmt/core.h +++ b/wpiutil/src/main/native/fmtlib/include/fmt/core.h @@ -1,4 +1,4 @@ -// Formatting library for C++ - the core API +// Formatting library for C++ - the core API for char/UTF-8 // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. @@ -8,15 +8,15 @@ #ifndef FMT_CORE_H_ #define FMT_CORE_H_ -#include // INT_MAX -#include // std::FILE +#include // std::FILE #include #include +#include #include #include // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 70104 +#define FMT_VERSION 80000 #ifdef __clang__ # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) @@ -87,7 +87,7 @@ // GCC doesn't allow throw in constexpr until version 6 (bug 67371). #ifndef FMT_USE_CONSTEXPR # define FMT_USE_CONSTEXPR \ - (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1920 || \ + (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \ (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ !FMT_NVCC && !FMT_ICC_VERSION #endif @@ -253,9 +253,10 @@ # endif #else # define FMT_CLASS_API -# if defined(__GNUC__) || defined(__clang__) -# define FMT_API __attribute__((visibility("default"))) -# define FMT_EXTERN_TEMPLATE_API FMT_API +# if defined(FMT_EXPORT) || defined(FMT_SHARED) +# if defined(__GNUC__) || defined(__clang__) +# define FMT_API __attribute__((visibility("default"))) +# endif # endif #endif #ifndef FMT_API @@ -283,8 +284,16 @@ # define FMT_UNICODE !FMT_MSC_VER #endif -#ifndef FMT_COMPILE_TIME_CHECKS -# define FMT_COMPILE_TIME_CHECKS 0 +#ifndef FMT_CONSTEVAL +# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ + __cplusplus > 201703L) || \ + (defined(__cpp_consteval) && \ + !FMT_MSC_VER) // consteval is broken in MSVC. +# define FMT_CONSTEVAL consteval +# define FMT_HAS_CONSTEVAL +# else +# define FMT_CONSTEVAL +# endif #endif #ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS @@ -319,7 +328,9 @@ 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 {}; +struct monostate { + constexpr monostate() {} +}; // An enable_if helper to be used in template parameters which results in much // shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed @@ -723,6 +734,23 @@ inline auto get_container(std::back_insert_iterator it) return *accessor(it).container; } +template +FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + while (begin != end) *out++ = static_cast(*begin++); + return out; +} + +template ::value)> +FMT_CONSTEXPR auto copy_str(const Char* begin, const Char* end, Char* out) + -> Char* { + if (is_constant_evaluated()) + return copy_str(begin, end, out); + auto size = to_unsigned(end - begin); + memcpy(out, begin, size); + return out + size; +} + /** \rst A contiguous memory buffer with an optional growing ability. It is an internal @@ -746,6 +774,7 @@ template class buffer { capacity_(cap) {} ~buffer() = default; + buffer(buffer&&) = default; /** Sets the buffer data and capacity. */ void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT { @@ -762,7 +791,6 @@ template class buffer { buffer(const buffer&) = delete; void operator=(const buffer&) = delete; - buffer(buffer&&) = default; auto begin() FMT_NOEXCEPT -> T* { return ptr_; } auto end() FMT_NOEXCEPT -> T* { return ptr_ + size_; } @@ -847,7 +875,12 @@ class iterator_buffer final : public Traits, public buffer { void grow(size_t) final FMT_OVERRIDE { if (this->size() == buffer_size) flush(); } - void flush(); + + void flush() { + auto size = this->size(); + this->clear(); + out_ = copy_str(data_, data_ + this->limit(size), out_); + } public: explicit iterator_buffer(OutputIt out, size_t n = buffer_size) @@ -914,7 +947,6 @@ template class counting_buffer final : public buffer { public: counting_buffer() : buffer(data_, 0, buffer_size) {} - counting_buffer(counting_buffer&&) : buffer(data_, 0, buffer_size) {} auto count() -> size_t { return count_ + this->size(); } }; @@ -1101,6 +1133,7 @@ template class value { using char_type = typename Context::char_type; union { + monostate no_value; int int_value; unsigned uint_value; long long long_long_value; @@ -1118,7 +1151,8 @@ template class value { named_arg_value named_args; }; - constexpr FMT_INLINE value(int val = 0) : int_value(val) {} + constexpr FMT_INLINE value() : no_value() {} + constexpr FMT_INLINE value(int val) : int_value(val) {} constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} constexpr FMT_INLINE value(long long val) : long_long_value(val) {} constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} @@ -1141,7 +1175,7 @@ template class value { FMT_INLINE value(const named_arg_info* args, size_t size) : named_args{args, size} {} - template FMT_INLINE value(const T& val) { + template FMT_CONSTEXPR FMT_INLINE value(const T& val) { custom.value = &val; // Get the formatter type through the context to allow different contexts // have different extension points, e.g. `formatter` for `format` and @@ -1463,6 +1497,12 @@ FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( FMT_BEGIN_DETAIL_NAMESPACE +template +auto copy_str(InputIt begin, InputIt end, appender out) -> appender { + get_container(out).append(begin, end); + return out; +} + #if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 // A workaround for gcc 4.8 to make void_t work in a SFINAE context. template struct void_t_impl { using type = void; }; @@ -1539,7 +1579,7 @@ FMT_CONSTEXPR FMT_INLINE auto make_arg(const T& val) -> value { !std::is_same::value, "Cannot format an argument. To make type T formattable provide a " "formatter specialization: https://fmt.dev/latest/api.html#udt"); - return arg; + return {arg}; } template class basic_format_context { FMT_CONSTEXPR auto arg(basic_string_view name) -> format_arg { return args_.get(name); } - auto arg_id(basic_string_view name) -> int { + FMT_CONSTEXPR auto arg_id(basic_string_view name) -> int { return args_.get_id(name); } auto args() const -> const basic_format_args& { @@ -1948,7 +1988,7 @@ template class specs_setter { FMT_CONSTEXPR void on_localized() { specs_.localized = true; } FMT_CONSTEXPR void on_zero() { - specs_.align = align::numeric; + if (specs_.align == align::none) specs_.align = align::numeric; specs_.fill[0] = Char('0'); } @@ -2063,25 +2103,27 @@ inline auto find(const char* first, const char* last, char value, // Parses the range [begin, end) as an unsigned integer. This function assumes // that the range is non-empty and the first character is a digit. -template +template FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, - ErrorHandler&& eh) -> int { + int error_value) noexcept -> int { FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); - unsigned value = 0; - // Convert to unsigned to prevent a warning. - const unsigned max_int = to_unsigned(INT_MAX); - unsigned big = max_int / 10; + unsigned value = 0, prev = 0; + auto p = begin; do { - // Check for overflow. - if (value > big) { - value = max_int + 1; - break; - } - value = value * 10 + unsigned(*begin - '0'); - ++begin; - } while (begin != end && '0' <= *begin && *begin <= '9'); - if (value > max_int) eh.on_error("number is too big"); - return static_cast(value); + prev = value; + value = value * 10 + unsigned(*p - '0'); + ++p; + } while (p != end && '0' <= *p && *p <= '9'); + auto num_digits = p - begin; + begin = p; + if (num_digits <= std::numeric_limits::digits10) + return static_cast(value); + // Check for overflow. + const unsigned max = to_unsigned((std::numeric_limits::max)()); + return num_digits == std::numeric_limits::digits10 + 1 && + prev * 10ull + unsigned(p[-1] - '0') <= max + ? static_cast(value) + : error_value; } // Parses fill and alignment. @@ -2137,7 +2179,8 @@ FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, if (c >= '0' && c <= '9') { int index = 0; if (c != '0') - index = parse_nonnegative_int(begin, end, handler); + index = + parse_nonnegative_int(begin, end, (std::numeric_limits::max)()); else ++begin; if (begin == end || (*begin != '}' && *begin != ':')) @@ -2170,6 +2213,7 @@ FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end, template FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, Handler&& handler) -> const Char* { + using detail::auto_id; struct width_adapter { Handler& handler; @@ -2179,13 +2223,17 @@ FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, handler.on_dynamic_width(id); } FMT_CONSTEXPR void on_error(const char* message) { - handler.on_error(message); + if (message) handler.on_error(message); } }; FMT_ASSERT(begin != end, ""); if ('0' <= *begin && *begin <= '9') { - handler.on_width(parse_nonnegative_int(begin, end, handler)); + int width = parse_nonnegative_int(begin, end, -1); + if (width != -1) + handler.on_width(width); + else + handler.on_error("number is too big"); } else if (*begin == '{') { ++begin; if (begin != end) begin = parse_arg_id(begin, end, width_adapter{handler}); @@ -2199,6 +2247,7 @@ FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, template FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, Handler&& handler) -> const Char* { + using detail::auto_id; struct precision_adapter { Handler& handler; @@ -2208,14 +2257,18 @@ FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, handler.on_dynamic_precision(id); } FMT_CONSTEXPR void on_error(const char* message) { - handler.on_error(message); + if (message) handler.on_error(message); } }; ++begin; auto c = begin != end ? *begin : Char(); if ('0' <= c && c <= '9') { - handler.on_precision(parse_nonnegative_int(begin, end, handler)); + auto precision = parse_nonnegative_int(begin, end, -1); + if (precision != -1) + handler.on_precision(precision); + else + handler.on_error("number is too big"); } else if (c == '{') { ++begin; if (begin != end) @@ -2309,7 +2362,7 @@ FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end, arg_id = handler.on_arg_id(id); } FMT_CONSTEXPR void on_error(const char* message) { - handler.on_error(message); + if (message) handler.on_error(message); } }; @@ -2416,8 +2469,8 @@ class compile_parse_context public: explicit FMT_CONSTEXPR compile_parse_context( - basic_string_view format_str, int num_args = INT_MAX, - ErrorHandler eh = {}) + basic_string_view format_str, + int num_args = (std::numeric_limits::max)(), ErrorHandler eh = {}) : base(format_str, eh), num_args_(num_args) {} FMT_CONSTEXPR auto next_arg_id() -> int { @@ -2783,17 +2836,13 @@ template class basic_format_string { template >::value)> -#if FMT_COMPILE_TIME_CHECKS - consteval -#endif - basic_format_string(const S& s) - : str_(s) { + FMT_CONSTEVAL basic_format_string(const S& s) : str_(s) { static_assert( detail::count< (std::is_base_of>::value && std::is_reference::value)...>() == 0, "passing views as lvalues is disallowed"); -#if FMT_COMPILE_TIME_CHECKS +#ifdef FMT_HAS_CONSTEVAL if constexpr (detail::count_named_args() == 0) { using checker = detail::format_string_checker...>; @@ -2910,8 +2959,8 @@ FMT_INLINE auto formatted_size(format_string fmt, T&&... args) -> size_t { return buf.count(); } -FMT_API void vprint(string_view, format_args); -FMT_API void vprint(std::FILE*, string_view, format_args); +FMT_API void vprint(string_view fmt, format_args args); +FMT_API void vprint(std::FILE* f, string_view fmt, format_args args); /** \rst diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/format-inl.h b/wpiutil/src/main/native/fmtlib/include/fmt/format-inl.h index 4be6c1c36a..a802aea5e1 100644 --- a/wpiutil/src/main/native/fmtlib/include/fmt/format-inl.h +++ b/wpiutil/src/main/native/fmtlib/include/fmt/format-inl.h @@ -103,23 +103,21 @@ template Locale locale_ref::get() const { return locale_ ? *static_cast(locale_) : std::locale(); } -template FMT_FUNC std::string grouping_impl(locale_ref loc) { - return std::use_facet>(loc.get()).grouping(); -} -template FMT_FUNC Char thousands_sep_impl(locale_ref loc) { - return std::use_facet>(loc.get()) - .thousands_sep(); +template +FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { + auto& facet = std::use_facet>(loc.get()); + auto grouping = facet.grouping(); + auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); + return {std::move(grouping), thousands_sep}; } template FMT_FUNC Char decimal_point_impl(locale_ref loc) { return std::use_facet>(loc.get()) .decimal_point(); } #else -template FMT_FUNC std::string grouping_impl(locale_ref) { - return "\03"; -} -template FMT_FUNC Char thousands_sep_impl(locale_ref) { - return FMT_STATIC_THOUSANDS_SEPARATOR; +template +FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result { + return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR}; } template FMT_FUNC Char decimal_point_impl(locale_ref) { return '.'; @@ -148,8 +146,7 @@ template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) { } #if __cplusplus < 201703L -template -constexpr const typename basic_data::digit_pair basic_data::digits[]; +template constexpr const char basic_data::digits[][2]; template constexpr const char basic_data::hex_digits[]; template constexpr const char basic_data::signs[]; template constexpr const unsigned basic_data::prefixes[]; @@ -641,8 +638,8 @@ inline uint64_t power_of_10_64(int exp) { // error: the size of the region (lower, upper) outside of which numbers // definitely do not round to value (Delta in Grisu3). template -FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, - int& exp, Handler& handler) { +FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp, + Handler& handler) { const fp one(1ULL << -value.e, value.e); // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be // zero because it contains a product of two 64-bit numbers with MSB set (due @@ -1920,7 +1917,7 @@ bool is_center_integer(typename float_info::carrier_uint two_f, int exponent, } // Remove trailing zeros from n and return the number of zeros removed (float) -FMT_ALWAYS_INLINE int remove_trailing_zeros(uint32_t& n) FMT_NOEXCEPT { +FMT_INLINE int remove_trailing_zeros(uint32_t& n) FMT_NOEXCEPT { #ifdef FMT_BUILTIN_CTZ int t = FMT_BUILTIN_CTZ(n); #else @@ -1948,7 +1945,7 @@ FMT_ALWAYS_INLINE int remove_trailing_zeros(uint32_t& n) FMT_NOEXCEPT { } // Removes trailing zeros and returns the number of zeros removed (double) -FMT_ALWAYS_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT { +FMT_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT { #ifdef FMT_BUILTIN_CTZLL int t = FMT_BUILTIN_CTZLL(n); #else @@ -2034,8 +2031,7 @@ FMT_ALWAYS_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT { // The main algorithm for shorter interval case template -FMT_ALWAYS_INLINE decimal_fp shorter_interval_case(int exponent) - FMT_NOEXCEPT { +FMT_INLINE decimal_fp shorter_interval_case(int exponent) FMT_NOEXCEPT { decimal_fp ret_value; // Compute k and beta const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); @@ -2501,19 +2497,6 @@ int snprintf_float(T value, int precision, float_specs specs, return exp - fraction_size; } } - -struct stringifier { - template FMT_INLINE std::string operator()(T value) const { - return to_string(value); - } - std::string operator()(basic_format_arg::handle h) const { - memory_buffer buf; - format_parse_context parse_ctx({}); - format_context format_ctx(buffer_appender(buf), {}, {}); - h.format(parse_ctx, format_ctx); - return to_string(buf); - } -}; } // namespace detail template <> struct formatter { @@ -2577,12 +2560,9 @@ FMT_FUNC void report_system_error(int error_code, } FMT_FUNC std::string vformat(string_view fmt, format_args args) { - if (fmt.size() == 2 && detail::equal2(fmt.data(), "{}")) { - auto arg = args.get(0); - if (!arg) detail::error_handler().on_error("argument not found"); - return visit_format_arg(detail::stringifier(), arg); - } - memory_buffer buffer; + // Don't optimize the "{}" case to keep the binary size small and because it + // can be better optimized in fmt::format anyway. + auto buffer = memory_buffer(); detail::vformat_to(buffer, fmt, args); return to_string(buffer); } @@ -2595,13 +2575,12 @@ extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // } // namespace detail #endif -FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { - memory_buffer buffer; - detail::vformat_to(buffer, format_str, args); +namespace detail { +FMT_FUNC void print(std::FILE* f, string_view text) { #ifdef _WIN32 auto fd = _fileno(f); if (_isatty(fd)) { - detail::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size())); + detail::utf8_to_utf16 u16(string_view(text.data(), text.size())); auto written = detail::dword(); if (detail::WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), u16.c_str(), static_cast(u16.size()), @@ -2612,7 +2591,14 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { // redirected to NUL. } #endif - detail::fwrite_fully(buffer.data(), 1, buffer.size(), f); + detail::fwrite_fully(text.data(), 1, text.size(), f); +} +} // namespace detail + +FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { + memory_buffer buffer; + detail::vformat_to(buffer, format_str, args); + detail::print(f, {buffer.data(), buffer.size()}); } #ifdef _WIN32 diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/format.h b/wpiutil/src/main/native/fmtlib/include/fmt/format.h index 6df5d849de..03ae1c961a 100644 --- a/wpiutil/src/main/native/fmtlib/include/fmt/format.h +++ b/wpiutil/src/main/native/fmtlib/include/fmt/format.h @@ -184,7 +184,7 @@ namespace detail { # endif # endif -inline int clz(uint32_t x) { +inline auto clz(uint32_t x) -> int { unsigned long r = 0; _BitScanReverse(&r, x); FMT_ASSERT(x != 0, ""); @@ -196,7 +196,7 @@ inline int clz(uint32_t x) { } # define FMT_BUILTIN_CLZ(n) detail::clz(n) -inline int clzll(uint64_t x) { +inline auto clzll(uint64_t x) -> int { unsigned long r = 0; # ifdef _WIN64 _BitScanReverse64(&r, x); @@ -212,7 +212,7 @@ inline int clzll(uint64_t x) { } # define FMT_BUILTIN_CLZLL(n) detail::clzll(n) -inline int ctz(uint32_t x) { +inline auto ctz(uint32_t x) -> int { unsigned long r = 0; _BitScanForward(&r, x); FMT_ASSERT(x != 0, ""); @@ -221,7 +221,7 @@ inline int ctz(uint32_t x) { } # define FMT_BUILTIN_CTZ(n) detail::ctz(n) -inline int ctzll(uint64_t x) { +inline auto ctzll(uint64_t x) -> int { unsigned long r = 0; FMT_ASSERT(x != 0, ""); FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. @@ -258,14 +258,14 @@ namespace detail { // undefined behavior (e.g. due to type aliasing). // Example: uint64_t d = bit_cast(2.718); template -inline Dest bit_cast(const Source& source) { +inline auto bit_cast(const Source& source) -> Dest { static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); Dest dest; std::memcpy(&dest, &source, sizeof(dest)); return dest; } -inline bool is_big_endian() { +inline auto is_big_endian() -> bool { const auto u = 1u; struct bytes { char data[sizeof(u)]; @@ -288,26 +288,28 @@ struct fallback_uintptr { }; #ifdef UINTPTR_MAX using uintptr_t = ::uintptr_t; -inline uintptr_t to_uintptr(const void* p) { return bit_cast(p); } +inline auto to_uintptr(const void* p) -> uintptr_t { + return bit_cast(p); +} #else using uintptr_t = fallback_uintptr; -inline fallback_uintptr to_uintptr(const void* p) { +inline auto to_uintptr(const void* p) -> fallback_uintptr { return fallback_uintptr(p); } #endif // Returns the largest possible value for type T. Same as // std::numeric_limits::max() but shorter and not affected by the max macro. -template constexpr T max_value() { +template constexpr auto max_value() -> T { return (std::numeric_limits::max)(); } -template constexpr int num_bits() { +template constexpr auto num_bits() -> int { return std::numeric_limits::digits; } // std::numeric_limits::digits may return 0 for 128-bit ints. -template <> constexpr int num_bits() { return 128; } -template <> constexpr int num_bits() { return 128; } -template <> constexpr int num_bits() { +template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return static_cast(sizeof(void*) * std::numeric_limits::digits); } @@ -325,23 +327,24 @@ using iterator_t = decltype(std::begin(std::declval())); template using sentinel_t = decltype(std::end(std::declval())); // A workaround for std::string not having mutable data() until C++17. -template inline Char* get_data(std::basic_string& s) { +template +inline auto get_data(std::basic_string& s) -> Char* { return &s[0]; } template -inline typename Container::value_type* get_data(Container& c) { +inline auto get_data(Container& c) -> typename Container::value_type* { return c.data(); } #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, size_t size) { +template auto make_checked(T* p, size_t size) -> checked_ptr { return {p, size}; } #else template using checked_ptr = T*; -template inline T* make_checked(T* p, size_t) { return p; } +template inline auto make_checked(T* p, size_t) -> T* { return p; } #endif // Attempts to reserve space for n extra characters in the output range. @@ -350,8 +353,9 @@ template ::value)> #if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION __attribute__((no_sanitize("undefined"))) #endif -inline checked_ptr -reserve(std::back_insert_iterator it, size_t n) { +inline auto +reserve(std::back_insert_iterator it, size_t n) + -> checked_ptr { Container& c = get_container(it); size_t size = c.size(); c.resize(size + n); @@ -359,13 +363,14 @@ reserve(std::back_insert_iterator it, size_t n) { } template -inline buffer_appender reserve(buffer_appender it, size_t n) { +inline auto reserve(buffer_appender it, size_t n) -> buffer_appender { buffer& buf = get_container(it); buf.try_reserve(buf.size() + n); return it; } -template constexpr Iterator& reserve(Iterator& it, size_t) { +template +constexpr auto reserve(Iterator& it, size_t) -> Iterator& { return it; } @@ -374,10 +379,10 @@ using reserve_iterator = remove_reference_t(), 0))>; template -constexpr T* to_pointer(OutputIt, size_t) { +constexpr auto to_pointer(OutputIt, size_t) -> T* { return nullptr; } -template T* to_pointer(buffer_appender it, size_t n) { +template auto to_pointer(buffer_appender it, size_t n) -> T* { buffer& buf = get_container(it); auto size = buf.size(); if (buf.capacity() < size + n) return nullptr; @@ -386,26 +391,27 @@ template T* to_pointer(buffer_appender it, size_t n) { } template ::value)> -inline std::back_insert_iterator base_iterator( - std::back_insert_iterator& it, - checked_ptr) { +inline auto base_iterator(std::back_insert_iterator& it, + checked_ptr) + -> std::back_insert_iterator { return it; } template -constexpr Iterator base_iterator(Iterator, Iterator it) { +constexpr auto base_iterator(Iterator, Iterator it) -> Iterator { return it; } // is spectacularly slow to compile in C++20 so use a simple fill_n // instead (#1998). template -FMT_CONSTEXPR OutputIt fill_n(OutputIt out, Size count, const T& value) { +FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) + -> OutputIt { for (Size i = 0; i < count; ++i) *out++ = value; return out; } template -FMT_CONSTEXPR20 T* fill_n(T* out, Size count, char value) { +FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* { if (is_constant_evaluated()) { return fill_n(out, count, value); } @@ -419,48 +425,9 @@ using char8_type = char8_t; enum char8_type : unsigned char {}; #endif -template -using needs_conversion = bool_constant< - std::is_same::value_type, - char>::value && - std::is_same::value>; - -template ::value)> -FMT_CONSTEXPR OutputIt copy_str(InputIt begin, InputIt end, OutputIt out) { - while (begin != end) *out++ = *begin++; - return out; -} - -template ::value)> -FMT_CONSTEXPR20 OutChar* copy_str(InputIt begin, InputIt end, OutChar* out) { - if (is_constant_evaluated()) { - return copy_str(begin, end, out); - } - auto size = to_unsigned(end - begin); - std::uninitialized_copy(begin, end, make_checked(out, size)); - return out + size; -} - -template ::value)> -OutputIt copy_str(InputIt begin, InputIt end, OutputIt out) { - while (begin != end) *out++ = static_cast(*begin++); - return out; -} - -template ::value)> -appender copy_str(InputIt begin, InputIt end, appender out) { - get_container(out).append(begin, end); - return out; -} - template -FMT_CONSTEXPR FMT_NOINLINE OutputIt copy_str_noinline(InputIt begin, - InputIt end, - OutputIt out) { +FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, + OutputIt out) -> OutputIt { return copy_str(begin, end, out); } @@ -481,8 +448,8 @@ FMT_CONSTEXPR FMT_NOINLINE OutputIt copy_str_noinline(InputIt begin, * occurs, this pointer will be a guess that depends on the particular * error, but it will always advance at least one byte. */ -FMT_CONSTEXPR inline const char* utf8_decode(const char* s, uint32_t* c, - int* e) { +FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) + -> const char* { constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; constexpr const int shiftc[] = {0, 18, 12, 6, 0}; @@ -538,7 +505,7 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { } template -inline size_t compute_width(basic_string_view s) { +inline auto compute_width(basic_string_view s) -> size_t { return s.size(); } @@ -549,7 +516,7 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) { struct count_code_points { size_t* count; FMT_CONSTEXPR void operator()(uint32_t cp, int error) const { - *count += + *count += detail::to_unsigned( 1 + (error == 0 && cp >= 0x1100 && (cp <= 0x115f || // Hangul Jamo init. consonants @@ -568,26 +535,27 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) { // Miscellaneous Symbols and Pictographs + Emoticons: (cp >= 0x1f300 && cp <= 0x1f64f) || // Supplemental Symbols and Pictographs: - (cp >= 0x1f900 && cp <= 0x1f9ff))); + (cp >= 0x1f900 && cp <= 0x1f9ff)))); } }; for_each_codepoint(s, count_code_points{&num_code_points}); return num_code_points; } -inline size_t compute_width(basic_string_view s) { +inline auto compute_width(basic_string_view s) -> size_t { return compute_width(basic_string_view( reinterpret_cast(s.data()), s.size())); } template -inline size_t code_point_index(basic_string_view s, size_t n) { +inline auto code_point_index(basic_string_view s, size_t n) -> size_t { size_t size = s.size(); return n < size ? n : size; } // 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) { +inline auto code_point_index(basic_string_view s, size_t n) + -> size_t { const char8_type* data = s.data(); size_t num_code_points = 0; for (size_t i = 0, size = s.size(); i != size; ++i) { @@ -618,13 +586,6 @@ void buffer::append(const U* begin, const U* end) { } } -template -void iterator_buffer::flush() { - auto size = this->size(); - this->clear(); - out_ = copy_str(data_, data_ + this->limit(size), out_); -} - template struct is_locale : std::false_type {}; template @@ -633,11 +594,6 @@ struct is_locale> : std::true_type {}; FMT_MODULE_EXPORT_BEGIN -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 {}; - // The number of characters to store in the basic_memory_buffer object itself // to avoid dynamic memory allocation. enum { inline_buffer_size = 500 }; @@ -647,15 +603,7 @@ enum { inline_buffer_size = 500 }; A dynamically growing memory buffer for trivially copyable/constructible types with the first ``SIZE`` elements stored in the object itself. - You can use one of the following type aliases for common character types: - - +----------------+------------------------------+ - | Type | Definition | - +================+==============================+ - | memory_buffer | basic_memory_buffer | - +----------------+------------------------------+ - | wmemory_buffer | basic_memory_buffer | - +----------------+------------------------------+ + You can use the ```memory_buffer`` type alias for ``char`` instead. **Example**:: @@ -732,7 +680,8 @@ class basic_memory_buffer final : public detail::buffer { Moves the content of the other ``basic_memory_buffer`` object to this one. \endrst */ - basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT { + auto operator=(basic_memory_buffer&& other) FMT_NOEXCEPT + -> basic_memory_buffer& { FMT_ASSERT(this != &other, ""); deallocate(); move(other); @@ -740,7 +689,7 @@ class basic_memory_buffer final : public detail::buffer { } // Returns a copy of the allocator associated with this buffer. - Allocator get_allocator() const { return alloc_; } + auto get_allocator() const -> Allocator { return alloc_; } /** Resizes the buffer to contain *count* elements. If T is a POD type new @@ -785,12 +734,15 @@ void basic_memory_buffer::grow(size_t size) { } using memory_buffer = basic_memory_buffer; -using wmemory_buffer = basic_memory_buffer; template struct is_contiguous> : std::true_type { }; +namespace detail { +FMT_API void print(std::FILE*, string_view); +} + /** A formatting error such as invalid format string. */ FMT_CLASS_API class FMT_API format_error : public std::runtime_error { @@ -826,6 +778,33 @@ FMT_INLINE auto make_args_checked(const S& fmt, return {args...}; } +// compile-time support +namespace detail_exported { +#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +template struct fixed_string { + constexpr fixed_string(const Char (&str)[N]) { + detail::copy_str(static_cast(str), + str + N, data); + } + Char data[N]{}; +}; +#endif + +// Converts a compile-time string to basic_string_view. +template +constexpr auto compile_string_to_view(const Char (&s)[N]) + -> basic_string_view { + // Remove trailing NUL character if needed. Won't be present if this is used + // with a 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)}; +} +template +constexpr auto compile_string_to_view(detail::std_string_view s) + -> basic_string_view { + return {s.data(), s.size()}; +} +} // namespace detail_exported + FMT_BEGIN_DETAIL_NAMESPACE inline void throw_format_error(const char* message) { @@ -844,16 +823,16 @@ using is_signed = // Returns true if value is negative, false otherwise. // Same as `value < 0` but doesn't produce warnings if T is an unsigned type. template ::value)> -FMT_CONSTEXPR bool is_negative(T value) { +FMT_CONSTEXPR auto is_negative(T value) -> bool { return value < 0; } template ::value)> -FMT_CONSTEXPR bool is_negative(T) { +FMT_CONSTEXPR auto is_negative(T) -> bool { return false; } template ::value)> -FMT_CONSTEXPR bool is_supported_floating_point(T) { +FMT_CONSTEXPR auto is_supported_floating_point(T) -> uint16_t { return (std::is_same::value && FMT_USE_FLOAT) || (std::is_same::value && FMT_USE_DOUBLE) || (std::is_same::value && FMT_USE_LONG_DOUBLE); @@ -880,8 +859,7 @@ template struct basic_data { static const uint64_t log10_2_significand = 0x4d104d427de7fbcc; // GCC generates slightly better code for pairs than chars. - using digit_pair = char[2]; - FMT_API static constexpr const digit_pair digits[] = { + FMT_API static constexpr const char digits[][2] = { {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, @@ -908,7 +886,7 @@ template struct basic_data { FMT_API static constexpr const char right_padding_shifts[] = {0, 31, 0, 1, 0}; }; -#ifndef FMT_HEADER_ONLY +#ifdef FMT_SHARED // Required for -flto, -fivisibility=hidden and -shared to work extern template struct basic_data; #endif @@ -916,18 +894,7 @@ extern template struct basic_data; // This is a struct rather than an alias to avoid shadowing warnings in gcc. struct data : basic_data<> {}; -// Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). -// This is a function instead of an array to workaround a bug in GCC10 (#1810). -FMT_INLINE uint16_t bsr2log10(int bsr) { - static constexpr uint16_t data[] = { - 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, - 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, - 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, - 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; - return data[bsr]; -} - -template FMT_CONSTEXPR int count_digits_fallback(T n) { +template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { int count = 1; for (;;) { // Integer division is slow so do it for a group of four digits instead @@ -942,31 +909,36 @@ template FMT_CONSTEXPR int count_digits_fallback(T n) { } } #if FMT_USE_INT128 -FMT_CONSTEXPR inline int count_digits(uint128_t n) { +FMT_CONSTEXPR inline auto count_digits(uint128_t n) -> int { return count_digits_fallback(n); } #endif // Returns the number of decimal digits in n. Leading zeros are not counted // except for n == 0 in which case count_digits returns 1. -FMT_CONSTEXPR20 inline int count_digits(uint64_t n) { - if (is_constant_evaluated()) { - return count_digits_fallback(n); - } +FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { #ifdef FMT_BUILTIN_CLZLL - // https://github.com/fmtlib/format-benchmark/blob/master/digits10 - auto t = bsr2log10(FMT_BUILTIN_CLZLL(n | 1) ^ 63); - constexpr const uint64_t zero_or_powers_of_10[] = { - 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), - 10000000000000000000ULL}; - return t - (n < zero_or_powers_of_10[t]); -#else - return count_digits_fallback(n); + if (!is_constant_evaluated()) { + // https://github.com/fmtlib/format-benchmark/blob/master/digits10 + // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). + constexpr uint16_t bsr2log10[] = { + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, + 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; + auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; + constexpr const uint64_t zero_or_powers_of_10[] = { + 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + return t - (n < zero_or_powers_of_10[t]); + } #endif + return count_digits_fallback(n); } // Counts the number of digits in n. BITS = log2(radix). -template FMT_CONSTEXPR int count_digits(UInt n) { +template +FMT_CONSTEXPR auto count_digits(UInt n) -> int { #ifdef FMT_BUILTIN_CLZ if (num_bits() == 32) return (FMT_BUILTIN_CLZ(static_cast(n) | 1) ^ 31) / BITS + 1; @@ -978,65 +950,82 @@ template FMT_CONSTEXPR int count_digits(UInt n) { return num_digits; } -template <> int count_digits<4>(detail::fallback_uintptr n); +template <> auto count_digits<4>(detail::fallback_uintptr n) -> int; -#if FMT_GCC_VERSION || FMT_CLANG_VERSION -# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) -#elif FMT_MSC_VER -# define FMT_ALWAYS_INLINE __forceinline -#else -# define FMT_ALWAYS_INLINE inline -#endif - -#ifdef FMT_BUILTIN_CLZ -// Optional version of count_digits for better performance on 32-bit platforms. -FMT_CONSTEXPR20 inline int count_digits(uint32_t n) { - if (is_constant_evaluated()) { - return count_digits_fallback(n); - } - auto t = bsr2log10(FMT_BUILTIN_CLZ(n | 1) ^ 31); - constexpr const uint32_t zero_or_powers_of_10[] = {0, 0, - FMT_POWERS_OF_10(1U)}; - return t - (n < zero_or_powers_of_10[t]); +// It is a separate function rather than a part of count_digits to workaround +// the lack of static constexpr in constexpr functions. +FMT_INLINE uint64_t count_digits_inc(int n) { + // An optimization by Kendall Willets from https://bit.ly/3uOIQrB. + // This increments the upper 32 bits (log10(T) - 1) when >= T is added. +#define FMT_INC(T) (((sizeof(#T) - 1ull) << 32) - T) + static constexpr uint64_t table[] = { + FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8 + FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64 + FMT_INC(100), FMT_INC(100), FMT_INC(100), // 512 + FMT_INC(1000), FMT_INC(1000), FMT_INC(1000), // 4096 + FMT_INC(10000), FMT_INC(10000), FMT_INC(10000), // 32k + FMT_INC(100000), FMT_INC(100000), FMT_INC(100000), // 256k + FMT_INC(1000000), FMT_INC(1000000), FMT_INC(1000000), // 2048k + FMT_INC(10000000), FMT_INC(10000000), FMT_INC(10000000), // 16M + FMT_INC(100000000), FMT_INC(100000000), FMT_INC(100000000), // 128M + FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000), // 1024M + FMT_INC(1000000000), FMT_INC(1000000000) // 4B + }; + return table[n]; } -#endif -template constexpr int digits10() FMT_NOEXCEPT { +// Optional version of count_digits for better performance on 32-bit platforms. +FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (!is_constant_evaluated()) { + auto inc = count_digits_inc(FMT_BUILTIN_CLZ(n | 1) ^ 31); + return static_cast((n + inc) >> 32); + } +#endif + return count_digits_fallback(n); +} + +template constexpr auto digits10() FMT_NOEXCEPT -> int { return std::numeric_limits::digits10; } -template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } -template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } - -// DEPRECATED! grouping will be merged into thousands_sep. -template FMT_API std::string grouping_impl(locale_ref loc); -template inline std::string grouping(locale_ref loc) { - return grouping_impl(loc); +template <> constexpr auto digits10() FMT_NOEXCEPT -> int { + return 38; } -template <> inline std::string grouping(locale_ref loc) { - return grouping_impl(loc); +template <> constexpr auto digits10() FMT_NOEXCEPT -> int { + return 38; } -template FMT_API Char thousands_sep_impl(locale_ref loc); -template inline Char thousands_sep(locale_ref loc) { - return Char(thousands_sep_impl(loc)); +template struct thousands_sep_result { + std::string grouping; + Char thousands_sep; +}; + +template +FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result; +template +inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { + auto result = thousands_sep_impl(loc); + return {result.grouping, Char(result.thousands_sep)}; } -template <> inline wchar_t thousands_sep(locale_ref loc) { +template <> +inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { return thousands_sep_impl(loc); } -template FMT_API Char decimal_point_impl(locale_ref loc); -template inline Char decimal_point(locale_ref loc) { +template +FMT_API auto decimal_point_impl(locale_ref loc) -> Char; +template inline auto decimal_point(locale_ref loc) -> Char { return Char(decimal_point_impl(loc)); } -template <> inline wchar_t decimal_point(locale_ref loc) { +template <> inline auto decimal_point(locale_ref loc) -> wchar_t { return decimal_point_impl(loc); } // Compares two characters for equality. -template bool equal2(const Char* lhs, const char* rhs) { +template auto equal2(const Char* lhs, const char* rhs) -> bool { return lhs[0] == rhs[0] && lhs[1] == rhs[1]; } -inline bool equal2(const char* lhs, const char* rhs) { +inline auto equal2(const char* lhs, const char* rhs) -> bool { return memcmp(lhs, rhs, 2) == 0; } @@ -1056,9 +1045,8 @@ template struct format_decimal_result { // buffer of specified size. The caller must ensure that the buffer is large // enough. template -FMT_CONSTEXPR20 format_decimal_result format_decimal(Char* out, - UInt value, - int size) { +FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) + -> format_decimal_result { FMT_ASSERT(size >= count_digits(value), "invalid digit count"); out += size; Char* end = out; @@ -1089,8 +1077,8 @@ FMT_CONSTEXPR20 format_decimal_result format_decimal(Char* out, template >::value)> -inline format_decimal_result format_decimal(Iterator out, UInt value, - int size) { +inline auto format_decimal(Iterator out, UInt value, int size) + -> format_decimal_result { // Buffer is large enough to hold all digits (digits10 + 1). Char buffer[digits10() + 1]; auto end = format_decimal(buffer, value, size).end; @@ -1098,8 +1086,8 @@ inline format_decimal_result format_decimal(Iterator out, UInt value, } template -FMT_CONSTEXPR Char* format_uint(Char* buffer, UInt value, int num_digits, - bool upper = false) { +FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, + bool upper = false) -> Char* { buffer += num_digits; Char* end = buffer; do { @@ -1112,8 +1100,8 @@ FMT_CONSTEXPR Char* format_uint(Char* buffer, UInt value, int num_digits, } template -Char* format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, - bool = false) { +auto format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, + bool = false) -> Char* { auto char_digits = std::numeric_limits::digits / 4; int start = (num_digits + char_digits - 1) / char_digits - 1; if (int start_digits = num_digits % char_digits) { @@ -1134,7 +1122,8 @@ Char* format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, } template -inline It format_uint(It out, UInt value, int num_digits, bool upper = false) { +inline auto format_uint(It out, UInt value, int num_digits, bool upper = false) + -> It { if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { format_uint(ptr, value, num_digits, upper); return out; @@ -1148,14 +1137,14 @@ inline It format_uint(It out, UInt value, int num_digits, bool upper = false) { // A converter from UTF-8 to UTF-16. class utf8_to_utf16 { private: - wmemory_buffer buffer_; + basic_memory_buffer buffer_; public: FMT_API explicit utf8_to_utf16(string_view s); operator basic_string_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 {&buffer_[0], size()}; } + auto size() const -> size_t { return buffer_.size() - 1; } + auto c_str() const -> const wchar_t* { return &buffer_[0]; } + auto str() const -> std::wstring { return {&buffer_[0], size()}; } }; namespace dragonbox { @@ -1221,18 +1210,21 @@ template struct decimal_fp { int exponent; }; -template FMT_API decimal_fp to_decimal(T x) FMT_NOEXCEPT; +template +FMT_API auto to_decimal(T x) FMT_NOEXCEPT -> decimal_fp; } // namespace dragonbox template -constexpr typename dragonbox::float_info::carrier_uint exponent_mask() { +constexpr auto exponent_mask() -> + typename dragonbox::float_info::carrier_uint { using uint = typename dragonbox::float_info::carrier_uint; return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) << dragonbox::float_info::significand_bits; } // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. -template It write_exponent(int exp, It it) { +template +auto write_exponent(int exp, It it) -> It { FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); if (exp < 0) { *it++ = static_cast('-'); @@ -1253,19 +1245,22 @@ template It write_exponent(int exp, It it) { } template -int format_float(T value, int precision, float_specs specs, buffer& buf); +auto format_float(T value, int precision, float_specs specs, buffer& buf) + -> int; // Formats a floating-point number with snprintf. template -int snprintf_float(T value, int precision, float_specs specs, - buffer& buf); +auto snprintf_float(T value, int precision, float_specs specs, + buffer& buf) -> int; -template T promote_float(T value) { return value; } -inline double promote_float(float value) { return static_cast(value); } +template auto promote_float(T value) -> T { return value; } +inline auto promote_float(float value) -> double { + return static_cast(value); +} template -FMT_NOINLINE FMT_CONSTEXPR OutputIt fill(OutputIt it, size_t n, - const fill_t& fill) { +FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, + const fill_t& fill) -> OutputIt { auto fill_size = fill.size(); if (fill_size == 1) return detail::fill_n(it, n, fill[0]); auto data = fill.data(); @@ -1279,9 +1274,9 @@ FMT_NOINLINE FMT_CONSTEXPR OutputIt fill(OutputIt it, size_t n, // width: output display width in (terminal) column positions. template -FMT_CONSTEXPR OutputIt write_padded(OutputIt out, - const basic_format_specs& specs, - size_t size, size_t width, F&& f) { +FMT_CONSTEXPR auto write_padded(OutputIt out, + const basic_format_specs& specs, + size_t size, size_t width, F&& f) -> OutputIt { static_assert(align == align::left || align == align::right, ""); unsigned spec_width = to_unsigned(specs.width); size_t padding = spec_width > width ? spec_width - width : 0; @@ -1298,25 +1293,25 @@ FMT_CONSTEXPR OutputIt write_padded(OutputIt out, template -constexpr OutputIt write_padded(OutputIt out, - const basic_format_specs& specs, - size_t size, F&& f) { +constexpr auto write_padded(OutputIt out, const basic_format_specs& specs, + size_t size, F&& f) -> OutputIt { return write_padded(out, specs, size, size, f); } -template -FMT_CONSTEXPR OutputIt write_bytes(OutputIt out, string_view bytes, - const basic_format_specs& specs) { - return write_padded(out, specs, bytes.size(), - [bytes](reserve_iterator it) { - const char* data = bytes.data(); - return copy_str(data, data + bytes.size(), it); - }); +template +FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, + const basic_format_specs& specs) + -> OutputIt { + return write_padded( + out, specs, bytes.size(), [bytes](reserve_iterator it) { + const char* data = bytes.data(); + return copy_str(data, data + bytes.size(), it); + }); } template -OutputIt write_ptr(OutputIt out, UIntPtr value, - const basic_format_specs* specs) { +auto write_ptr(OutputIt out, UIntPtr value, + const basic_format_specs* specs) -> OutputIt { int num_digits = count_digits<4>(value); auto size = to_unsigned(num_digits) + size_t(2); auto write = [=](reserve_iterator it) { @@ -1329,17 +1324,18 @@ OutputIt write_ptr(OutputIt out, UIntPtr value, } template -FMT_CONSTEXPR OutputIt write_char(OutputIt out, Char value, - const basic_format_specs& specs) { +FMT_CONSTEXPR auto write_char(OutputIt out, Char value, + const basic_format_specs& specs) + -> OutputIt { return write_padded(out, specs, 1, [=](reserve_iterator it) { *it++ = value; return it; }); } template -FMT_CONSTEXPR OutputIt write(OutputIt out, Char value, - const basic_format_specs& specs, - locale_ref loc = {}) { +FMT_CONSTEXPR auto write(OutputIt out, Char value, + const basic_format_specs& specs, + locale_ref loc = {}) -> OutputIt { return check_char_specs(specs) ? write_char(out, value, specs) : write(out, static_cast(value), specs, loc); @@ -1372,9 +1368,10 @@ template struct write_int_data { // where are written by write_digits(it). // prefix contains chars in three lower bytes and the size in the fourth byte. template -FMT_CONSTEXPR FMT_INLINE OutputIt -write_int(OutputIt out, int num_digits, unsigned prefix, - const basic_format_specs& specs, W write_digits) { +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, + unsigned prefix, + const basic_format_specs& specs, + W write_digits) -> OutputIt { // Slightly faster check for specs.width == 0 && specs.precision == -1. if ((specs.width | (specs.precision + 1)) == 0) { auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); @@ -1395,17 +1392,16 @@ write_int(OutputIt out, int num_digits, unsigned prefix, } template -bool write_int_localized(OutputIt& out, UInt value, unsigned prefix, - const basic_format_specs& specs, - locale_ref loc) { +auto write_int_localized(OutputIt& out, UInt value, unsigned prefix, + const basic_format_specs& specs, locale_ref loc) + -> bool { static_assert(std::is_same, UInt>::value, ""); const auto sep_size = 1; - std::string groups = grouping(loc); - if (groups.empty()) return false; - auto sep = thousands_sep(loc); - if (!sep) return false; + auto ts = thousands_sep(loc); + if (!ts.thousands_sep) return false; int num_digits = count_digits(value); int size = num_digits, n = num_digits; + const std::string& groups = ts.grouping; std::string::const_iterator group = groups.cbegin(); while (group != groups.cend() && n > *group && *group > 0 && *group != max_value()) { @@ -1420,7 +1416,7 @@ bool write_int_localized(OutputIt& out, UInt value, unsigned prefix, if (prefix != 0) ++size; const auto usize = to_unsigned(size); buffer.resize(usize); - basic_string_view s(&sep, sep_size); + basic_string_view s(&ts.thousands_sep, sep_size); // Index of a decimal digit with the least significant digit having index 0. int digit_index = 0; group = groups.cbegin(); @@ -1536,9 +1532,9 @@ template ::value && !std::is_same::value && std::is_same>::value)> -FMT_CONSTEXPR OutputIt write(OutputIt out, T value, - const basic_format_specs& specs, - locale_ref loc) { +FMT_CONSTEXPR auto write(OutputIt out, T value, + const basic_format_specs& specs, locale_ref loc) + -> OutputIt { return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); } // An inlined version of write used in format string compilation. @@ -1546,15 +1542,15 @@ template ::value && !std::is_same::value && !std::is_same>::value)> -FMT_CONSTEXPR FMT_INLINE OutputIt write(OutputIt out, T value, - const basic_format_specs& specs, - locale_ref loc) { +FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, + const basic_format_specs& specs, + locale_ref loc) -> OutputIt { return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); } template -FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view s, - const basic_format_specs& specs) { +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, + const basic_format_specs& specs) -> OutputIt { auto data = s.data(); auto size = s.size(); if (specs.precision >= 0 && to_unsigned(specs.precision) < size) @@ -1567,30 +1563,33 @@ FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view s, }); } template -FMT_CONSTEXPR OutputIt write(OutputIt out, - basic_string_view> s, - const basic_format_specs& specs, - locale_ref) { +FMT_CONSTEXPR auto write(OutputIt out, + basic_string_view> s, + const basic_format_specs& specs, locale_ref) + -> OutputIt { return write(out, s, specs); } template -FMT_CONSTEXPR OutputIt write(OutputIt out, const Char* s, - const basic_format_specs& specs, - locale_ref) { +FMT_CONSTEXPR auto write(OutputIt out, const Char* s, + const basic_format_specs& specs, locale_ref) + -> OutputIt { return check_cstring_type_spec(specs.type) ? write(out, basic_string_view(s), specs, {}) : write_ptr(out, to_uintptr(s), &specs); } template -OutputIt write_nonfinite(OutputIt out, bool isinf, - const basic_format_specs& specs, - const float_specs& fspecs) { +auto write_nonfinite(OutputIt out, bool isinf, basic_format_specs specs, + const float_specs& fspecs) -> OutputIt { auto str = isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan"); constexpr size_t str_size = 3; auto sign = fspecs.sign; auto size = str_size + (sign ? 1 : 0); + // Replace '0'-padding with space for non-finite values. + const bool is_zero_fill = + specs.fill.size() == 1 && *specs.fill.data() == static_cast('0'); + if (is_zero_fill) specs.fill[0] = static_cast(' '); return write_padded(out, specs, size, [=](reserve_iterator it) { if (sign) *it++ = static_cast(data::signs[sign]); return copy_str(str, str + str_size, it); @@ -1604,30 +1603,29 @@ struct big_decimal_fp { int exponent; }; -inline int get_significand_size(const big_decimal_fp& fp) { +inline auto get_significand_size(const big_decimal_fp& fp) -> int { return fp.significand_size; } template -inline int get_significand_size(const dragonbox::decimal_fp& fp) { +inline auto get_significand_size(const dragonbox::decimal_fp& fp) -> int { return count_digits(fp.significand); } template -inline OutputIt write_significand(OutputIt out, const char* significand, - int& significand_size) { +inline auto write_significand(OutputIt out, const char* significand, + int& significand_size) -> OutputIt { return copy_str(significand, significand + significand_size, out); } template -inline OutputIt write_significand(OutputIt out, UInt significand, - int significand_size) { +inline auto write_significand(OutputIt out, UInt significand, + int significand_size) -> OutputIt { return format_decimal(out, significand, significand_size).end; } template ::value)> -inline Char* write_significand(Char* out, UInt significand, - int significand_size, int integral_size, - Char decimal_point) { +inline auto write_significand(Char* out, UInt significand, int significand_size, + int integral_size, Char decimal_point) -> Char* { if (!decimal_point) return format_decimal(out, significand, significand_size).end; auto end = format_decimal(out + 1, significand, significand_size).end; @@ -1643,9 +1641,9 @@ inline Char* write_significand(Char* out, UInt significand, template >::value)> -inline OutputIt write_significand(OutputIt out, UInt significand, - int significand_size, int integral_size, - Char decimal_point) { +inline auto write_significand(OutputIt out, UInt significand, + int significand_size, int integral_size, + Char decimal_point) -> OutputIt { // Buffer is large enough to hold digits (digits10 + 1) and a decimal point. Char buffer[digits10() + 2]; auto end = write_significand(buffer, significand, significand_size, @@ -1654,9 +1652,9 @@ inline OutputIt write_significand(OutputIt out, UInt significand, } template -inline OutputIt write_significand(OutputIt out, const char* significand, - int significand_size, int integral_size, - Char decimal_point) { +inline auto write_significand(OutputIt out, const char* significand, + int significand_size, int integral_size, + Char decimal_point) -> OutputIt { out = detail::copy_str_noinline(significand, significand + integral_size, out); if (!decimal_point) return out; @@ -1666,9 +1664,9 @@ inline OutputIt write_significand(OutputIt out, const char* significand, } template -OutputIt write_float(OutputIt out, const DecimalFP& fp, - const basic_format_specs& specs, float_specs fspecs, - Char decimal_point) { +auto write_float(OutputIt out, const DecimalFP& fp, + const basic_format_specs& specs, float_specs fspecs, + Char decimal_point) -> OutputIt { auto significand = fp.significand; int significand_size = get_significand_size(fp); static const Char zero = static_cast('0'); @@ -1766,8 +1764,8 @@ OutputIt write_float(OutputIt out, const DecimalFP& fp, template ::value)> -OutputIt write(OutputIt out, T value, basic_format_specs specs, - locale_ref loc = {}) { +auto write(OutputIt out, T value, basic_format_specs specs, + locale_ref loc = {}) -> OutputIt { if (const_check(!is_supported_floating_point(value))) return out; float_specs fspecs = parse_float_type_spec(specs); fspecs.sign = specs.sign; @@ -1793,7 +1791,8 @@ OutputIt write(OutputIt out, T value, basic_format_specs specs, if (fspecs.format == float_format::hex) { if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]); snprintf_float(promote_float(value), specs.precision, fspecs, buffer); - return write_bytes(out, {buffer.data(), buffer.size()}, specs); + return write_bytes(out, {buffer.data(), buffer.size()}, + specs); } int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; if (fspecs.format == float_format::exp) { @@ -1814,7 +1813,7 @@ OutputIt write(OutputIt out, T value, basic_format_specs specs, template ::value)> -OutputIt write(OutputIt out, T value) { +auto write(OutputIt out, T value) -> OutputIt { if (const_check(!is_supported_floating_point(value))) return out; using floaty = conditional_t::value, double, T>; @@ -1840,19 +1839,20 @@ OutputIt write(OutputIt out, T value) { template ::value && !is_fast_float::value)> -inline OutputIt write(OutputIt out, T value) { +inline auto write(OutputIt out, T value) -> OutputIt { return write(out, value, basic_format_specs()); } template -OutputIt write(OutputIt out, monostate, basic_format_specs = {}, - locale_ref = {}) { +auto write(OutputIt out, monostate, basic_format_specs = {}, + locale_ref = {}) -> OutputIt { FMT_ASSERT(false, ""); return out; } template -FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view value) { +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) + -> OutputIt { auto it = reserve(out, value.size()); it = copy_str_noinline(value.begin(), value.end(), it); return base_iterator(out, it); @@ -1860,7 +1860,7 @@ FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view value) { template ::value)> -constexpr OutputIt write(OutputIt out, const T& value) { +constexpr auto write(OutputIt out, const T& value) -> OutputIt { return write(out, to_string_view(value)); } @@ -1868,7 +1868,7 @@ template ::value && !std::is_same::value && !std::is_same::value)> -FMT_CONSTEXPR OutputIt write(OutputIt out, T value) { +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { auto abs_value = static_cast>(value); bool negative = is_negative(value); // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. @@ -1894,30 +1894,31 @@ template < mapped_type_constant>::value != type::custom_type, FMT_ENABLE_IF(check)> -FMT_CONSTEXPR OutputIt write(OutputIt out, T value) { +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { return write( out, static_cast::type>(value)); } template ::value)> -FMT_CONSTEXPR OutputIt write(OutputIt out, T value, - const basic_format_specs& specs = {}, - locale_ref = {}) { +FMT_CONSTEXPR auto write(OutputIt out, T value, + const basic_format_specs& specs = {}, + locale_ref = {}) -> OutputIt { return specs.type && specs.type != 's' ? write(out, value ? 1 : 0, specs, {}) : write_bytes(out, value ? "true" : "false", specs); } template -FMT_CONSTEXPR OutputIt write(OutputIt out, Char value) { +FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { auto it = reserve(out, 1); *it++ = value; return base_iterator(out, it); } template -FMT_CONSTEXPR_CHAR_TRAITS OutputIt write(OutputIt out, const Char* value) { +FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) + -> OutputIt { if (!value) { FMT_THROW(format_error("string pointer is null")); } else { @@ -1929,17 +1930,19 @@ FMT_CONSTEXPR_CHAR_TRAITS OutputIt write(OutputIt out, const Char* value) { template ::value)> -OutputIt write(OutputIt out, const T* value, - const basic_format_specs& specs = {}, locale_ref = {}) { +auto write(OutputIt out, const T* value, + const basic_format_specs& specs = {}, locale_ref = {}) + -> OutputIt { check_pointer_type_spec(specs.type, error_handler()); return write_ptr(out, to_uintptr(value), &specs); } template -auto write(OutputIt out, const T& value) -> typename std::enable_if< - mapped_type_constant>::value == - type::custom_type, - OutputIt>::type { +FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> + typename std::enable_if< + mapped_type_constant>::value == + type::custom_type, + OutputIt>::type { using context_type = basic_format_context; using formatter_type = conditional_t::value, @@ -2011,13 +2014,13 @@ template class width_checker { explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T value) { + FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { if (is_negative(value)) handler_.on_error("negative width"); return static_cast(value); } template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T) { + FMT_CONSTEXPR auto operator()(T) -> unsigned long long { handler_.on_error("width is not integer"); return 0; } @@ -2031,13 +2034,13 @@ template class precision_checker { explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T value) { + FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { if (is_negative(value)) handler_.on_error("negative precision"); return static_cast(value); } template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T) { + FMT_CONSTEXPR auto operator()(T) -> unsigned long long { handler_.on_error("precision is not integer"); return 0; } @@ -2048,14 +2051,15 @@ template class precision_checker { template